2013-08-08 11:04:13 +00:00
#region = = = = = = = = = = = = = = = = = = Namespaces
using System ;
2012-04-17 19:13:47 +00:00
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 ;
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 ;
using SlimDX ;
using SlimDX.Direct3D9 ;
2013-04-11 09:27:16 +00:00
using CodeImp.DoomBuilder.Geometry ;
2012-04-17 19:13:47 +00:00
2013-08-08 11:04:13 +00:00
#endregion
2012-04-17 19:13:47 +00:00
//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
{
2013-03-18 13:52:27 +00:00
internal static class ModelReader
2013-08-08 11:04:13 +00:00
{
#region = = = = = = = = = = = = = = = = = = Variables
2013-03-18 13:52:27 +00:00
private class MD3LoadResult
{
public List < string > Skins ;
public List < Mesh > Meshes ;
public string Errors ;
2014-12-03 23:15:26 +00:00
public MD3LoadResult ( )
{
2013-03-18 13:52:27 +00:00
Skins = new List < string > ( ) ;
Meshes = new List < Mesh > ( ) ;
}
}
2013-08-08 11:04:13 +00:00
2015-04-14 11:33:57 +00:00
private static readonly VertexElement [ ] vertexElements = new [ ]
2014-12-03 23:15:26 +00:00
{
2015-04-14 11:33:57 +00:00
new VertexElement ( 0 , 0 , DeclarationType . Float3 , DeclarationMethod . Default , DeclarationUsage . Position , 0 ) ,
new VertexElement ( 0 , 12 , DeclarationType . Color , DeclarationMethod . Default , DeclarationUsage . Color , 0 ) ,
new VertexElement ( 0 , 16 , DeclarationType . Float2 , DeclarationMethod . Default , DeclarationUsage . TextureCoordinate , 0 ) ,
new VertexElement ( 0 , 24 , DeclarationType . Float3 , DeclarationMethod . Default , DeclarationUsage . Normal , 0 ) ,
VertexElement . VertexDeclarationEnd
} ;
2014-01-03 10:33:45 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Load
2014-12-03 23:15:26 +00:00
public static void Load ( ModelData mde , List < DataReader > containers , Device device )
{
2015-11-17 12:22:49 +00:00
if ( mde . IsVoxel ) LoadKVX ( mde , containers , device ) ;
else LoadModel ( mde , containers , device ) ;
2014-01-03 10:33:45 +00:00
}
2014-12-03 23:15:26 +00:00
private static void LoadKVX ( ModelData mde , List < DataReader > containers , Device device )
{
2014-01-03 10:33:45 +00:00
mde . Model = new GZModel ( ) ;
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < mde . ModelNames . Count ; i + + )
{
2014-01-03 10:33:45 +00:00
//find the model
Stream ms ;
2014-12-03 23:15:26 +00:00
foreach ( DataReader dr in containers )
{
2014-01-03 10:33:45 +00:00
ms = dr . GetVoxelData ( mde . ModelNames [ i ] ) ;
if ( ms = = null ) continue ;
//load kvx
ReadKVX ( mde , ms , device ) ;
//done
ms . Close ( ) ;
ms . Dispose ( ) ;
break ;
}
}
//clear unneeded data
mde . TextureNames = null ;
mde . ModelNames = null ;
2014-12-03 23:15:26 +00:00
if ( mde . Model . Meshes = = null | | mde . Model . Meshes . Count = = 0 )
{
2014-01-03 10:33:45 +00:00
mde . Model = null ;
}
}
2014-12-03 23:15:26 +00:00
private static void LoadModel ( ModelData mde , List < DataReader > containers , Device device )
{
2014-01-03 10:33:45 +00:00
mde . Model = new GZModel ( ) ;
BoundingBoxSizes bbs = new BoundingBoxSizes ( ) ;
MD3LoadResult result = new MD3LoadResult ( ) ;
2013-08-08 11:04:13 +00:00
2013-09-11 09:47:53 +00:00
//load models and textures
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < mde . ModelNames . Count ; i + + )
{
2013-03-18 13:52:27 +00:00
//need to use model skins?
bool useSkins = string . IsNullOrEmpty ( mde . TextureNames [ i ] ) ;
//load mesh
MemoryStream ms = LoadFile ( containers , mde . ModelNames [ i ] , true ) ;
2015-11-17 12:22:49 +00:00
if ( ms = = null )
2014-12-03 23:15:26 +00:00
{
2014-10-23 12:48:31 +00:00
General . ErrorLogger . Add ( ErrorType . Error , "Error while loading '" + mde . ModelNames [ i ] + "': unable to find file." ) ;
2013-07-30 09:25:27 +00:00
continue ;
}
2013-03-18 13:52:27 +00:00
string ext = Path . GetExtension ( mde . ModelNames [ i ] ) ;
2015-11-17 12:22:49 +00:00
switch ( ext )
2015-07-15 09:09:47 +00:00
{
case ".md3" :
2015-11-17 12:22:49 +00:00
if ( ! string . IsNullOrEmpty ( mde . FrameNames [ i ] ) )
{
General . ErrorLogger . Add ( ErrorType . Error , "Error while loading '" + mde . ModelNames [ i ] + "': frame names are not supported for MD3 models!" ) ;
continue ;
}
result = ReadMD3Model ( ref bbs , useSkins , ms , device , mde . FrameIndices [ i ] ) ;
2015-07-15 09:09:47 +00:00
break ;
case ".md2" :
2015-11-17 12:22:49 +00:00
result = ReadMD2Model ( ref bbs , ms , device , mde . FrameIndices [ i ] , mde . FrameNames [ i ] ) ;
2015-07-15 09:09:47 +00:00
break ;
default :
result . Errors = "model format is not supported" ;
break ;
}
2013-03-18 13:52:27 +00:00
ms . Close ( ) ;
ms . Dispose ( ) ;
//got errors?
2014-12-03 23:15:26 +00:00
if ( ! String . IsNullOrEmpty ( result . Errors ) )
{
2014-10-23 12:48:31 +00:00
General . ErrorLogger . Add ( ErrorType . Error , "Error while loading '" + mde . ModelNames [ i ] + "': " + result . Errors ) ;
2014-12-03 23:15:26 +00:00
}
else
{
2013-03-18 13:52:27 +00:00
//add loaded data to ModeldefEntry
mde . Model . Meshes . AddRange ( result . Meshes ) ;
//prepare UnknownTexture3D... just in case :)
if ( General . Map . Data . UnknownTexture3D . Texture = = null | | General . Map . Data . UnknownTexture3D . Texture . Disposed )
General . Map . Data . UnknownTexture3D . CreateTexture ( ) ;
//load texture
List < string > errors = new List < string > ( ) ;
2015-12-17 10:07:28 +00:00
// Texture not defined in MODELDEF?
if ( useSkins )
2014-12-03 23:15:26 +00:00
{
2013-03-18 13:52:27 +00:00
//try to use model's own skins
2014-12-03 23:15:26 +00:00
for ( int m = 0 ; m < result . Meshes . Count ; m + + )
{
if ( string . IsNullOrEmpty ( result . Skins [ m ] ) )
{
2013-03-18 13:52:27 +00:00
mde . Model . Textures . Add ( General . Map . Data . UnknownTexture3D . Texture ) ;
errors . Add ( "texture not found in MODELDEF or model skin." ) ;
continue ;
}
string path = result . Skins [ m ] . Replace ( Path . AltDirectorySeparatorChar , Path . DirectorySeparatorChar ) ;
ext = Path . GetExtension ( path ) ;
2015-12-17 10:07:28 +00:00
if ( Array . IndexOf ( ModelData . SUPPORTED_TEXTURE_EXTENSIONS , ext ) = = - 1 )
2014-12-03 23:15:26 +00:00
{
2013-03-18 13:52:27 +00:00
mde . Model . Textures . Add ( General . Map . Data . UnknownTexture3D . Texture ) ;
errors . Add ( "image format '" + ext + "' is not supported!" ) ;
continue ;
}
//relative path?
if ( path . IndexOf ( Path . DirectorySeparatorChar ) = = - 1 )
2015-06-21 21:35:28 +00:00
path = Path . Combine ( Path . GetDirectoryName ( mde . ModelNames [ useSkins ? i : m ] ) , path ) ;
2013-03-18 13:52:27 +00:00
Texture t = LoadTexture ( containers , path , device ) ;
2014-12-03 23:15:26 +00:00
if ( t = = null )
{
2013-03-18 13:52:27 +00:00
mde . Model . Textures . Add ( General . Map . Data . UnknownTexture3D . Texture ) ;
errors . Add ( "unable to load skin '" + result . Skins [ m ] + "'" ) ;
continue ;
}
mde . Model . Textures . Add ( t ) ;
}
2015-12-17 10:07:28 +00:00
}
//Try to use texture loaded from MODELDEFS
else
2014-12-03 23:15:26 +00:00
{
2013-03-18 13:52:27 +00:00
Texture t = LoadTexture ( containers , mde . TextureNames [ i ] , device ) ;
2014-12-03 23:15:26 +00:00
if ( t = = null )
{
2013-03-18 13:52:27 +00:00
mde . Model . Textures . Add ( General . Map . Data . UnknownTexture3D . Texture ) ;
errors . Add ( "unable to load texture '" + mde . TextureNames [ i ] + "'" ) ;
2014-12-03 23:15:26 +00:00
}
else
{
2013-03-18 13:52:27 +00:00
mde . Model . Textures . Add ( t ) ;
}
}
2012-04-17 19:13:47 +00:00
2013-03-18 13:52:27 +00:00
//report errors
2014-12-03 23:15:26 +00:00
if ( errors . Count > 0 )
{
2013-03-18 13:52:27 +00:00
foreach ( string e in errors )
2014-10-23 12:48:31 +00:00
General . ErrorLogger . Add ( ErrorType . Error , "Error while loading '" + mde . ModelNames [ i ] + "': " + e ) ;
2013-03-18 13:52:27 +00:00
}
}
}
2012-05-21 23:51:32 +00:00
2013-03-18 13:52:27 +00:00
//clear unneeded data
mde . TextureNames = null ;
mde . ModelNames = null ;
2012-04-17 19:13:47 +00:00
2014-12-03 23:15:26 +00:00
if ( mde . Model . Meshes = = null | | mde . Model . Meshes . Count = = 0 )
{
2013-09-11 09:47:53 +00:00
mde . Model = null ;
return ;
}
2012-04-17 19:13:47 +00:00
2015-06-22 19:52:23 +00:00
//scale bbs
bbs . MaxX = ( int ) ( bbs . MaxX * mde . Scale . X ) ;
bbs . MinX = ( int ) ( bbs . MinX * mde . Scale . X ) ;
bbs . MaxY = ( int ) ( bbs . MaxY * mde . Scale . Y ) ;
bbs . MinY = ( int ) ( bbs . MinY * mde . Scale . Y ) ;
//calculate model radius
mde . Model . Radius = Math . Max ( Math . Max ( Math . Abs ( bbs . MinY ) , Math . Abs ( bbs . MaxY ) ) , Math . Max ( Math . Abs ( bbs . MinX ) , Math . Abs ( bbs . MaxX ) ) ) ;
2013-08-08 11:04:13 +00:00
}
#endregion
#region = = = = = = = = = = = = = = = = = = MD3
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
private static MD3LoadResult ReadMD3Model ( ref BoundingBoxSizes bbs , bool useSkins , MemoryStream s , Device device , int frame )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
long start = s . Position ;
2013-03-18 13:52:27 +00:00
MD3LoadResult result = new MD3LoadResult ( ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
using ( var br = new BinaryReader ( s , Encoding . ASCII ) )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
string magic = ReadString ( br , 4 ) ;
2015-11-17 12:22:49 +00:00
if ( magic ! = "IDP3" )
{
result . Errors = "unknown header: expected 'IDP3', but got '" + magic + "'" ;
return result ;
}
int modelVersion = br . ReadInt32 ( ) ;
if ( modelVersion ! = 15 ) //MD3 version. Must be equal to 15
2014-12-03 23:15:26 +00:00
{
2015-11-17 12:22:49 +00:00
result . Errors = "expected MD3 version 15, but got " + modelVersion ;
2013-03-18 13:52:27 +00:00
return result ;
}
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
s . Position + = 76 ;
2013-09-11 09:47:53 +00:00
int numSurfaces = br . ReadInt32 ( ) ;
s . Position + = 12 ;
int ofsSurfaces = br . ReadInt32 ( ) ;
2012-04-17 19:13:47 +00:00
Model rendering (all modes): UDMF scale, pitch and roll are now displayed.
Thing Edit Form, UDMF: added controls for setting pitch, roll, scale, render style, fill color, alpha, health and score.
Visual mode, UDMF: UDMF scale is now applied when rendering sprites.
Added Thing Statistics form (Edit -> View Thing Types...), which shows all loaded thing types with some additional info.
Visual mode: sprites with negative ScaleX and positive ScaleY were not rendered properly.
Classic modes: display was not updated after loading a sprite.
Current testing engine change was not saved on closing the program when no other game configuration settings were changed.
2014-04-30 10:01:22 +00:00
s . Position = ofsSurfaces + start ;
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
List < int > polyIndecesList = new List < int > ( ) ;
List < WorldVertex > vertList = new List < WorldVertex > ( ) ;
2012-04-17 19:13:47 +00:00
2014-02-26 14:11:06 +00:00
Dictionary < string , List < List < int > > > polyIndecesListsPerTexture = new Dictionary < string , List < List < int > > > ( StringComparer . Ordinal ) ;
Dictionary < string , List < WorldVertex > > vertListsPerTexture = new Dictionary < string , List < WorldVertex > > ( StringComparer . Ordinal ) ;
Dictionary < string , List < int > > vertexOffsets = new Dictionary < string , List < int > > ( StringComparer . Ordinal ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
for ( int c = 0 ; c < numSurfaces ; c + + )
2014-12-03 23:15:26 +00:00
{
2013-03-18 13:52:27 +00:00
string skin = "" ;
2015-11-17 12:22:49 +00:00
string error = ReadSurface ( ref bbs , ref skin , br , polyIndecesList , vertList , frame ) ;
2012-04-17 19:13:47 +00:00
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( error ) )
{
2013-03-18 13:52:27 +00:00
result . Errors = error ;
return result ;
}
2012-04-17 19:13:47 +00:00
2014-12-03 23:15:26 +00:00
if ( useSkins )
{
if ( polyIndecesListsPerTexture . ContainsKey ( skin ) )
{
2013-03-18 13:52:27 +00:00
polyIndecesListsPerTexture [ skin ] . Add ( polyIndecesList ) ;
vertListsPerTexture [ skin ] . AddRange ( vertList . ToArray ( ) ) ;
vertexOffsets [ skin ] . Add ( vertList . Count ) ;
2014-12-03 23:15:26 +00:00
}
else
{
polyIndecesListsPerTexture . Add ( skin , new List < List < int > > { polyIndecesList } ) ;
2013-03-18 13:52:27 +00:00
vertListsPerTexture . Add ( skin , vertList ) ;
2014-12-03 23:15:26 +00:00
vertexOffsets . Add ( skin , new List < int > { vertList . Count } ) ;
2013-03-18 13:52:27 +00:00
}
//reset lists
polyIndecesList = new List < int > ( ) ;
vertList = new List < WorldVertex > ( ) ;
}
2013-09-11 09:47:53 +00:00
}
2012-04-17 19:13:47 +00:00
2014-12-03 23:15:26 +00:00
if ( ! useSkins )
{
//create mesh
2013-03-18 13:52:27 +00:00
CreateMesh ( device , ref result , vertList , polyIndecesList ) ;
result . Skins . Add ( "" ) ;
2014-12-03 23:15:26 +00:00
}
else
{
2013-03-18 13:52:27 +00:00
//create a mesh for each surface texture
2014-12-03 23:15:26 +00:00
foreach ( KeyValuePair < string , List < List < int > > > group in polyIndecesListsPerTexture )
{
2013-03-18 13:52:27 +00:00
polyIndecesList = new List < int > ( ) ;
2013-07-15 08:32:32 +00:00
int offset = 0 ;
2013-03-18 13:52:27 +00:00
//collect indices, fix vertex offsets
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < group . Value . Count ; i + + )
{
if ( i > 0 )
{
//TODO: Damn I need to rewrite all of this stuff from scratch...
offset + = vertexOffsets [ group . Key ] [ i - 1 ] ;
2013-07-15 08:32:32 +00:00
for ( int c = 0 ; c < group . Value [ i ] . Count ; c + + )
group . Value [ i ] [ c ] + = offset ;
2013-03-18 13:52:27 +00:00
}
polyIndecesList . AddRange ( group . Value [ i ] . ToArray ( ) ) ;
}
CreateMesh ( device , ref result , vertListsPerTexture [ group . Key ] , polyIndecesList ) ;
result . Skins . Add ( group . Key . ToLowerInvariant ( ) ) ;
}
}
2013-09-11 09:47:53 +00:00
}
return result ;
}
2015-11-17 12:22:49 +00:00
private static string ReadSurface ( ref BoundingBoxSizes bbs , ref string skin , BinaryReader br , List < int > polyIndecesList , List < WorldVertex > vertList , int frame )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
int vertexOffset = vertList . Count ;
long start = br . BaseStream . Position ;
2015-11-17 12:22:49 +00:00
2013-09-11 09:47:53 +00:00
string magic = ReadString ( br , 4 ) ;
2015-11-17 12:22:49 +00:00
if ( magic ! = "IDP3" ) return "error while reading surface. Unknown header: expected 'IDP3', but got '" + magic + "'" ;
2013-09-11 09:47:53 +00:00
2015-11-17 12:22:49 +00:00
string name = ReadString ( br , 64 ) ;
int flags = br . ReadInt32 ( ) ;
int numFrames = br . ReadInt32 ( ) ; //Number of animation frames. This should match NUM_FRAMES in the MD3 header.
int numShaders = br . ReadInt32 ( ) ; //Number of Shader objects defined in this Surface, with a limit of MD3_MAX_SHADERS. Current value of MD3_MAX_SHADERS is 256.
2013-09-11 09:47:53 +00:00
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.
2013-03-18 13:52:27 +00:00
int ofsShaders = br . ReadInt32 ( ) ;
2013-09-11 09:47:53 +00:00
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.
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// Sanity check
if ( frame < 0 | | frame > numFrames )
{
return "invalid frame number! (frame number: " + frame + ", total frames: " + numFrames + ")" ;
}
// Polygons
if ( start + ofsTriangles ! = br . BaseStream . Position )
2013-09-11 09:47:53 +00:00
br . BaseStream . Position = start + ofsTriangles ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
for ( int i = 0 ; i < numTriangles * 3 ; i + + )
polyIndecesList . Add ( vertexOffset + br . ReadInt32 ( ) ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// Shaders
2013-03-18 13:52:27 +00:00
if ( start + ofsShaders ! = br . BaseStream . Position )
br . BaseStream . Position = start + ofsShaders ;
skin = ReadString ( br , 64 ) ; //we are interested only in the first one
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// Vertices
if ( start + ofsST ! = br . BaseStream . Position )
2013-09-11 09:47:53 +00:00
br . BaseStream . Position = start + ofsST ;
2015-11-17 12:22:49 +00:00
for ( int i = 0 ; i < numVerts ; i + + )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
WorldVertex v = new WorldVertex ( ) ;
v . c = - 1 ; //white
v . u = br . ReadSingle ( ) ;
v . v = br . ReadSingle ( ) ;
vertList . Add ( v ) ;
}
2015-11-17 12:22:49 +00:00
// Positions and normals
long vertoffset = start + ofsNormal + numVerts * 8 * frame ; // The length of Vertex struct is 8 bytes
if ( br . BaseStream . Position ! = vertoffset ) br . BaseStream . Position = vertoffset ;
2013-09-11 09:47:53 +00:00
2015-11-17 12:22:49 +00:00
for ( int i = vertexOffset ; i < vertexOffset + numVerts ; i + + )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
WorldVertex v = vertList [ i ] ;
//read vertex
v . y = - ( float ) br . ReadInt16 ( ) / 64 ;
v . x = ( float ) br . ReadInt16 ( ) / 64 ;
v . z = ( float ) br . ReadInt16 ( ) / 64 ;
//bounding box
BoundingBoxTools . UpdateBoundingBoxSizes ( ref bbs , v ) ;
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
var lat = br . ReadByte ( ) * ( 2 * Math . PI ) / 255.0 ;
var lng = br . ReadByte ( ) * ( 2 * Math . PI ) / 255.0 ;
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +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
2013-09-11 09:47:53 +00:00
vertList [ i ] = v ;
}
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
if ( start + ofsEnd ! = br . BaseStream . Position )
2013-09-11 09:47:53 +00:00
br . BaseStream . Position = start + ofsEnd ;
return "" ;
}
2012-04-17 19:13:47 +00:00
2013-08-08 11:04:13 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = MD2
2015-11-17 12:22:49 +00:00
private static MD3LoadResult ReadMD2Model ( ref BoundingBoxSizes bbs , MemoryStream s , Device device , int frame , string framename )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
long start = s . Position ;
2013-03-18 13:52:27 +00:00
MD3LoadResult result = new MD3LoadResult ( ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
using ( var br = new BinaryReader ( s , Encoding . ASCII ) )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
string magic = ReadString ( br , 4 ) ;
2014-12-03 23:15:26 +00:00
if ( magic ! = "IDP2" ) //magic number: "IDP2"
{
2015-11-17 12:22:49 +00:00
result . Errors = "unknown header: expected 'IDP2', but got '" + magic + "'" ;
2013-03-18 13:52:27 +00:00
return result ;
}
2012-05-21 23:51:32 +00:00
2013-09-11 09:47:53 +00:00
int modelVersion = br . ReadInt32 ( ) ;
2014-12-03 23:15:26 +00:00
if ( modelVersion ! = 8 ) //MD2 version. Must be equal to 8
{
2015-11-17 12:22:49 +00:00
result . Errors = "expected MD3 version 15, but got " + modelVersion ;
2013-03-18 13:52:27 +00:00
return result ;
}
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
int texWidth = br . ReadInt32 ( ) ;
int texHeight = br . ReadInt32 ( ) ;
2015-11-17 12:22:49 +00:00
int framesize = br . ReadInt32 ( ) ; // Size of one frame in bytes
s . Position + = 4 ; //Number of textures
2013-09-11 09:47:53 +00:00
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
2015-11-17 12:22:49 +00:00
int num_frames = br . ReadInt32 ( ) ; //Total number of frames
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// Sanity checks
if ( num_frames = = 0 )
{
2013-03-18 13:52:27 +00:00
result . Errors = "model has 0 frames." ;
return result ;
}
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
if ( num_frames < frame | | frame < 0 )
{
result . Errors = "invalid target frame! (target frame: " + frame + ", total frames: " + num_frames + ")" ;
return result ;
}
2013-09-11 09:47:53 +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
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
List < int > polyIndecesList = new List < int > ( ) ;
List < int > uvIndecesList = new List < int > ( ) ;
List < Vector2 > uvCoordsList = new List < Vector2 > ( ) ;
List < WorldVertex > vertList = new List < WorldVertex > ( ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// Polygons
Model rendering (all modes): UDMF scale, pitch and roll are now displayed.
Thing Edit Form, UDMF: added controls for setting pitch, roll, scale, render style, fill color, alpha, health and score.
Visual mode, UDMF: UDMF scale is now applied when rendering sprites.
Added Thing Statistics form (Edit -> View Thing Types...), which shows all loaded thing types with some additional info.
Visual mode: sprites with negative ScaleX and positive ScaleY were not rendered properly.
Classic modes: display was not updated after loading a sprite.
Current testing engine change was not saved on closing the program when no other game configuration settings were changed.
2014-04-30 10:01:22 +00:00
s . Position = ofs_tris + start ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
for ( int i = 0 ; i < num_tris ; i + + )
2014-12-03 23:15:26 +00:00
{
2014-02-21 14:42:12 +00:00
polyIndecesList . Add ( br . ReadUInt16 ( ) ) ;
polyIndecesList . Add ( br . ReadUInt16 ( ) ) ;
polyIndecesList . Add ( br . ReadUInt16 ( ) ) ;
2012-04-17 19:13:47 +00:00
2014-02-21 14:42:12 +00:00
uvIndecesList . Add ( br . ReadUInt16 ( ) ) ;
uvIndecesList . Add ( br . ReadUInt16 ( ) ) ;
uvIndecesList . Add ( br . ReadUInt16 ( ) ) ;
2013-09-11 09:47:53 +00:00
}
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// UV coords
Model rendering (all modes): UDMF scale, pitch and roll are now displayed.
Thing Edit Form, UDMF: added controls for setting pitch, roll, scale, render style, fill color, alpha, health and score.
Visual mode, UDMF: UDMF scale is now applied when rendering sprites.
Added Thing Statistics form (Edit -> View Thing Types...), which shows all loaded thing types with some additional info.
Visual mode: sprites with negative ScaleX and positive ScaleY were not rendered properly.
Classic modes: display was not updated after loading a sprite.
Current testing engine change was not saved on closing the program when no other game configuration settings were changed.
2014-04-30 10:01:22 +00:00
s . Position = ofs_uv + start ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
for ( int i = 0 ; i < num_uv ; i + + )
2013-09-11 09:47:53 +00:00
uvCoordsList . Add ( new Vector2 ( ( float ) br . ReadInt16 ( ) / texWidth , ( float ) br . ReadInt16 ( ) / texHeight ) ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
// Frames
// Find correct frame
if ( ! string . IsNullOrEmpty ( framename ) )
{
// Skip frames untill frame name matches
bool framefound = false ;
for ( int i = 0 ; i < num_frames ; i + + )
{
s . Position = ofs_animFrame + start + i * framesize ;
s . Position + = 24 ; // Skip scale and translate
string curframename = ReadString ( br , 16 ) . ToLowerInvariant ( ) ;
if ( curframename = = framename )
{
// Step back so scale and translate can be read
s . Position - = 40 ;
framefound = true ;
break ;
}
}
// No dice? Bail out!
if ( ! framefound )
{
result . Errors = "unable to find frame '" + framename + "'!" ;
return result ;
}
}
else
{
// If we have frame number, we can go directly to target frame
s . Position = ofs_animFrame + start + frame * framesize ;
}
2012-04-17 19:13:47 +00:00
2014-02-21 14:42:12 +00:00
Vector3 scale = new Vector3 ( br . ReadSingle ( ) , br . ReadSingle ( ) , br . ReadSingle ( ) ) ;
Vector3 translate = new Vector3 ( br . ReadSingle ( ) , br . ReadSingle ( ) , br . ReadSingle ( ) ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
s . Position + = 16 ; // Skip frame name
2012-04-17 19:13:47 +00:00
2014-12-10 22:50:16 +00:00
// Prepare to fix rotation angle
float angleOfsetCos = ( float ) Math . Cos ( - Angle2D . PIHALF ) ;
float angleOfsetSin = ( float ) Math . Sin ( - Angle2D . PIHALF ) ;
2013-09-11 09:47:53 +00:00
//verts
2015-11-17 12:22:49 +00:00
for ( int i = 0 ; i < num_verts ; i + + )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
WorldVertex v = new WorldVertex ( ) ;
2014-02-21 14:42:12 +00:00
v . x = ( br . ReadByte ( ) * scale . X + translate . X ) ;
v . y = ( br . ReadByte ( ) * scale . Y + translate . Y ) ;
v . z = ( br . ReadByte ( ) * scale . Z + translate . Z ) ;
2013-09-11 09:47:53 +00:00
2014-12-10 22:50:16 +00:00
// Fix rotation angle
float rx = angleOfsetCos * v . x - angleOfsetSin * v . y ;
float ry = angleOfsetSin * v . x + angleOfsetCos * v . y ;
v . y = ry ;
v . x = rx ;
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
vertList . Add ( v ) ;
s . Position + = 1 ; //vertex normal
}
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
for ( int i = 0 ; i < polyIndecesList . Count ; i + + )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
WorldVertex v = vertList [ polyIndecesList [ i ] ] ;
//bounding box
BoundingBoxTools . UpdateBoundingBoxSizes ( ref bbs , new WorldVertex ( v . y , v . x , v . z ) ) ;
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
//uv
2012-11-20 22:08:34 +00:00
float tu = uvCoordsList [ uvIndecesList [ i ] ] . X ;
2013-09-11 09:47:53 +00:00
float tv = uvCoordsList [ uvIndecesList [ i ] ] . Y ;
2012-11-20 22:08:34 +00:00
//uv-coordinates already set?
2014-12-03 23:15:26 +00:00
if ( v . c = = - 1 & & ( v . u ! = tu | | v . v ! = tv ) )
{
2012-11-20 22:08:34 +00:00
//add a new vertex
vertList . Add ( new WorldVertex ( v . x , v . y , v . z , - 1 , tu , tv ) ) ;
polyIndecesList [ i ] = vertList . Count - 1 ;
2014-12-03 23:15:26 +00:00
}
else
{
2012-11-20 22:08:34 +00:00
v . u = tu ;
v . v = tv ;
v . c = - 1 ; //set color to white
//return to proper place
vertList [ polyIndecesList [ i ] ] = v ;
}
2013-09-11 09:47:53 +00:00
}
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
//mesh
2014-01-03 10:33:45 +00:00
Mesh mesh = new Mesh ( device , polyIndecesList . Count / 3 , vertList . Count , MeshFlags . Use32Bit | MeshFlags . IndexBufferManaged | MeshFlags . VertexBufferManaged , vertexElements ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
using ( DataStream stream = mesh . LockVertexBuffer ( LockFlags . None ) )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
stream . WriteRange ( vertList . ToArray ( ) ) ;
}
mesh . UnlockVertexBuffer ( ) ;
2012-04-17 19:13:47 +00:00
2015-11-17 12:22:49 +00:00
using ( DataStream stream = mesh . LockIndexBuffer ( LockFlags . None ) )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
stream . WriteRange ( polyIndecesList . ToArray ( ) ) ;
}
mesh . UnlockIndexBuffer ( ) ;
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
mesh . OptimizeInPlace ( MeshOptimizeFlags . AttributeSort ) ;
2012-04-17 19:13:47 +00:00
2013-03-18 13:52:27 +00:00
//store in result
result . Meshes . Add ( mesh ) ;
result . Skins . Add ( "" ) ; //no skin support for MD2
2013-09-11 09:47:53 +00:00
}
2012-04-17 19:13:47 +00:00
2013-09-11 09:47:53 +00:00
return result ;
2013-08-08 11:04:13 +00:00
}
#endregion
2014-01-03 10:33:45 +00:00
#region = = = = = = = = = = = = = = = = = = KVX
2014-12-03 23:15:26 +00:00
private static void ReadKVX ( ModelData mde , Stream stream , Device device )
{
2014-01-03 10:33:45 +00:00
PixelColor [ ] palette = new PixelColor [ 256 ] ;
List < WorldVertex > verts = new List < WorldVertex > ( ) ;
int xsize , ysize , zsize ;
Vector3D pivot ;
2014-12-03 23:15:26 +00:00
using ( BinaryReader reader = new BinaryReader ( stream , Encoding . ASCII ) )
{
2014-01-03 10:33:45 +00:00
reader . ReadInt32 ( ) ; //numbytes, we don't use that
xsize = reader . ReadInt32 ( ) ;
ysize = reader . ReadInt32 ( ) ;
zsize = reader . ReadInt32 ( ) ;
pivot = new Vector3D ( ) ;
pivot . x = reader . ReadInt32 ( ) / 256f ;
pivot . y = reader . ReadInt32 ( ) / 256f ;
pivot . z = reader . ReadInt32 ( ) / 256f ;
//read offsets
int [ ] xoffset = new int [ xsize + 1 ] ; //why is it xsize + 1, not xsize?..
short [ , ] xyoffset = new short [ xsize , ysize + 1 ] ; //why is it ysize + 1, not ysize?..
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < xoffset . Length ; i + + )
{
2014-01-03 10:33:45 +00:00
xoffset [ i ] = reader . ReadInt32 ( ) ;
}
2014-12-03 23:15:26 +00:00
for ( int x = 0 ; x < xsize ; x + + )
{
for ( int y = 0 ; y < ysize + 1 ; y + + )
{
2014-01-03 10:33:45 +00:00
xyoffset [ x , y ] = reader . ReadInt16 ( ) ;
}
}
//read slabs
List < int > offsets = new List < int > ( xsize * ysize ) ;
2014-12-03 23:15:26 +00:00
for ( int x = 0 ; x < xsize ; x + + )
{
for ( int y = 0 ; y < ysize ; y + + )
{
2014-01-03 10:33:45 +00:00
offsets . Add ( xoffset [ x ] + xyoffset [ x , y ] + 28 ) ; //for some reason offsets are counted from start of xoffset[]...
}
}
int counter = 0 ;
int slabsEnd = ( int ) ( reader . BaseStream . Length - 768 ) ;
//read palette
2014-12-03 23:15:26 +00:00
if ( ! mde . OverridePalette )
{
2014-01-03 10:33:45 +00:00
reader . BaseStream . Position = slabsEnd ;
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < 256 ; i + + )
{
2014-01-03 10:33:45 +00:00
byte r = ( byte ) ( reader . ReadByte ( ) * 4 ) ;
byte g = ( byte ) ( reader . ReadByte ( ) * 4 ) ;
byte b = ( byte ) ( reader . ReadByte ( ) * 4 ) ;
palette [ i ] = new PixelColor ( 255 , r , g , b ) ;
}
2014-12-03 23:15:26 +00:00
}
else
{
for ( int i = 0 ; i < 256 ; i + + )
{
2014-01-03 10:33:45 +00:00
palette [ i ] = General . Map . Data . Palette [ i ] ;
}
}
2014-12-03 23:15:26 +00:00
for ( int x = 0 ; x < xsize ; x + + )
{
for ( int y = 0 ; y < ysize ; y + + )
{
2014-01-03 10:33:45 +00:00
reader . BaseStream . Position = offsets [ counter ] ;
int next = ( counter < offsets . Count - 1 ? offsets [ counter + 1 ] : slabsEnd ) ;
//read slab
2014-12-03 23:15:26 +00:00
while ( reader . BaseStream . Position < next )
{
2014-01-03 10:33:45 +00:00
int ztop = reader . ReadByte ( ) ;
int zleng = reader . ReadByte ( ) ;
if ( ztop + zleng > zsize ) break ;
int flags = reader . ReadByte ( ) ;
2014-12-03 23:15:26 +00:00
if ( zleng > 0 )
{
2014-01-03 10:33:45 +00:00
List < int > colorIndices = new List < int > ( zleng ) ;
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < zleng ; i + + )
{
2014-01-03 10:33:45 +00:00
colorIndices . Add ( reader . ReadByte ( ) ) ;
}
2014-12-03 23:15:26 +00:00
if ( ( flags & 16 ) ! = 0 )
{
2014-12-10 22:50:16 +00:00
AddFace ( verts , new Vector3D ( x , y , ztop ) , new Vector3D ( x + 1 , y , ztop ) , new Vector3D ( x , y + 1 , ztop ) , new Vector3D ( x + 1 , y + 1 , ztop ) , pivot , colorIndices [ 0 ] ) ;
2014-01-03 10:33:45 +00:00
}
int z = ztop ;
int cstart = 0 ;
2014-12-03 23:15:26 +00:00
while ( z < ztop + zleng )
{
2014-01-03 10:33:45 +00:00
int c = 0 ;
while ( z + c < ztop + zleng & & colorIndices [ cstart + c ] = = colorIndices [ cstart ] ) c + + ;
2014-12-03 23:15:26 +00:00
if ( ( flags & 1 ) ! = 0 )
{
2014-12-10 22:50:16 +00:00
AddFace ( verts , new Vector3D ( x , y , z ) , new Vector3D ( x , y + 1 , z ) , new Vector3D ( x , y , z + c ) , new Vector3D ( x , y + 1 , z + c ) , pivot , colorIndices [ cstart ] ) ;
2014-01-03 10:33:45 +00:00
}
2014-12-03 23:15:26 +00:00
if ( ( flags & 2 ) ! = 0 )
{
2014-12-10 22:50:16 +00:00
AddFace ( verts , new Vector3D ( x + 1 , y + 1 , z ) , new Vector3D ( x + 1 , y , z ) , new Vector3D ( x + 1 , y + 1 , z + c ) , new Vector3D ( x + 1 , y , z + c ) , pivot , colorIndices [ cstart ] ) ;
2014-01-03 10:33:45 +00:00
}
2014-12-03 23:15:26 +00:00
if ( ( flags & 4 ) ! = 0 )
{
2014-12-10 22:50:16 +00:00
AddFace ( verts , new Vector3D ( x + 1 , y , z ) , new Vector3D ( x , y , z ) , new Vector3D ( x + 1 , y , z + c ) , new Vector3D ( x , y , z + c ) , pivot , colorIndices [ cstart ] ) ;
2014-01-03 10:33:45 +00:00
}
2014-12-03 23:15:26 +00:00
if ( ( flags & 8 ) ! = 0 )
{
2014-12-10 22:50:16 +00:00
AddFace ( verts , new Vector3D ( x , y + 1 , z ) , new Vector3D ( x + 1 , y + 1 , z ) , new Vector3D ( x , y + 1 , z + c ) , new Vector3D ( x + 1 , y + 1 , z + c ) , pivot , colorIndices [ cstart ] ) ;
2014-01-03 10:33:45 +00:00
}
if ( c = = 0 ) c + + ;
z + = c ;
cstart + = c ;
}
2014-12-03 23:15:26 +00:00
if ( ( flags & 32 ) ! = 0 )
{
2014-01-03 10:33:45 +00:00
z = ztop + zleng - 1 ;
2014-12-10 22:50:16 +00:00
AddFace ( verts , new Vector3D ( x + 1 , y , z + 1 ) , new Vector3D ( x , y , z + 1 ) , new Vector3D ( x + 1 , y + 1 , z + 1 ) , new Vector3D ( x , y + 1 , z + 1 ) , pivot , colorIndices [ zleng - 1 ] ) ;
2014-01-03 10:33:45 +00:00
}
}
}
counter + + ;
}
}
}
2015-06-22 19:52:23 +00:00
// get model extents
int minX = ( int ) ( ( xsize / 2f - pivot . x ) * mde . Scale . X ) ;
int maxX = ( int ) ( ( xsize / 2f + pivot . x ) * mde . Scale . X ) ;
int minY = ( int ) ( ( ysize / 2f - pivot . y ) * mde . Scale . Y ) ;
int maxY = ( int ) ( ( ysize / 2f + pivot . y ) * mde . Scale . Y ) ;
// calculate model radius
mde . Model . Radius = Math . Max ( Math . Max ( Math . Abs ( minY ) , Math . Abs ( maxY ) ) , Math . Max ( Math . Abs ( minX ) , Math . Abs ( maxX ) ) ) ;
2014-01-03 10:33:45 +00:00
//create texture
MemoryStream memstream = new MemoryStream ( ( 4096 * 4 ) + 4096 ) ;
2015-05-27 10:34:25 +00:00
using ( Bitmap bmp = CreateVoxelTexture ( palette ) ) bmp . Save ( memstream , ImageFormat . Bmp ) ;
2014-01-03 10:33:45 +00:00
memstream . Seek ( 0 , SeekOrigin . Begin ) ;
Texture texture = Texture . FromStream ( device , memstream , ( int ) memstream . Length , 64 , 64 , 0 , Usage . None , Format . Unknown , Pool . Managed , Filter . Point , Filter . Box , 0 ) ;
memstream . Dispose ( ) ;
//add texture
mde . Model . Textures . Add ( texture ) ;
//create mesh
int [ ] indices = new int [ verts . Count ] ;
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < verts . Count ; i + + )
{
2014-01-03 10:33:45 +00:00
indices [ i ] = i ;
}
Mesh mesh = new Mesh ( device , verts . Count / 3 , verts . Count , MeshFlags . Use32Bit | MeshFlags . IndexBufferManaged | MeshFlags . VertexBufferManaged , vertexElements ) ;
DataStream mstream = mesh . VertexBuffer . Lock ( 0 , 0 , LockFlags . None ) ;
mstream . WriteRange ( verts . ToArray ( ) ) ;
mesh . VertexBuffer . Unlock ( ) ;
mstream = mesh . IndexBuffer . Lock ( 0 , 0 , LockFlags . None ) ;
mstream . WriteRange ( indices ) ;
mesh . IndexBuffer . Unlock ( ) ;
mesh . OptimizeInPlace ( MeshOptimizeFlags . AttributeSort ) ;
//add mesh
mde . Model . Meshes . Add ( mesh ) ;
}
// Shameless GZDoom rip-off
2014-12-10 22:50:16 +00:00
private static void AddFace ( List < WorldVertex > verts , Vector3D v1 , Vector3D v2 , Vector3D v3 , Vector3D v4 , Vector3D pivot , int colorIndex )
2014-12-03 23:15:26 +00:00
{
2014-01-03 10:33:45 +00:00
float pu0 = ( colorIndex % 16 ) / 16f ;
float pu1 = pu0 + 0.0001f ;
float pv0 = ( colorIndex / 16 ) / 16f ;
float pv1 = pv0 + 0.0001f ;
WorldVertex wv1 = new WorldVertex ( ) ;
wv1 . x = v1 . x - pivot . x ;
wv1 . y = - v1 . y + pivot . y ;
wv1 . z = - v1 . z + pivot . z ;
2015-05-27 11:05:57 +00:00
wv1 . c = - 1 ;
2014-01-03 10:33:45 +00:00
wv1 . u = pu0 ;
wv1 . v = pv0 ;
verts . Add ( wv1 ) ;
WorldVertex wv2 = new WorldVertex ( ) ;
wv2 . x = v2 . x - pivot . x ;
wv2 . y = - v2 . y + pivot . y ;
wv2 . z = - v2 . z + pivot . z ;
2015-05-27 11:05:57 +00:00
wv2 . c = - 1 ;
2014-01-03 10:33:45 +00:00
wv2 . u = pu1 ;
wv2 . v = pv1 ;
verts . Add ( wv2 ) ;
WorldVertex wv4 = new WorldVertex ( ) ;
wv4 . x = v4 . x - pivot . x ;
wv4 . y = - v4 . y + pivot . y ;
wv4 . z = - v4 . z + pivot . z ;
2015-05-27 11:05:57 +00:00
wv4 . c = - 1 ;
2014-01-03 10:33:45 +00:00
wv4 . u = pu0 ;
wv4 . v = pv0 ;
verts . Add ( wv4 ) ;
WorldVertex wv3 = new WorldVertex ( ) ;
wv3 . x = v3 . x - pivot . x ;
wv3 . y = - v3 . y + pivot . y ;
wv3 . z = - v3 . z + pivot . z ;
2015-05-27 11:05:57 +00:00
wv3 . c = - 1 ;
2014-01-03 10:33:45 +00:00
wv3 . u = pu1 ;
wv3 . v = pv1 ;
verts . Add ( wv3 ) ;
verts . Add ( wv1 ) ;
verts . Add ( wv4 ) ;
}
2014-12-03 23:15:26 +00:00
private unsafe static Bitmap CreateVoxelTexture ( PixelColor [ ] palette )
{
2014-01-03 10:33:45 +00:00
Bitmap bmp = new Bitmap ( 16 , 16 ) ;
BitmapData bmpdata = bmp . LockBits ( new Rectangle ( 0 , 0 , 16 , 16 ) , ImageLockMode . WriteOnly , PixelFormat . Format32bppArgb ) ;
2014-12-03 23:15:26 +00:00
if ( bmpdata ! = null )
{
2014-01-03 10:33:45 +00:00
PixelColor * pixels = ( PixelColor * ) ( bmpdata . Scan0 . ToPointer ( ) ) ;
const int numpixels = 256 ;
int i = 255 ;
2014-12-03 23:15:26 +00:00
for ( PixelColor * cp = pixels + numpixels - 1 ; cp > = pixels ; cp - - , i - - )
{
2014-01-03 10:33:45 +00:00
cp - > r = palette [ i ] . r ;
cp - > g = palette [ i ] . g ;
cp - > b = palette [ i ] . b ;
cp - > a = palette [ i ] . a ;
}
bmp . UnlockBits ( bmpdata ) ;
}
//scale bitmap, so colors stay (almost) the same when bilinear filtering is enabled
Bitmap scaled = new Bitmap ( 64 , 64 ) ;
2014-12-03 23:15:26 +00:00
using ( Graphics gs = Graphics . FromImage ( scaled ) )
{
2014-01-03 10:33:45 +00:00
gs . InterpolationMode = System . Drawing . Drawing2D . InterpolationMode . NearestNeighbor ;
gs . DrawImage ( bmp , new Rectangle ( 0 , 0 , 64 , 64 ) , new Rectangle ( 0 , 0 , 16 , 16 ) , GraphicsUnit . Pixel ) ;
}
2015-05-27 10:34:25 +00:00
bmp . Dispose ( ) ;
2014-01-03 10:33:45 +00:00
return scaled ;
}
#endregion
2013-08-08 11:04:13 +00:00
#region = = = = = = = = = = = = = = = = = = Utility
2012-04-17 19:13:47 +00:00
2014-12-03 23:15:26 +00:00
private static MemoryStream LoadFile ( List < DataReader > containers , string path , bool isModel )
{
foreach ( DataReader dr in containers )
{
2013-03-18 13:52:27 +00:00
if ( isModel & & dr is WADReader ) continue ; //models cannot be stored in WADs
//load file
2014-12-03 23:15:26 +00:00
if ( dr . FileExists ( path ) ) return dr . LoadFile ( path ) ;
2013-03-18 13:52:27 +00:00
}
return null ;
}
2014-12-03 23:15:26 +00:00
private static Texture LoadTexture ( List < DataReader > containers , string path , Device device )
{
2013-03-18 13:52:27 +00:00
if ( string . IsNullOrEmpty ( path ) ) return null ;
MemoryStream ms = LoadFile ( containers , path , false ) ;
if ( ms = = null ) return null ;
Texture texture = null ;
//create texture
2014-12-03 23:15:26 +00:00
if ( Path . GetExtension ( path ) = = ".pcx" ) //pcx format requires special handling...
{
2013-03-18 13:52:27 +00:00
FileImageReader fir = new FileImageReader ( ) ;
Bitmap bitmap = fir . ReadAsBitmap ( ms ) ;
ms . Close ( ) ;
ms . Dispose ( ) ;
2014-12-03 23:15:26 +00:00
if ( bitmap ! = null )
{
2014-02-21 14:42:12 +00:00
BitmapData bmlock = bitmap . LockBits ( new Rectangle ( 0 , 0 , bitmap . Width , bitmap . Height ) , ImageLockMode . ReadOnly , bitmap . PixelFormat ) ;
2013-07-29 08:50:50 +00:00
texture = new Texture ( device , bitmap . Width , bitmap . Height , 1 , Usage . None , Format . A8R8G8B8 , Pool . Managed ) ;
2013-03-18 13:52:27 +00:00
2013-07-29 08:50:50 +00:00
DataRectangle textureLock = texture . LockRectangle ( 0 , LockFlags . None ) ;
textureLock . Data . WriteRange ( bmlock . Scan0 , bmlock . Height * bmlock . Stride ) ;
2013-03-18 13:52:27 +00:00
2013-07-29 08:50:50 +00:00
bitmap . UnlockBits ( bmlock ) ;
texture . UnlockRectangle ( 0 ) ;
}
2014-12-03 23:15:26 +00:00
}
else
{
2013-07-29 08:50:50 +00:00
texture = Texture . FromStream ( device , ms ) ;
2013-03-18 13:52:27 +00:00
2013-07-29 08:50:50 +00:00
ms . Close ( ) ;
ms . Dispose ( ) ;
2013-03-18 13:52:27 +00:00
}
return texture ;
}
2015-11-17 12:22:49 +00:00
private static void CreateMesh ( Device device , ref MD3LoadResult result , List < WorldVertex > verts , List < int > indices )
{
//create mesh
Mesh mesh = new Mesh ( device , indices . Count / 3 , verts . Count , MeshFlags . Use32Bit | MeshFlags . IndexBufferManaged | MeshFlags . VertexBufferManaged , vertexElements ) ;
using ( DataStream stream = mesh . LockVertexBuffer ( LockFlags . None ) )
{
stream . WriteRange ( verts . ToArray ( ) ) ;
}
mesh . UnlockVertexBuffer ( ) ;
using ( DataStream stream = mesh . LockIndexBuffer ( LockFlags . None ) )
{
stream . WriteRange ( indices . ToArray ( ) ) ;
}
mesh . UnlockIndexBuffer ( ) ;
mesh . OptimizeInPlace ( MeshOptimizeFlags . AttributeSort ) ;
//store in result
result . Meshes . Add ( mesh ) ;
}
2014-12-03 23:15:26 +00:00
private static string ReadString ( BinaryReader br , int len )
{
2015-11-17 12:22:49 +00:00
string result = string . Empty ;
2014-02-21 14:42:12 +00:00
int i ;
2015-11-17 12:22:49 +00:00
for ( i = 0 ; i < len ; + + i )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
var c = br . ReadChar ( ) ;
2015-11-17 12:22:49 +00:00
if ( c = = '\0' )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
+ + i ;
break ;
}
2015-11-17 12:22:49 +00:00
result + = c ;
2013-09-11 09:47:53 +00:00
}
2015-11-17 12:22:49 +00:00
for ( ; i < len ; + + i ) br . ReadChar ( ) ;
return result ;
2013-08-08 11:04:13 +00:00
}
#endregion
}
2012-04-17 19:13:47 +00:00
}