mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-12-18 08:01:36 +00:00
50c136fbfe
Also, I would like to use this opportunity to thank Siberian Tiger from DTD Team for helping me with SVN builds for more than two years. You made my constant wrestling with the code a much less tedious process.
299 lines
8.2 KiB
C#
299 lines
8.2 KiB
C#
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using CodeImp.DoomBuilder.Editing;
|
|
using System.Windows.Forms;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using System.IO;
|
|
using System.Globalization;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
using CodeImp.DoomBuilder.Map;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.BuilderEffects
|
|
{
|
|
[EditMode(DisplayName = "Terrain Importer",
|
|
SwitchAction = "importobjasterrain",
|
|
Volatile = true,
|
|
UseByDefault = true,
|
|
AllowCopyPaste = false)]
|
|
public class ImportObjAsTerrainMode : ClassicMode
|
|
{
|
|
#region ================== Constants
|
|
|
|
private readonly static char[] space = { ' ' };
|
|
private const string slash = "/";
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
private struct Face
|
|
{
|
|
public readonly Vector3D V1;
|
|
public readonly Vector3D V2;
|
|
public readonly Vector3D V3;
|
|
|
|
public Face(Vector3D v1, Vector3D v2, Vector3D v3) {
|
|
V1 = v1;
|
|
V2 = v2;
|
|
V3 = v3;
|
|
}
|
|
}
|
|
|
|
private readonly ObjImportSettingsForm form;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
internal enum UpAxis
|
|
{
|
|
Y,
|
|
Z,
|
|
X
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor
|
|
|
|
public ImportObjAsTerrainMode() {
|
|
form = new ObjImportSettingsForm();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Methods
|
|
|
|
public override void OnEngage() {
|
|
if(!General.Map.UDMF) {
|
|
General.Interface.DisplayStatus(StatusType.Warning, "Terrain importer works only in UDMF map format!");
|
|
OnCancel();
|
|
}
|
|
|
|
base.OnEngage();
|
|
General.Map.Map.ClearAllSelected();
|
|
|
|
//show interface
|
|
if(form.ShowDialog() == DialogResult.OK && File.Exists(form.FilePath)) {
|
|
OnAccept();
|
|
} else {
|
|
OnCancel();
|
|
}
|
|
}
|
|
|
|
public override void OnAccept() {
|
|
Cursor.Current = Cursors.AppStarting;
|
|
General.Interface.DisplayStatus(StatusType.Busy, "Creating geometry...");
|
|
|
|
// Collections! Everyone loves them!
|
|
List<Vector3D> verts = new List<Vector3D>(12);
|
|
List<Face> faces = new List<Face>(4);
|
|
int minZ = int.MaxValue;
|
|
int maxZ = int.MinValue;
|
|
|
|
// Read .obj, create and select sectors
|
|
if(!readGeometry(form.FilePath, form.ObjScale, form.UpAxis, verts, faces, ref minZ, ref maxZ) || !createGeometry(verts, faces, minZ, maxZ + (maxZ - minZ)/2)) {
|
|
// Fial!
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Return to base mode
|
|
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
|
|
}
|
|
|
|
// Update caches
|
|
General.Map.Map.Update();
|
|
General.Map.IsChanged = true;
|
|
|
|
// Done
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Switch to Edit Selection mode
|
|
General.Editing.ChangeMode("EditSelectionMode", true);
|
|
}
|
|
|
|
public override void OnCancel() {
|
|
// Cancel base class
|
|
base.OnCancel();
|
|
|
|
// Return to base mode
|
|
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Geometry creation
|
|
|
|
private bool createGeometry(List<Vector3D> verts, List<Face> faces, int minZ, int maxZ) {
|
|
//make undo
|
|
General.Map.UndoRedo.CreateUndo("Import Terrain");
|
|
|
|
//prepare mapset
|
|
List<Linedef> newlines = new List<Linedef>();
|
|
MapSet map = General.Map.Map;
|
|
map.BeginAddRemove();
|
|
map.SetCapacity(map.Vertices.Count + verts.Count, map.Linedefs.Count + faces.Count * 3, map.Sidedefs.Count + faces.Count * 3, map.Sectors.Count + faces.Count, 0);
|
|
|
|
//terrain has many faces... let's create them
|
|
Dictionary<Vector3D, Vertex> newverts = new Dictionary<Vector3D, Vertex>();
|
|
foreach(Face face in faces){
|
|
Sector s = map.CreateSector();
|
|
s.Selected = true;
|
|
s.FloorHeight = minZ;
|
|
s.CeilHeight = maxZ;
|
|
s.Brightness = General.Settings.DefaultBrightness; //todo: allow user to change this
|
|
s.SetCeilTexture(General.Map.Config.SkyFlatName);
|
|
s.SetFloorTexture(General.Map.Options.DefaultFloorTexture); //todo: allow user to change this
|
|
|
|
Linedef newline = getLine(newverts, s, face.V1, face.V2);
|
|
if(newline != null) newlines.Add(newline);
|
|
|
|
newline = getLine(newverts, s, face.V2, face.V3);
|
|
if(newline != null) newlines.Add(newline);
|
|
|
|
newline = getLine(newverts, s, face.V3, face.V1);
|
|
if(newline != null) newlines.Add(newline);
|
|
|
|
s.UpdateCache();
|
|
}
|
|
|
|
//update new lines
|
|
foreach(Linedef l in newlines){
|
|
l.ApplySidedFlags();
|
|
}
|
|
|
|
map.EndAddRemove();
|
|
return true;
|
|
}
|
|
|
|
private Linedef getLine(Dictionary<Vector3D, Vertex> verts, Sector sector, Vector3D v1, Vector3D v2) {
|
|
Linedef line = null;
|
|
|
|
//get start and end verts
|
|
Vertex start = getVertex(verts, v1);
|
|
Vertex end = getVertex(verts, v2);
|
|
|
|
//check if the line is already created
|
|
foreach(Linedef l in start.Linedefs){
|
|
if(l.End == end || l.Start == end) {
|
|
line = l;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//create a new line?
|
|
if(line == null) {
|
|
line = General.Map.Map.CreateLinedef(start, end);
|
|
|
|
//create front sidedef and attach sector to it
|
|
General.Map.Map.CreateSidedef(line, true, sector);
|
|
} else {
|
|
//create back sidedef and attach sector to it
|
|
General.Map.Map.CreateSidedef(line, false, sector);
|
|
}
|
|
|
|
line.Selected = true;
|
|
return line;
|
|
}
|
|
|
|
private static Vertex getVertex(Dictionary<Vector3D, Vertex> verts, Vector3D pos) {
|
|
//already there?
|
|
if(verts.ContainsKey(pos)) return verts[pos];
|
|
|
|
//make a new one
|
|
Vertex v = General.Map.Map.CreateVertex(pos);
|
|
v.ZFloor = pos.z;
|
|
verts.Add(pos, v);
|
|
return v;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== .obj import
|
|
|
|
private static bool readGeometry(string path, float scale, UpAxis axis, List<Vector3D> verts, List<Face> faces, ref int minZ, ref int maxZ) {
|
|
using(StreamReader reader = File.OpenText(path)) {
|
|
string line;
|
|
float x, y, z;
|
|
int px, py, pz;
|
|
int counter = 0;
|
|
|
|
while((line = reader.ReadLine()) != null) {
|
|
counter++;
|
|
|
|
if(line.StartsWith("v ")) {
|
|
string[] parts = line.Split(space);
|
|
|
|
if(parts.Length != 4 || !float.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out x) ||
|
|
!float.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out y) ||
|
|
!float.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out z)) {
|
|
MessageBox.Show("Failed to parse vertex definition at line " + counter + "!", "Terrain Importer", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return false;
|
|
}
|
|
|
|
//apply up axis
|
|
switch (axis) {
|
|
case UpAxis.Y:
|
|
px = (int)Math.Round(x * scale);
|
|
py = (int)Math.Round(-z * scale);
|
|
pz = (int)Math.Round(y * scale);
|
|
break;
|
|
|
|
case UpAxis.Z:
|
|
px = (int)Math.Round(-x * scale);
|
|
py = (int)Math.Round(-y * scale);
|
|
pz = (int)Math.Round(z * scale);
|
|
break;
|
|
|
|
case UpAxis.X:
|
|
px = (int)Math.Round(-y * scale);
|
|
py = (int)Math.Round(-z * scale);
|
|
pz = (int)Math.Round(x * scale);
|
|
break;
|
|
|
|
default: //same as UpAxis.Y
|
|
px = (int)Math.Round(x * scale);
|
|
py = (int)Math.Round(-z * scale);
|
|
pz = (int)Math.Round(y * scale);
|
|
break;
|
|
}
|
|
|
|
if(maxZ < pz) maxZ = pz;
|
|
if(minZ > pz) minZ = pz;
|
|
|
|
verts.Add(new Vector3D(px, py, pz));
|
|
|
|
} else if(line.StartsWith("f ")) {
|
|
string[] parts = line.Split(space);
|
|
|
|
if(parts.Length != 4) {
|
|
MessageBox.Show("Failed to parse face definition at line " + counter + ": only triangle faces are supported!", "Terrain Importer", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return false;
|
|
}
|
|
|
|
//.obj vertex indices are 1-based
|
|
int v1 = readVertexIndex(parts[1]) - 1;
|
|
int v2 = readVertexIndex(parts[2]) - 1;
|
|
int v3 = readVertexIndex(parts[3]) - 1;
|
|
|
|
if(verts[v1] == verts[v2] || verts[v1] == verts[v3] || verts[v2] == verts[v3]) continue;
|
|
faces.Add(new Face(verts[v3], verts[v2], verts[v1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static int readVertexIndex(string def) {
|
|
int slashpos = def.IndexOf(slash);
|
|
if(slashpos != -1) def = def.Substring(0, slashpos);
|
|
return int.Parse(def);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|