读取MD3文件,暂时只支持静态的,Delphi版本

其实,也就是读取文件罢了,一看MD3都是OPENGL的,很少有DX的,搞得我十分郁闷,只好自己写了。。。

unit VRMD3Loader;

interface

uses

VRCommon,DXCommon,Direct3D9,VRTexture,SysUtils,VRMaterial,D3DX9,Windows,VRFile,VRModelObject{,Log};

const MD3FVF = D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1;

Type

// 下面的结构体保存了从文件的头部信息

TMd3Header = packed record

fileID : array[0..3] of char; // 文件ID,对于MD3格式的文件,必须是IDP3

version : integer; // MD3格式文件的版本号,必须是15

strFile : array[0..67] of char; // 文件的名

numFrames : integer; // 动画帧的数目

numTags : integer; // 标签的数目

numMeshes : integer; // 网格中子对象的数目

numMaxSkins : integer; // 网格中皮肤的数目

headerSize : integer; // 文件头部大小

tagStart : integer; // 标签信息的开始位置

tagEnd : integer; // 标签信息的结束位置

fileSize : integer; // 文件大小

end;

// 下面的结构体保存了MD3模型中的网格数据

TMd3MeshInfo = packed record

meshID : array[0..3]of char; // 网格ID

strName : array[0..67] of char; // 网格名称

numMeshFrames : integer; // 帧的数目

numSkins : integer; // 皮肤的数目

numVertices : integer; // 顶点数目

numTriangles : integer; // 三角形面的数目

triStart : integer; // 三角形的开始位置

headerSize : integer; // 网格的头部数据的大小

uvStart : integer; // UV纹理坐标的开始位置

vertexStart : integer; // 顶点数据的开始位置

meshSize : integer; // 整个网格数据的大小

end;

PMd3MeshInfo = ^TMd3MeshInfo;

// MD3文件格式的标签结构

TMd3Tag = packed record

strName : array[0..63] of char; // 标签的名称

vPosition : TPoint3; // 平移变换

rotation : array[0..2,0..2] of single; // 旋转矩阵

end;

// 下面的结构体保存了骨骼信息

TMd3Bone = packed record

mins : array[0..2] of single; // 最小值(x, y, z)

maxs : array[0..2] of single; // 最大值(x, y, z)

position : array[0..2] of single; // 骨骼位置

scale : single; // 骨骼缩放比例

creator : array[0..15] of char;

end;

// 下面的结构体保存了面的法向量和顶点索引

TMd3Triangle = packed record

Vertex : Array[0..2] of Integer;

end;

// 下面的结构体保存UV纹理坐标

TMd3TexCoord = packed record

textureCoord : array[0..1] of single;

end;

// 下面的结构体保存皮肤名称(也就是纹理名称)

TMd3Skin = packed record

strName : array[0..67] of char;

end;

TMD3Vertex = Record

Vertex : Array[0..2] of Smallint;

Normal : Array[0..1] of Byte;

end;

TVRMD3Mesh = packed Record

MeshHeader : TMd3MeshInfo;

Skins : Array of Array[0..67] of Char;

Triangle : Array of TMd3Triangle;

TexCoord : Array of TMd3TexCoord;

Vertex : Array of TMD3Vertex;

Texture : IVRTexture;

SetTexture : Boolean;

end;

PVRMD3Mesh = ^TVRMD3Mesh;

PVRMD3Loader = ^TVRMD3Loader;

TMeshVertex = packed record

Pos : TPoint3;

Normal : TPoint3;

Tex : TPoint2;

end;

PMeshVertex = ^TMeshVertex;

TVRMD3MeshLoader = class( TObject )//MD3 Mesh读取器,以MESH为单位绘制

private

Material : IVRMaterial;

Info : PVRMD3Mesh;

Matrix : TD3DMatrix;

Buffer : IDirect3DVertexBuffer9;

IndexBuf : IDirect3DIndexBuffer9;

Procedure SwapAxis();

procedure PrePare();

public

Procedure RotationYLocal( const Angle : single );//绕本地 Y轴 旋转,角度为弧度制

Procedure RotationXLocal( const Angle : Single );//绕本地 X轴 旋转,角度为弧度制

Procedure RotationZLocal( const Angle : Single );//绕本地 Z轴 旋转,角度为弧度制

Procedure Render();//暂时这样

procedure SetTexture( const Index : integer;const Texture : IVRTexture );

