2014-01-30 14:52:08 +00:00
#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 ;
2014-12-03 23:15:26 +00:00
public Face ( Vector3D v1 , Vector3D v2 , Vector3D v3 )
{
2014-01-30 14:52:08 +00:00
V1 = v1 ;
V2 = v2 ;
V3 = v3 ;
}
}
private readonly ObjImportSettingsForm form ;
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
internal enum UpAxis
{
Y ,
Z ,
X
}
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor
2014-12-03 23:15:26 +00:00
public ImportObjAsTerrainMode ( )
{
2014-01-30 14:52:08 +00:00
form = new ObjImportSettingsForm ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
2014-12-03 23:15:26 +00:00
public override void OnEngage ( )
{
if ( ! General . Map . UDMF )
{
2014-01-30 14:52:08 +00:00
General . Interface . DisplayStatus ( StatusType . Warning , "Terrain importer works only in UDMF map format!" ) ;
OnCancel ( ) ;
}
base . OnEngage ( ) ;
General . Map . Map . ClearAllSelected ( ) ;
//show interface
2014-12-03 23:15:26 +00:00
if ( form . ShowDialog ( ) = = DialogResult . OK & & File . Exists ( form . FilePath ) )
{
2014-01-30 14:52:08 +00:00
OnAccept ( ) ;
2014-12-03 23:15:26 +00:00
}
else
{
2014-01-30 14:52:08 +00:00
OnCancel ( ) ;
}
}
2014-12-03 23:15:26 +00:00
public override void OnAccept ( )
{
2014-01-30 14:52:08 +00:00
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
2014-12-03 23:15:26 +00:00
if ( ! ReadGeometry ( form . FilePath , form . ObjScale , form . UpAxis , verts , faces , ref minZ , ref maxZ ) | | ! CreateGeometry ( verts , faces , minZ , maxZ + ( maxZ - minZ ) / 2 ) )
{
2014-01-30 14:52:08 +00:00
// 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 ) ;
}
2014-12-03 23:15:26 +00:00
public override void OnCancel ( )
{
2014-01-30 14:52:08 +00:00
// Cancel base class
base . OnCancel ( ) ;
// Return to base mode
General . Editing . ChangeMode ( General . Editing . PreviousStableMode . Name ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Geometry creation
2014-12-03 23:15:26 +00:00
private static bool CreateGeometry ( List < Vector3D > verts , List < Face > faces , int minZ , int maxZ )
{
2014-01-30 14:52:08 +00:00
//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 > ( ) ;
2014-12-03 23:15:26 +00:00
foreach ( Face face in faces )
{
2014-01-30 14:52:08 +00:00
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
2014-12-03 23:15:26 +00:00
Linedef newline = GetLine ( newverts , s , face . V1 , face . V2 ) ;
2014-01-30 14:52:08 +00:00
if ( newline ! = null ) newlines . Add ( newline ) ;
2014-12-03 23:15:26 +00:00
newline = GetLine ( newverts , s , face . V2 , face . V3 ) ;
2014-01-30 14:52:08 +00:00
if ( newline ! = null ) newlines . Add ( newline ) ;
2014-12-03 23:15:26 +00:00
newline = GetLine ( newverts , s , face . V3 , face . V1 ) ;
2014-01-30 14:52:08 +00:00
if ( newline ! = null ) newlines . Add ( newline ) ;
s . UpdateCache ( ) ;
}
//update new lines
2014-12-03 23:15:26 +00:00
foreach ( Linedef l in newlines ) l . ApplySidedFlags ( ) ;
2014-01-30 14:52:08 +00:00
map . EndAddRemove ( ) ;
return true ;
}
2014-12-03 23:15:26 +00:00
private static Linedef GetLine ( Dictionary < Vector3D , Vertex > verts , Sector sector , Vector3D v1 , Vector3D v2 )
{
2014-01-30 14:52:08 +00:00
Linedef line = null ;
//get start and end verts
2014-12-03 23:15:26 +00:00
Vertex start = GetVertex ( verts , v1 ) ;
Vertex end = GetVertex ( verts , v2 ) ;
2014-01-30 14:52:08 +00:00
//check if the line is already created
2014-12-03 23:15:26 +00:00
foreach ( Linedef l in start . Linedefs )
{
if ( l . End = = end | | l . Start = = end )
{
2014-01-30 14:52:08 +00:00
line = l ;
break ;
}
}
//create a new line?
2014-12-03 23:15:26 +00:00
if ( line = = null )
{
2014-01-30 14:52:08 +00:00
line = General . Map . Map . CreateLinedef ( start , end ) ;
//create front sidedef and attach sector to it
General . Map . Map . CreateSidedef ( line , true , sector ) ;
2014-12-03 23:15:26 +00:00
}
else
{
2014-01-30 14:52:08 +00:00
//create back sidedef and attach sector to it
General . Map . Map . CreateSidedef ( line , false , sector ) ;
}
line . Selected = true ;
return line ;
}
2014-12-03 23:15:26 +00:00
private static Vertex GetVertex ( Dictionary < Vector3D , Vertex > verts , Vector3D pos )
{
2014-01-30 14:52:08 +00:00
//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
2014-12-03 23:15:26 +00:00
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 ) )
{
2014-01-30 14:52:08 +00:00
string line ;
float x , y , z ;
int px , py , pz ;
int counter = 0 ;
2014-12-03 23:15:26 +00:00
while ( ( line = reader . ReadLine ( ) ) ! = null )
{
2014-01-30 14:52:08 +00:00
counter + + ;
2014-12-03 23:15:26 +00:00
if ( line . StartsWith ( "v " ) )
{
2014-01-30 14:52:08 +00:00
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 ) | |
2014-12-03 23:15:26 +00:00
! float . TryParse ( parts [ 3 ] , NumberStyles . Float , CultureInfo . InvariantCulture , out z ) )
{
2014-01-30 14:52:08 +00:00
MessageBox . Show ( "Failed to parse vertex definition at line " + counter + "!" , "Terrain Importer" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
return false ;
}
//apply up axis
2014-12-03 23:15:26 +00:00
switch ( axis )
{
2014-01-30 14:52:08 +00:00
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 :
2014-09-02 20:44:58 +00:00
px = ( int ) Math . Round ( - x * scale ) ;
2014-01-30 14:52:08 +00:00
py = ( int ) Math . Round ( - y * scale ) ;
pz = ( int ) Math . Round ( z * scale ) ;
break ;
case UpAxis . X :
2014-09-02 20:44:58 +00:00
px = ( int ) Math . Round ( - y * scale ) ;
2014-01-30 14:52:08 +00:00
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 ) ) ;
2014-12-03 23:15:26 +00:00
}
else if ( line . StartsWith ( "f " ) )
{
2014-01-30 14:52:08 +00:00
string [ ] parts = line . Split ( space ) ;
2014-12-03 23:15:26 +00:00
if ( parts . Length ! = 4 )
{
2014-01-30 14:52:08 +00:00
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
2014-12-03 23:15:26 +00:00
int v1 = ReadVertexIndex ( parts [ 1 ] ) - 1 ;
int v2 = ReadVertexIndex ( parts [ 2 ] ) - 1 ;
int v3 = ReadVertexIndex ( parts [ 3 ] ) - 1 ;
2014-01-30 14:52:08 +00:00
if ( verts [ v1 ] = = verts [ v2 ] | | verts [ v1 ] = = verts [ v3 ] | | verts [ v2 ] = = verts [ v3 ] ) continue ;
2014-09-02 20:44:58 +00:00
faces . Add ( new Face ( verts [ v3 ] , verts [ v2 ] , verts [ v1 ] ) ) ;
2014-01-30 14:52:08 +00:00
}
}
}
return true ;
}
2014-12-03 23:15:26 +00:00
private static int ReadVertexIndex ( string def )
{
2014-01-30 14:52:08 +00:00
int slashpos = def . IndexOf ( slash ) ;
if ( slashpos ! = - 1 ) def = def . Substring ( 0 , slashpos ) ;
return int . Parse ( def ) ;
}
#endregion
}
}