2012-04-17 19:13:47 +00:00
using System ;
using System.IO ;
using System.Drawing ;
2012-06-03 23:36:53 +00:00
using System.Drawing.Imaging ;
2012-04-17 19:13:47 +00:00
using System.Text ;
using System.Collections.Generic ;
using CodeImp.DoomBuilder ;
2012-06-03 23:36:53 +00:00
using CodeImp.DoomBuilder.IO ;
2012-05-21 23:51:32 +00:00
using CodeImp.DoomBuilder.Data ;
2012-04-18 19:34:11 +00:00
using CodeImp.DoomBuilder.Rendering ;
2012-04-17 19:13:47 +00:00
using CodeImp.DoomBuilder.GZBuilder.Data ;
2012-05-21 23:51:32 +00:00
using CodeImp.DoomBuilder.GZBuilder.GZDoom ;
2012-04-17 19:13:47 +00:00
using SlimDX ;
using SlimDX.Direct3D9 ;
//mxd. Original version taken from here: http://colladadotnet.codeplex.com/SourceControl/changeset/view/40680
2012-05-21 23:51:32 +00:00
namespace CodeImp.DoomBuilder.GZBuilder.MD3
{
internal class ModelReader
{
public static void Parse ( ref ModeldefEntry mde , PK3StructuredReader reader , Device D3DDevice ) {
string [ ] modelNames = new string [ mde . ModelNames . Count ] ;
string [ ] textureNames = new string [ mde . TextureNames . Count ] ;
mde . ModelNames . CopyTo ( modelNames ) ;
mde . TextureNames . CopyTo ( textureNames ) ;
mde . Model = new GZModel ( ) ;
mde . Model . NUM_MESHES = ( byte ) modelNames . Length ;
2012-04-17 19:13:47 +00:00
BoundingBoxSizes bbs = new BoundingBoxSizes ( ) ;
2012-05-21 23:51:32 +00:00
for ( int i = 0 ; i < modelNames . Length ; i + + ) {
string modelPath = Path . Combine ( mde . Path , modelNames [ i ] ) ;
if ( reader . FileExists ( modelPath ) ) {
MemoryStream stream = reader . LoadFile ( modelPath ) ;
2012-06-03 23:36:53 +00:00
General . WriteLogLine ( "ModelLoader: loading '" + modelPath + "'" ) ;
2012-05-21 23:51:32 +00:00
2012-04-17 19:13:47 +00:00
//mesh
2012-05-21 23:51:32 +00:00
string ext = modelNames [ i ] . Substring ( modelNames [ i ] . Length - 4 ) ;
string error = "" ;
if ( ext = = ".md3" )
error = ReadMD3Model ( ref bbs , ref mde , stream , D3DDevice ) ;
else if ( ext = = ".md2" )
error = ReadMD2Model ( ref bbs , ref mde , stream , D3DDevice ) ;
2012-04-17 19:13:47 +00:00
//texture
2012-05-21 23:51:32 +00:00
if ( string . IsNullOrEmpty ( error ) ) {
string texturePath = Path . Combine ( mde . Path , textureNames [ i ] ) ;
2012-06-01 10:17:47 +00:00
if ( textureNames [ i ] ! = TextureData . INVALID_TEXTURE & & reader . FileExists ( texturePath ) ) {
2012-06-03 23:36:53 +00:00
if ( Path . GetExtension ( texturePath ) = = ".pcx" ) { //pcx format requires special handling...
FileImageReader fir = new FileImageReader ( ) ;
Bitmap bitmap = fir . ReadAsBitmap ( reader . LoadFile ( texturePath ) ) ;
if ( bitmap ! = null ) {
BitmapData bmlock = bitmap . LockBits ( new System . Drawing . Rectangle ( 0 , 0 , bitmap . Width , bitmap . Height ) , ImageLockMode . ReadOnly , bitmap . PixelFormat ) ;
Texture texture = new Texture ( D3DDevice , bitmap . Width , bitmap . Height , 1 , Usage . None , Format . A8R8G8B8 , Pool . Managed ) ;
DataRectangle textureLock = texture . LockRectangle ( 0 , LockFlags . None ) ;
textureLock . Data . WriteRange ( bmlock . Scan0 , bmlock . Height * bmlock . Stride ) ;
bitmap . UnlockBits ( bmlock ) ;
texture . UnlockRectangle ( 0 ) ;
mde . Model . Textures . Add ( texture ) ;
} else {
mde . Model . Textures . Add ( General . Map . Data . UnknownTexture3D . Texture ) ;
GZBuilder . GZGeneral . LogAndTraceWarning ( "ModelLoader: unable to load texture '" + texturePath + "'" ) ;
}
} else {
mde . Model . Textures . Add ( Texture . FromStream ( D3DDevice , reader . LoadFile ( texturePath ) ) ) ;
}
2012-04-17 19:13:47 +00:00
} else {
2012-05-21 23:51:32 +00:00
mde . Model . Textures . Add ( General . Map . Data . UnknownTexture3D . Texture ) ;
2012-06-01 10:17:47 +00:00
if ( textureNames [ i ] ! = TextureData . INVALID_TEXTURE )
2012-06-03 23:36:53 +00:00
GZBuilder . GZGeneral . LogAndTraceWarning ( "ModelLoader: unable to load texture '" + texturePath + "' - no such file" ) ;
2012-04-17 19:13:47 +00:00
}
} else {
2012-06-03 23:36:53 +00:00
GZBuilder . GZGeneral . LogAndTraceWarning ( "ModelLoader: error while loading " + modelPath + ": " + error ) ;
2012-05-21 23:51:32 +00:00
mde . Model . NUM_MESHES - - ;
2012-04-17 19:13:47 +00:00
}
2012-05-21 23:51:32 +00:00
stream . Dispose ( ) ;
2012-04-17 19:13:47 +00:00
} else {
2012-06-03 23:36:53 +00:00
GZBuilder . GZGeneral . LogAndTraceWarning ( "ModelLoader: unable to load model '" + modelPath + "' - no such file" ) ;
2012-05-21 23:51:32 +00:00
mde . Model . NUM_MESHES - - ;
2012-04-17 19:13:47 +00:00
}
}
2012-05-21 23:51:32 +00:00
if ( mde . Model . NUM_MESHES < = 0 ) {
mde . Model = null ;
return ;
}
2012-04-17 19:13:47 +00:00
2012-05-21 23:51:32 +00:00
mde . Model . BoundingBox = BoundingBoxTools . CalculateBoundingBox ( bbs ) ;
2012-04-17 19:13:47 +00:00
}
2012-05-21 23:51:32 +00:00
private static string ReadMD3Model ( ref BoundingBoxSizes bbs , ref ModeldefEntry mde , MemoryStream s , Device D3DDevice ) {
2012-04-17 19:13:47 +00:00
long start = s . Position ;
using ( var br = new BinaryReader ( s , Encoding . ASCII ) ) {
2012-05-20 00:56:59 +00:00
string magic = ReadString ( br , 4 ) ;
2012-05-21 23:51:32 +00:00
if ( magic ! = "IDP3" )
return "magic should be 'IDP3', not '" + magic + "'" ;
2012-04-17 19:13:47 +00:00
s . Position + = 80 ;
int numSurfaces = br . ReadInt32 ( ) ;
s . Position + = 12 ;
int ofsSurfaces = br . ReadInt32 ( ) ;
if ( s . Position ! = ofsSurfaces + start )
s . Position = ofsSurfaces + start ;
List < short > polyIndecesList = new List < short > ( ) ;
2012-04-18 19:34:11 +00:00
List < WorldVertex > vertList = new List < WorldVertex > ( ) ;
2012-04-17 19:13:47 +00:00
2012-05-21 23:51:32 +00:00
string error = "" ;
for ( int c = 0 ; c < numSurfaces ; + + c ) {
error = ReadSurface ( ref bbs , br , polyIndecesList , vertList , mde ) ;
if ( ! string . IsNullOrEmpty ( error ) )
return error ;
}
2012-04-17 19:13:47 +00:00
//indeces for rendering current mesh in 2d
short [ ] indeces2d_arr = CreateLineListIndeces ( polyIndecesList ) ;
//mesh
2012-04-18 19:34:11 +00:00
Mesh mesh = new Mesh ( D3DDevice , polyIndecesList . Count / 3 , vertList . Count , MeshFlags . IndexBufferManaged | MeshFlags . VertexBufferManaged , General . Map . Graphics . Shaders . World3D . VertexElements ) ;
2012-04-17 19:13:47 +00:00
DataStream stream = mesh . VertexBuffer . Lock ( 0 , 0 , LockFlags . None ) ;
stream . WriteRange ( vertList . ToArray ( ) ) ;
mesh . VertexBuffer . Unlock ( ) ;
stream = mesh . IndexBuffer . Lock ( 0 , 0 , LockFlags . None ) ;
stream . WriteRange ( polyIndecesList . ToArray ( ) ) ;
mesh . IndexBuffer . Unlock ( ) ;
2012-04-18 19:34:11 +00:00
mesh . OptimizeInPlace ( MeshOptimizeFlags . AttributeSort ) ;
2012-05-21 23:51:32 +00:00
mde . Model . Meshes . Add ( mesh ) ;
2012-04-18 19:34:11 +00:00
2012-04-17 19:13:47 +00:00
//2d data
IndexBuffer indeces2d = new IndexBuffer ( D3DDevice , 2 * indeces2d_arr . Length , Usage . WriteOnly , Pool . Managed , true ) ;
stream = indeces2d . Lock ( 0 , 0 , LockFlags . None ) ;
stream . WriteRange ( indeces2d_arr ) ;
indeces2d . Unlock ( ) ;
2012-05-21 23:51:32 +00:00
mde . Model . Indeces2D . Add ( indeces2d ) ;
mde . Model . NumIndeces2D . Add ( ( short ) polyIndecesList . Count ) ;
2012-04-17 19:13:47 +00:00
}
2012-05-21 23:51:32 +00:00
return "" ;
2012-04-17 19:13:47 +00:00
}
2012-05-21 23:51:32 +00:00
private static string ReadSurface ( ref BoundingBoxSizes bbs , BinaryReader br , List < short > polyIndecesList , List < WorldVertex > vertList , ModeldefEntry mde ) {
2012-05-25 11:21:54 +00:00
int vertexOffset = vertList . Count ;
long start = br . BaseStream . Position ;
2012-05-20 00:56:59 +00:00
string magic = ReadString ( br , 4 ) ;
2012-05-21 23:51:32 +00:00
if ( magic ! = "IDP3" )
return "error while reading surface: Magic should be 'IDP3', not '" + magic + "'" ;
2012-04-17 19:13:47 +00:00
br . BaseStream . Position + = 76 ;
int numVerts = br . ReadInt32 ( ) ; //Number of Vertex objects defined in this Surface, up to MD3_MAX_VERTS. Current value of MD3_MAX_VERTS is 4096.
int numTriangles = br . ReadInt32 ( ) ; //Number of Triangle objects defined in this Surface, maximum of MD3_MAX_TRIANGLES. Current value of MD3_MAX_TRIANGLES is 8192.
int ofsTriangles = br . ReadInt32 ( ) ; //Relative offset from SURFACE_START where the list of Triangle objects starts.
br . BaseStream . Position + = 4 ;
int ofsST = br . ReadInt32 ( ) ; //Relative offset from SURFACE_START where the list of ST objects (s-t texture coordinates) starts.
int ofsNormal = br . ReadInt32 ( ) ; //Relative offset from SURFACE_START where the list of Vertex objects (X-Y-Z-N vertices) starts.
int ofsEnd = br . ReadInt32 ( ) ; //Relative offset from SURFACE_START to where the Surface object ends.
//polygons
if ( start + ofsTriangles ! = br . BaseStream . Position )
br . BaseStream . Position = start + ofsTriangles ;
for ( int i = 0 ; i < numTriangles * 3 ; i + + )
2012-05-25 11:21:54 +00:00
polyIndecesList . Add ( ( short ) ( vertexOffset + br . ReadInt32 ( ) ) ) ;
2012-04-17 19:13:47 +00:00
//Vertices
if ( start + ofsST ! = br . BaseStream . Position )
br . BaseStream . Position = start + ofsST ;
for ( int i = 0 ; i < numVerts ; i + + ) {
2012-04-18 19:34:11 +00:00
WorldVertex v = new WorldVertex ( ) ;
2012-05-25 11:21:54 +00:00
v . c = - 1 ; //white
2012-04-18 19:34:11 +00:00
v . u = br . ReadSingle ( ) ;
v . v = br . ReadSingle ( ) ;
2012-04-17 19:13:47 +00:00
vertList . Add ( v ) ;
}
//Normals
if ( start + ofsNormal ! = br . BaseStream . Position )
br . BaseStream . Position = start + ofsNormal ;
2012-05-25 11:21:54 +00:00
for ( int i = vertexOffset ; i < vertexOffset + numVerts ; i + + ) {
2012-04-18 19:34:11 +00:00
WorldVertex v = vertList [ i ] ;
2012-04-17 19:13:47 +00:00
2012-04-18 19:34:11 +00:00
v . y = - ( float ) br . ReadInt16 ( ) / 64 * mde . Scale . X ;
v . x = ( float ) br . ReadInt16 ( ) / 64 * mde . Scale . Y ;
v . z = ( float ) br . ReadInt16 ( ) / 64 * mde . Scale . Z + mde . zOffset ;
2012-04-17 19:13:47 +00:00
//bounding box
2012-04-23 21:35:48 +00:00
BoundingBoxTools . UpdateBoundingBoxSizes ( ref bbs , v ) ;
2012-04-17 19:13:47 +00:00
var lat = br . ReadByte ( ) * ( 2 * Math . PI ) / 255.0 ;
var lng = br . ReadByte ( ) * ( 2 * Math . PI ) / 255.0 ;
2012-04-18 19:34:11 +00:00
v . nx = ( float ) ( Math . Sin ( lng ) * Math . Sin ( lat ) ) ;
v . ny = - ( float ) ( Math . Cos ( lng ) * Math . Sin ( lat ) ) ;
v . nz = ( float ) ( Math . Cos ( lat ) ) ;
2012-04-17 19:13:47 +00:00
vertList [ i ] = v ;
}
if ( start + ofsEnd ! = br . BaseStream . Position )
br . BaseStream . Position = start + ofsEnd ;
2012-05-21 23:51:32 +00:00
return "" ;
2012-04-17 19:13:47 +00:00
}
2012-05-21 23:51:32 +00:00
private static string ReadMD2Model ( ref BoundingBoxSizes bbs , ref ModeldefEntry mde , MemoryStream s , Device D3DDevice ) {
2012-04-17 19:13:47 +00:00
long start = s . Position ;
using ( var br = new BinaryReader ( s , Encoding . ASCII ) ) {
2012-05-20 00:56:59 +00:00
string magic = ReadString ( br , 4 ) ;
2012-05-21 23:51:32 +00:00
if ( magic ! = "IDP2" ) //magic number: "IDP2"
return "magic should be 'IDP2', not '" + magic + "'" ;
2012-04-17 19:13:47 +00:00
int modelVersion = br . ReadInt32 ( ) ;
2012-05-21 23:51:32 +00:00
if ( modelVersion ! = 8 ) //MD2 version. Must be equal to 8
return "MD2 version must be 8 but is " + modelVersion ;
2012-04-17 19:13:47 +00:00
int texWidth = br . ReadInt32 ( ) ;
int texHeight = br . ReadInt32 ( ) ;
s . Position + = 8 ; //Size of one frame in bytes
//s.Position += 4; //Number of textures
int num_verts = br . ReadInt32 ( ) ; //Number of vertices
int num_uv = br . ReadInt32 ( ) ; //The number of UV coordinates in the model
int num_tris = br . ReadInt32 ( ) ; //Number of triangles
s . Position + = 4 ; //Number of OpenGL commands
2012-05-21 23:51:32 +00:00
if ( br . ReadInt32 ( ) = = 0 ) //Total number of frames
return "model has 0 frames" ;
2012-04-17 19:13:47 +00:00
s . Position + = 4 ; //Offset to skin names (each skin name is an unsigned char[64] and are null terminated)
int ofs_uv = br . ReadInt32 ( ) ; //Offset to s-t texture coordinates
int ofs_tris = br . ReadInt32 ( ) ; //Offset to triangles
int ofs_animFrame = br . ReadInt32 ( ) ; //An offset to the first animation frame
List < short > polyIndecesList = new List < short > ( ) ;
List < short > uvIndecesList = new List < short > ( ) ;
List < Vector2 > uvCoordsList = new List < Vector2 > ( ) ;
2012-04-18 19:34:11 +00:00
List < WorldVertex > vertList = new List < WorldVertex > ( ) ;
2012-04-17 19:13:47 +00:00
//polygons
if ( s . Position ! = ofs_tris + start )
s . Position = ofs_tris + start ;
for ( int i = 0 ; i < num_tris ; i + + ) {
polyIndecesList . Add ( ( short ) br . ReadInt16 ( ) ) ;
polyIndecesList . Add ( ( short ) br . ReadInt16 ( ) ) ;
polyIndecesList . Add ( ( short ) br . ReadInt16 ( ) ) ;
uvIndecesList . Add ( ( short ) br . ReadInt16 ( ) ) ;
uvIndecesList . Add ( ( short ) br . ReadInt16 ( ) ) ;
uvIndecesList . Add ( ( short ) br . ReadInt16 ( ) ) ;
}
//UV coords
if ( s . Position ! = ofs_uv + start )
s . Position = ofs_uv + start ;
for ( int i = 0 ; i < num_uv ; i + + ) {
uvCoordsList . Add ( new Vector2 ( ( float ) br . ReadInt16 ( ) / texWidth , ( float ) br . ReadInt16 ( ) / texHeight ) ) ;
}
//first frame
//header
if ( s . Position ! = ofs_animFrame + start )
s . Position = ofs_animFrame + start ;
Vector3 scale = new Vector3 ( ( float ) br . ReadSingle ( ) , ( float ) br . ReadSingle ( ) , ( float ) br . ReadSingle ( ) ) ;
Vector3 translate = new Vector3 ( ( float ) br . ReadSingle ( ) , ( float ) br . ReadSingle ( ) , ( float ) br . ReadSingle ( ) ) ;
s . Position + = 16 ; //frame name
//verts
for ( int i = 0 ; i < num_verts ; i + + ) {
//pos
2012-04-18 19:34:11 +00:00
WorldVertex v = new WorldVertex ( ) ;
2012-04-17 19:13:47 +00:00
2012-04-18 19:34:11 +00:00
v . x = ( ( float ) br . ReadByte ( ) * scale . X + translate . X ) * mde . Scale . X ;
v . y = ( ( float ) br . ReadByte ( ) * scale . Y + translate . Y ) * mde . Scale . Y ;
v . z = ( ( float ) br . ReadByte ( ) * scale . Z + translate . Z ) * mde . Scale . Z + mde . zOffset ;
2012-04-17 19:13:47 +00:00
vertList . Add ( v ) ;
2012-04-18 19:34:11 +00:00
s . Position + = 1 ; //vertex normal
2012-04-17 19:13:47 +00:00
}
for ( int i = 0 ; i < polyIndecesList . Count ; i + + ) {
2012-04-18 19:34:11 +00:00
WorldVertex v = vertList [ polyIndecesList [ i ] ] ;
2012-04-17 19:13:47 +00:00
//bounding box
2012-05-20 00:56:59 +00:00
BoundingBoxTools . UpdateBoundingBoxSizes ( ref bbs , new WorldVertex ( v . y , v . x , v . z ) ) ;
2012-04-17 19:13:47 +00:00
//uv
2012-04-18 19:34:11 +00:00
v . u = uvCoordsList [ uvIndecesList [ i ] ] . X ;
v . v = uvCoordsList [ uvIndecesList [ i ] ] . Y ;
2012-05-25 11:21:54 +00:00
//color
v . c = - 1 ; //white
2012-04-17 19:13:47 +00:00
vertList [ polyIndecesList [ i ] ] = v ;
}
//indeces for rendering current mesh in 2d
short [ ] indeces2d_arr = CreateLineListIndeces ( polyIndecesList ) ;
//mesh
2012-04-18 19:34:11 +00:00
Mesh mesh = new Mesh ( D3DDevice , polyIndecesList . Count / 3 , vertList . Count , MeshFlags . IndexBufferManaged | MeshFlags . VertexBufferManaged , General . Map . Graphics . Shaders . World3D . VertexElements ) ;
2012-04-17 19:13:47 +00:00
DataStream stream = mesh . VertexBuffer . Lock ( 0 , 0 , LockFlags . None ) ;
stream . WriteRange ( vertList . ToArray ( ) ) ;
mesh . VertexBuffer . Unlock ( ) ;
stream = mesh . IndexBuffer . Lock ( 0 , 0 , LockFlags . None ) ;
stream . WriteRange ( polyIndecesList . ToArray ( ) ) ;
mesh . IndexBuffer . Unlock ( ) ;
2012-04-18 19:34:11 +00:00
mesh . OptimizeInPlace ( MeshOptimizeFlags . AttributeSort ) ;
2012-05-21 23:51:32 +00:00
mde . Model . Meshes . Add ( mesh ) ;
2012-04-17 19:13:47 +00:00
//2d data
IndexBuffer indeces2d = new IndexBuffer ( D3DDevice , 2 * indeces2d_arr . Length , Usage . WriteOnly , Pool . Managed , true ) ;
stream = indeces2d . Lock ( 0 , 0 , LockFlags . None ) ;
stream . WriteRange ( indeces2d_arr ) ;
indeces2d . Unlock ( ) ;
2012-05-21 23:51:32 +00:00
mde . Model . Indeces2D . Add ( indeces2d ) ;
mde . Model . NumIndeces2D . Add ( ( short ) polyIndecesList . Count ) ;
mde . Model . Angle = - 90.0f * ( float ) Math . PI / 180.0f ;
2012-04-17 19:13:47 +00:00
}
2012-05-21 23:51:32 +00:00
return "" ;
2012-04-17 19:13:47 +00:00
}
//this creates list of vertex indeces for rendering using LineList method
private static short [ ] CreateLineListIndeces ( List < short > polyIndecesList ) {
short [ ] indeces2d_arr = new short [ polyIndecesList . Count * 2 ] ;
short ind1 , ind2 , ind3 ;
for ( short i = 0 ; i < polyIndecesList . Count ; i + = 3 ) {
ind1 = polyIndecesList [ i ] ;
ind2 = polyIndecesList [ i + 1 ] ;
ind3 = polyIndecesList [ i + 2 ] ;
indeces2d_arr [ i * 2 ] = ind1 ;
indeces2d_arr [ i * 2 + 1 ] = ind2 ;
indeces2d_arr [ i * 2 + 2 ] = ind2 ;
indeces2d_arr [ i * 2 + 3 ] = ind3 ;
indeces2d_arr [ i * 2 + 4 ] = ind3 ;
indeces2d_arr [ i * 2 + 5 ] = ind1 ;
}
return indeces2d_arr ;
}
private static string ReadString ( BinaryReader br , int len ) {
var NAME = string . Empty ;
int i = 0 ;
for ( i = 0 ; i < len ; + + i ) {
var c = br . ReadChar ( ) ;
if ( c = = '\0' ) {
+ + i ;
break ;
}
NAME + = c ;
}
for ( ; i < len ; + + i ) {
br . ReadChar ( ) ;
}
return NAME ;
}
}
}