Constructor Create( const addr : PVRMD3Mesh );

Destructor Destroy;override;

end;

TVRMD3Loader = class( TInterfacedObject, IVRMD3Loader) //即为单个*.MD3文件

private

TextureList : Array of IVRTexture;

Head : TMd3Header;

//MeshInfo : TMd3MeshInfo;

Bones : array of TMd3Bone;

Tags : array of TMD3Tag;

Links : array of PVRMD3Loader;

Meshes : array of TVRMD3Mesh;

Models : array of TVRMD3MeshLoader;

procedure UnInit();

Procedure CreateMeshes();

function LoadSkin(const Imagepath, filename: String): Boolean;

function LoadMD3( const FileName : string ) : Boolean;

public

function Init( const FileName : String ) : Boolean;

Procedure RotationYLocal( const Angle : single );//绕本地 Y轴 旋转,角度为弧度制

Procedure RotationXLocal( const Angle : Single );//绕本地 X轴 旋转,角度为弧度制

Procedure RotationZLocal( const Angle : Single );//绕本地 Z轴 旋转,角度为弧度制

// procedure Render( const CurrentFrame,NextFrame : integer; const pol :Real);overload; //暂时不用

procedure Render;overload;

procedure SetTexture( const MeshIndex : integer;const TextureIndex : integer; const Texture : IVRTexture );

function GetTexture( const MeshIndex : integer;const TextureIndex : integer=0 ) : IVRTexture;

function GetMeshCount() : integer;

Destructor Destroy();override;

end;

TVRMD3Anim = class //MD3动画

end;

implementation

var anorms : Array[0..255, 0..255, 0..2] of Real;

procedure InitNormals;

var I, J : Integer;

alpha, beta : Real;

begin

for I :=0 to 255 do

begin

for J :=0 to 255 do

begin

alpha :=2*I*pi/255;

beta :=2*j*pi/255;

anorms[i][j][0] := cos(beta) * sin(alpha);

anorms[i][j][1] := sin(beta) * sin(alpha);

anorms[i][j][2] := cos(alpha);

end;

end;

end;

function CharArrToStr(const C : Array of Char) : String;

var I : Integer;

begin

// convert the array of characters to a String

I :=0;

result :='';

while C[i] <> #0 do

begin

result := result + C[I];

Inc(I);

end;

end;

{ TVRMD3Loader }

Procedure TVRMD3Loader.CreateMeshes;

var

i : integer;

begin

SetLength( Models, Head.numMeshes );

for i := 0 to Head.numMeshes - 1 do

begin

Models[i] := TVRMD3MeshLoader.Create(addr(Meshes[i]));

end;

end;

destructor TVRMD3Loader.Destroy;

begin

Self.UnInit;

inherited;

end;

function TVRMD3Loader.GetMeshCount: integer;

begin

Result := Length( Self.Models );

end;

function TVRMD3Loader.GetTexture(const MeshIndex,

TextureIndex: integer): IVRTexture;

begin

if MeshIndex >= Length( Self.Meshes ) then exit;

Result := Meshes[MeshIndex].Texture;

end;

function TVRMD3Loader.Init(const FileName: String): Boolean;

begin

UnInit;

Result := false;

