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