其实,也就是读取文件罢了,一看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.