fillchar( Head,sizeof( TMd3Header ),#0 );

if LoadMD3( FileName ) = false then exit;

Result := True;

end;

function TVRMD3Loader.LoadMD3(const FileName: string): Boolean;

var

load : TVRFile;

Buf : Pointer;

MeshOffset : integer;

i : integer;

begin

Result := false;

load := TVRFile.Create;

try

if load.load(FileName) = false then exit;

Buf := @Head;

//获取文件头信息

if load.Read(Buf, Sizeof( TMD3Header )) = false then exit;

if Uppercase(Head.fileID) <> 'IDP3' then exit;

if Head.version <> 15 then exit;

//======================

//获得模型信息

SetLength( Bones, Head.numFrames );

Buf := Bones;

if load.Read(Buf,Head.numFrames * sizeof( TMD3Bone )) = false then exit;

SetLength( Tags,Head.numFrames * Head.numTags );

Buf := Tags;

if load.Read(Buf,Head.numFrames * Head.numTags * sizeof(TMD3Tag)) = false then exit;

SetLength(Links,Head.numTags);

//fillchar( Pchar( Links )^,Head.numTags * sizeof( PVRMD3Loader ),#0);

SetLength(Meshes, Head.numMeshes);

MeshOffset := Load.GetFilePos; //获得文件当前指针位置

for i:=0 to Head.numMeshes - 1 do

begin

IF Load.Seek(MeshOffSet,FILE_BEGIN) = FALSE then continue;

Buf := @Meshes[i].MeshHeader;

if Load.Read(Buf,sizeof( TMd3MeshInfo )) = false then continue;

//读取SKINS信息

SetLength( Meshes[i].Skins,Meshes[i].MeshHeader.numSkins );

if Load.Read(Pointer( Meshes[i].Skins ),68*Meshes[i].MeshHeader.numSkins) = false then continue;

// Messagebox( 0 ,Pchar(Meshes[i].Skins ),'',0 );

//==================

//三角形信息

if Load.Seek(MeshOffset + Meshes[I].MeshHeader.triStart,FILE_BEGIN) = false then continue;

SetLength( Meshes[i].Triangle, Meshes[I].MeshHeader.numTriangles);

if Load.Read(Pointer( Meshes[i].Triangle ), sizeOf(TMd3Triangle)*Meshes[I].MeshHeader.numTriangles) = false then continue;

//============================

//纹理坐标

if Load.Seek(MeshOffset + Meshes[I].MeshHeader.uvStart,FILE_BEGIN) = false then Continue;

SetLength(Meshes[I].TexCoord, Meshes[I].MeshHeader.numVertices);

if Load.Read(Pointer( Meshes[i].TexCoord ), sizeOf(TMd3TexCoord)*Meshes[I].MeshHeader.numVertices) = false then Continue;

//============================

//顶点

if Load.Seek(MeshOffset + Meshes[I].MeshHeader.vertexStart,FILE_BEGIN) = false then Continue;

SetLength( Meshes[i].Vertex,Meshes[I].MeshHeader.numVertices* Meshes[I].MeshHeader.numMeshFrames );

if Load.Read(Pointer( Meshes[i].Vertex ), sizeOf(TMD3Vertex)*Meshes[I].MeshHeader.numVertices* Meshes[I].MeshHeader.numMeshFrames) = false then continue;

//============================

MeshOffset :=MeshOffset + Meshes[I].MeshHeader.meshSize;

end;

//=============================

finally

load.Free; //关闭文件

end;

CreateMeshes();

Result := true;

end;

function TVRMD3Loader.LoadSkin(const Imagepath, filename: String): Boolean;

var F : TextFile;

I : Integer;

S : String;

MeshName, ImageName : String;

begin

Result := false;

if FileExists(Imagepath + filename) = false then exit;

AssignFile(F,Imagepath + filename);

Reset(F);

while EOF(F) = FALSE do

begin

Readln(F, S);

if Length(S) > 1 then

begin

if Pos(',', S)+1 < Length(S) then // there must be something after the comma

begin

MeshName :=Copy(S, 1, Pos(',', S)-1);

if Copy(MeshName, 1, 4) <> 'tag_' then // tags dont have skins

begin

ImageName :=Copy(S, Pos(',', S)+1, Length(S)); // get the full image and path name

ImageName :=StrRScan(PChar(S), '/'); // get name from last / (i.e only filename)

ImageName :=Copy(ImageName, 2, Length(ImageName)); // lose the starting /

// if its a TGA or JPG, then load the skin

if (pos('.JPG', UpperCase(ImageName)) > 0) OR (pos('.TGA', UpperCase(ImageName)) > 0) then

begin

// Find the right mesh item to assign the skin to

for I :=0 to head.numMeshes-1 do

begin

// check it the two names are the same

if UpperCase(CharArrToStr(meshes[i].MeshHeader.strName)) = Uppercase(meshname) then

begin

//LoadTexture(ImagePath + ImageName, meshes[i].Texture, FALSE);

SetLength( TextureList, Length(TextureList) + 1 );

TextureList[Length(TextureList) - 1] := TVRNormalTexture.Create;

if IVRNormalTexture(TextureList[Length(TextureList) - 1]).init( ImagePath + ImageName ) = false then Continue;

meshes[i].SetTexture :=TRUE;

meshes[i].Texture := TextureList[i];

end;

end;

end;

end;

end;

end;

end;

CloseFile(F);

Result := true;

end;

procedure TVRMD3Loader.Render;

var

i : integer;

begin

for i := 0 to Length( Models ) - 1 do

begin

if Models[i] <> nil then

Models[i].Render;

end;

end;

{procedure TVRMD3Loader.Render(const CurrentFrame, NextFrame: integer;

const pol: Real);

var

TriangleNum,currentMesh,currentOffsetVertex,nextCurrentOffsetVertex : integer;

k,i : integer;

begin

//暂时放在这里

For k :=0 to head.numMeshes-1 do

begin

currentMesh :=k;

currentOffsetVertex :=currentFrame * meshes[currentMesh].MeshHeader.numVertices;

nextCurrentOffsetVertex := NextFrame * meshes[currentMesh].MeshHeader.numVertices;

TriangleNum := Meshes[currentMesh].MeshHeader.numTriangles;

if meshes[k].SetTexture then

begin

meshes[k].Texture.PrePare(0);

for I :=0 to TriangleNum-1 do

end;

end;

end; }

procedure TVRMD3Loader.RotationXLocal(const Angle: Single);

var

i : integer;

begin

for i := 0 to Length( Models ) - 1 do

begin

if Models[i] <> nil then

Models[i].RotationXLocal(Angle);

end;

end;

procedure TVRMD3Loader.RotationYLocal(const Angle: single);

var

i : integer;

begin

for i := 0 to Length( Models ) - 1 do

begin

if Models[i] <> nil then

Models[i].RotationYLocal(Angle);

end;

end;

procedure TVRMD3Loader.RotationZLocal(const Angle: Single);

var

i : integer;

begin

for i := 0 to Length( Models ) - 1 do

begin

if Models[i] <> nil then

Models[i].RotationZLocal(Angle);

end;

end;

procedure TVRMD3Loader.SetTexture(const MeshIndex : integer;const TextureIndex : integer; const Texture : IVRTexture);

begin

if MeshIndex >= Length( Self.Meshes ) then exit;

if (Models[MeshIndex] = nil ) then exit;

Meshes[MeshIndex].Texture := nil;

Meshes[MeshIndex].Texture := Texture;

if Texture = nil then

Self.Meshes[MeshIndex].SetTexture := false

else

Self.Meshes[MeshIndex].SetTexture := true;

Models[MeshIndex].SetTexture(TextureIndex,Texture);

end;

procedure TVRMD3Loader.UnInit;

var

index : integer;

begin

for index := 0 to Length( TextureList ) - 1 do

begin

TextureList[index] := nil;

end;

SetLength(TextureList,0);

SetLength( Bones,0 );

SetLength( Tags,0 );

SetLength(Links,0);

for index := 0 to Length( Models ) - 1 do

begin

if Models[index] <> nil then

begin

Models[index].Free;

end;

end;

SetLength( Models,0 );

for index := 0 to Length( Meshes ) - 1 do

begin

SetLength( Meshes[index].Skins,0 );

SetLength( Meshes[index].Triangle, 0);

SetLength( Meshes[index].TexCoord, 0);

SetLength( Meshes[index].Vertex, 0 );

end;

for index := 0 to Length( Meshes ) - 1 do

begin

Meshes[index].Texture := nil;

end;

SetLength(Meshes,0);

end;

{ TVRMD3MeshLoader }

constructor TVRMD3MeshLoader.Create( const addr : PVRMD3Mesh );

var

Buf : Pchar;

i,j : integer;

Texture : IVRNormalTexture;

normU, normV : Integer;

begin

Self.Info := Addr;

D3DX9.D3DXMatrixIdentity(Matrix);

Buffer := nil;

Material := TVRMaterial.Create;

SwapAxis();

// Logfile.Write('Mesh='+Info.MeshHeader.strName+#13#10);

if Failed( DXDevice.CreateVertexBuffer( Info.MeshHeader.numVertices * sizeof(TMeshVertex),0,MD3FVF,D3DPOOL_DEFAULT,Buffer,nil) ) then exit;

if Failed ( DXDevice.CreateIndexBuffer(Sizeof(Word)*Info.MeshHeader.numTriangles*3,0,D3DFMT_INDEX16,D3DPOOL_DEFAULT,IndexBuf,nil) ) then exit;

if Failed( Buffer.Lock(0,0,Pointer( Buf ),0) ) then exit;

for i:=0 to Info.MeshHeader.numVertices - 1 do

begin

PMeshVertex(Buf + i*sizeof(TMeshVertex))^.Pos := Point3( Info.Vertex[i].Vertex[0]/64,Info.Vertex[i].Vertex[1]/64,Info.Vertex[i].Vertex[2]/64 );

PMeshVertex(Buf + i*sizeof(TMeshVertex))^.Tex := Point2( Info.TexCoord[i].textureCoord[0],1-Info.TexCoord[i].textureCoord[1]);

normU := Info.Vertex[i].Normal[0];

normV := Info.Vertex[i].Normal[1];

PMeshVertex(Buf + i*sizeof(TMeshVertex))^.Normal := Point3(aNorms[normU, normV, 0],aNorms[normU, normV, 1],aNorms[normU, normV, 2]);

end;

Buffer.Unlock;

if Failed( IndexBuf.Lock(0,0,Pointer( buf ),0)) then exit;

for i := 0 to Info.MeshHeader.numTriangles - 1 do

begin

for j:=0 to 2 do

begin

PWord(Buf + sizeof(Word)*(i*3+j))^:= Info.Triangle[i].Vertex[j];

end;

end;

IndexBuf.Unlock;

//清楚一些无用信息

SetLength( Info.Vertex, 0);

SetLength( Info.Triangle, 0);

SetLength( Info.TexCoord, 0);

//===========================

//加载Texture

Texture := TVRNormalTexture.Create;

try

if Texture.Init( Trim(Info.Skins[0] ) ) = false then

begin

Info.SetTexture := false;

Info.Texture := nil;

exit;

end;

Info.Texture := Texture;

Info.SetTexture := true;

finally

Texture := nil;

end;

SetLength( Info.Skins, 0);

end;

destructor TVRMD3MeshLoader.Destroy;

begin

// Model := nil;

IndexBuf := nil;

Buffer := nil;

Material := nil;

Inherited;

end;

procedure TVRMD3MeshLoader.PrePare;

begin

if Material <> nil then

Material.PrePare;

if Info.Texture <> nil then

Info.Texture.PrePare(0);

end;

procedure TVRMD3MeshLoader.Render;

var

Local,world : TD3DMatrix;

begin

// if Model = nil then exit;

// Model.Render;

if Buffer = nil then exit;

PrePare();

DXDevice.GetTransform(D3DTS_WORLD,world);

D3DX9.D3DXMatrixMultiply(Local,world,matrix);

DXDevice.SetTransform(D3DTS_WORLD,local);

DXDevice.SetFVF(MD3FVF);

DXDevice.SetStreamSource(0,Buffer,0,Sizeof(TMeshVertex));

DXDevice.SetIndices(IndexBuf);

DXDevice.DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,Info.MeshHeader.numVertices,0,Info.MeshHeader.numTriangles);

DXDevice.SetTransform(D3DTS_WORLD,world);

end;

procedure TVRMD3MeshLoader.RotationXLocal(const Angle: Single);

var

Local : TD3DMatrix;

begin

D3DX9.D3DXMatrixRotationX( Local,angle );

D3DX9.D3DXMatrixMultiply(Matrix,Local,Matrix);

end;

procedure TVRMD3MeshLoader.RotationYLocal(const Angle: single);

var

Local : TD3DMatrix;

begin

D3DX9.D3DXMatrixRotationY( Local,angle );

D3DX9.D3DXMatrixMultiply(Matrix,Local,Matrix);

end;

procedure TVRMD3MeshLoader.RotationZLocal(const Angle: Single);

var

Local : TD3DMatrix;

begin

D3DX9.D3DXMatrixRotationZ( Local,angle );

D3DX9.D3DXMatrixMultiply(Matrix,Local,Matrix);

end;

procedure TVRMD3MeshLoader.SetTexture(const Index: integer;

const Texture: IVRTexture);

begin

// if Model = nil then exit;

// Model.ModifyTexture(index,Texture);

Info.Texture := nil;

Info.Texture := Texture;

end;

procedure TVRMD3MeshLoader.SwapAxis;

var

i : integer;

temp : smallint;

begin

for i := 0 to Info.MeshHeader.numVertices - 1 do

begin

temp := Info.Vertex[i].Vertex[1];

Info.Vertex[i].Vertex[1] := Info.Vertex[i].Vertex[2];

Info.Vertex[i].Vertex[2] := - temp;

Info.TexCoord[i].textureCoord[1] := -Info.TexCoord[i].textureCoord[1];

end;

end;

initialization

InitNormals;

end.