在以前版本上做了修改,完善了功能,修改了BUG。
类代码如下:

unit VRMeshXLoader;

{ TODO : .X模型文件读取单元 }

interface

uses
  VRCommon,DXCommon,VRMaterial,VRTexture,VRModelObject,Direct3D9,D3DX9,Windows,SysUtils;

Type

  TVRXMeshLoader = class( TInterfacedObject,IVRModelXLoader )
  private
    FMesh : ID3DXMesh;
    SubmitNum : integer;
    Position : TPoint3;
    Matrix : TD3DXMatrix;
    IndexSize : integer;
    BoundingBox : IVRModelObject;//包围盒子对象
    BoundingBoxVisible : Boolean;
    MaterialList : Array Of IVRMaterial;
    TextureList : Array of IVRNormalTexture;
    function Init(const FaceNum : Cardinal;const VertexNum : Cardinal;const FVF : DWORD;const pVertexElements: PD3DVertexElement9 = nil) : Boolean;overload;      //传递顶点格式
    function LoadMaterials(const Mat : ID3DXBuffer;const Num : DWORD) : Boolean;
    procedure AddMaterial( const index : integer;const Material: IVRMaterial );
    function optimizeInPlace() : integer; ////经过优化后将自动建立属性表
     procedure AddTexture(const Index : integer;const Texture : IVRTexture);//不让用户调用,暂时不支持
     function _CreateBoundingBox() : IVRModelObject; //创建包围盒对象
  public
     function Init( const FileName : String ) : Boolean;overload; //读取 X 文件
     procedure UnInit();
     procedure Render();

     procedure SetBoundingBox( const Enable : Boolean );
     function LockVertexBuffer() : Pchar;
     procedure UnLockVertexBuffer();
     function LockIndexBuffer() : PWord;
     procedure UnLockIndexBuffer();
     function LockAttriBuffer() : PDWord;//attrib是相对于 面为最小单位的
     procedure UnLockAttriBuffer();
     function GetAttriTable(out table : TVRAttriTableArray) : Boolean;//获得属性表
     function SetAttribTable( const table : TVRAttriTableArray ) : Boolean;    //一般不使用
     function GetMaterial( const index : integer ) : IVRMaterial;
     Procedure RotationYLocal( const Angle : single );//绕本地 Y轴 旋转,角度为弧度制
     Procedure RotationXLocal( const Angle : Single );//绕本地 X轴 旋转,角度为弧度制
     Procedure RotationZLocal( const Angle : Single );//绕本地 Z轴 旋转,角度为弧度制
     Procedure RotationLocal( const XAngle,YAngle,ZAngle : Single);//绕本地旋转,角度为弧度制
     Procedure transfer(const Distance: TPoint3);//平移
     function _getD3DXMesh() : ID3DXMesh;
     function getMatrix() : TD3DMatrix;
     procedure SetMatrix( const Matrix : TD3DMatrix );
     function GetIndexSize : integer;
     procedure Scale(const value : TPoint3);//缩放比例系数
     procedure RotationAxisLocal( const Angle : Single;const Up : TPoint3 );
     function GetTexture( const index : integer ) : IVRTexture;
      procedure _setD3DXMesh(const Mesh : ID3DXMesh);
      function GetBoundingBox() : IVRModelObject;
      function GetSubitNum() : integer;
      procedure ModifyTexture( const index : integer; const Texture : IVRTexture );

     Constructor Create();
     Destructor Destroy();override;
  end;

implementation

{ TVRXMeshLoader }


procedure TVRXMeshLoader.AddMaterial(const index : integer;const Material: IVRMaterial);
begin
  if index < 0 then exit;
  if index >= Length( MaterialList ) then
  begin
    SetLength( MaterialList,index );
  end;
  MaterialList[Index] := Material;
end;

procedure TVRXMeshLoader.AddTexture(const Index: integer;
  const Texture: IVRTexture);
begin
  if index < 0 then exit;
  if Index >= Length(TextureList) then
  begin
    SetLength( TextureList, Index );
  end;
  TextureList[Index] := IVRNormalTexture( Texture ) ;
end;

constructor TVRXMeshLoader.Create;
begin
  UnInit;
  SetLength( MaterialList,0 );
  SetLength( TextureList, 0 );
end;

destructor TVRXMeshLoader.Destroy;
begin
  UnInit();
  SetLength( MaterialList,0 );
  SetLength( TextureList, 0 );
  inherited;
end;

function TVRXMeshLoader.GetAttriTable(
  out table: TVRAttriTableArray): Boolean;
var
  size : DWORD;
begin
  Result := false;
  size := 0;
  if not Assigned( FMesh ) then exit; //不存在
 if FMesh.GetAttributeTable(nil,@size) = D3DERR_INVALIDCALL then
   exit;
  if size = 0 then exit;//没有数据
  setLength( table,size );
  fillchar( table,size,#0);
 if FMesh.GetAttributeTable(PD3DXAttributerange(table),@size) = D3DERR_INVALIDCALL then
   exit;

  Result := true;
end;

function TVRXMeshLoader.GetBoundingBox: IVRModelObject;
begin
   Result := Self.BoundingBox;
end;

function TVRXMeshLoader.GetIndexSize: integer;
begin
  result := Self.IndexSize;
end;

function TVRXMeshLoader.GetMaterial(const index: integer): IVRMaterial;
begin
  Result := nil;
  if index < 0 then exit;
  if index >= Length( MaterialList ) then exit;
  result := MaterialList[index];
end;

function TVRXMeshLoader.getMatrix: TD3DMatrix;
begin
  Result := Self.Matrix;
end;

function TVRXMeshLoader.GetSubitNum: integer;
begin
  Result := Self.SubmitNum;
end;

function TVRXMeshLoader.GetTexture(const index: integer): IVRTexture;
begin
  result := nil;
  if TextureList = nil then exit;
  if Index >= Length( TextureList ) then exit;
  result := TextureList[index];
end;

function TVRXMeshLoader.Init(const FileName: String): Boolean;
var
  Materials : ID3DXBuffer;
  MaterialsNum : DWORD;
  buf : IDirect3DIndexBuffer9;
  Desc:TD3DIndexBufferDesc;
begin
  Result := false;
  SubmitNum := 0;
  IndexSize:= 0;
  D3DX9.D3DXMatrixIdentity(Matrix);
  Position := Point3(0,0,0);
  if FileExists( FileName ) = false then exit;//文件不存在
  Materials := nil;
  MaterialsNum := 0;
  if Failed( D3DXLoadMeshFromX( Pchar( FileName ),D3DXMESH_SYSTEMMEM,DXDevice,nil,@Materials,nil,@MaterialsNum,FMesh ) ) then exit;
  D3DX9.D3DXComputeNormals(FMesh,nil);
  if LoadMaterials(Materials,MaterialsNum) = false then exit;
  Materials :=nil;
  SubmitNum := MaterialsNum;
  Result := true;

  buf := nil;
  FMesh.GetIndexBuffer(buf);
  if buf = nil then exit;
  fillchar(Desc,sizeof(TD3DIndexBufferDesc),#0);
  buf.GetDesc(Desc);
  case Desc.Format of
    D3DFMT_INDEX16 : indexSize := 16;
    D3DFMT_INDEX32 : indexSize := 32;
  else
    indexSize := 0;
  end;

  //包围盒子
  Self.BoundingBoxVisible := false;
  Self.BoundingBox := nil;
  // Messagebox(0,pchar(inttostr(Self.FMesh.GetFVF)),'',0);
  //Messagebox( 0,pchar( inttostr( FMesh.GetNumBytesPerVertex ) +':'+ inttostr(FMesh.GetNumFaces)),'',0);
end;

function TVRXMeshLoader.Init(const FaceNum, VertexNum: Cardinal;
  const FVF: DWORD;const pVertexElements: PD3DVertexElement9): Boolean;
begin
  Result := false;
end;

function TVRXMeshLoader.LoadMaterials(const Mat : ID3DXBuffer;const Num : DWORD): Boolean;
var
  Material : PD3DXMaterial;
  Buf : Pchar;
  index : integer;
begin
  Result := false;

  if Num = 0 then //本来就没有材质,就用默认材质
  begin
    SetLength(  MaterialList,1 );
    MaterialList[0] := TVRMaterial.Create;
    Result := true;
    exit;
  end;

  if Mat = nil then exit;
  Buf := Mat.GetBufferPointer;
  if Buf = nil then  exit;

  SetLength( MaterialList,Num );
  SetLength( TextureList,Num );
  for index := 0 to Num - 1 do
  begin
    Material := PD3DXMaterial(Buf + sizeof(TD3DXMaterial)*index );
    //不用考虑Material不存在的情况

    MaterialList[index]:=TVRMaterial.Create(Material.MatD3D);
    //MaterialList[index].SetAmibent(Color4(177,177,177,255)); //测试的

    if not FileExists( Material.pTextureFilename ) then
    begin
      result := true;
      continue;
    end;

    TextureList[index] := TVRNormalTexture.Create;
   if TextureList[index].Init(Material.pTextureFilename) = false then exit;
    //暂时不考虑纹理信息
  end; 
  Result := true;
end;

function TVRXMeshLoader.LockAttriBuffer: PDWord;
begin
   //attrib是相对于 面为最小单位的
  Result := nil;
  if Assigned( FMesh ) then
  begin
    FMesh.LockAttributeBuffer(D3DLOCK_DISCARD,Result);
  end;
end;

function TVRXMeshLoader.LockIndexBuffer: PWord;
begin
     Result := nil;
   if Assigned( FMesh ) then
  begin
    FMesh.LockIndexBuffer(D3DLOCK_DISCARD,Pointer( Result ));
  end;
end;

function TVRXMeshLoader.LockVertexBuffer: Pchar;
begin
    Result := nil;
  if Assigned( FMesh ) then
  begin
     FMesh.LockVertexBuffer(D3DLOCK_DISCARD,Pointer( Result )) ;
  end;
end;

procedure TVRXMeshLoader.ModifyTexture(const index: integer;
  const Texture: IVRTexture);
begin
  if index >= Length( TextureList ) then exit;
  TextureList[index] := nil;
  TextureList[index] := IVRNormalTexture( Texture );
end;

function TVRXMeshLoader.optimizeInPlace : integer;
begin
  Result := 0;
end;

procedure TVRXMeshLoader.Render;
var
  index : integer;
  Indy,Local : TD3DMatrix;
begin
  if FMesh = nil then  exit;
  DXDevice.GetTransform(D3DTS_WORLD,Indy);
  D3DX9.D3DXMatrixMultiply(Local,Indy,Matrix);
  DXDevice.SetTransform(D3DTS_WORLD,Local);
  for index := 0 to SubmitNum - 1 do
  begin
    MaterialList[index].PrePare;
    if TextureList[index] <> nil then
      TextureList[index].PrePare(0);
  //  DXDevice.SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE );
    FMesh.DrawSubset(index);
  end;

  if Self.BoundingBoxVisible then
  begin
    if Self.BoundingBox <> nil then
    begin
      Self.BoundingBox.Render;
    end;
  end;

  DXDevice.SetTransform(D3DTS_WORLD,Indy);
end;

procedure TVRXMeshLoader.RotationAxisLocal(const Angle: Single;
  const Up: TPoint3);
var
  m : TD3DMatrix;
begin
   D3DX9.D3DXMatrixRotationAxis(m,VRCommon.Point3ToVector(Up),Angle);
   D3DX9.D3DXMatrixMultiply(Matrix,m,Matrix);
end;

procedure TVRXMeshLoader.RotationLocal(const XAngle, YAngle,
  ZAngle: Single);
begin
  Self.RotationYLocal(XAngle);
  Self.RotationYLocal(YAngle);
  Self.RotationZLocal(ZAngle);
end;

procedure TVRXMeshLoader.RotationXLocal(const Angle: Single);
var
  Local : TD3DMatrix;
begin
   D3DX9.D3DXMatrixRotationX( Local,angle );
   D3DX9.D3DXMatrixMultiply(Matrix,Local,Matrix);
end;

procedure TVRXMeshLoader.RotationYLocal(const Angle: single);
var
  Local : TD3DMatrix;
begin
   D3DX9.D3DXMatrixRotationY( Local,angle );
   D3DX9.D3DXMatrixMultiply(Matrix,Local,Matrix);
end;

procedure TVRXMeshLoader.RotationZLocal(const Angle: Single);
var
  Local : TD3DMatrix;
begin
   D3DX9.D3DXMatrixRotationZ( Local,angle );
   D3DX9.D3DXMatrixMultiply(Matrix,Local,Matrix);
end;

procedure TVRXMeshLoader.Scale(const value: TPoint3);
var
  scalar : TD3DMatrix;
begin
  D3DX9.D3DXMatrixScaling(scalar,value.x,value.y,value.z);
  D3DX9.D3DXMatrixMultiply(Matrix,scalar,Matrix);
end;

function TVRXMeshLoader.SetAttribTable(
  const table: TVRAttriTableArray): Boolean;
begin
  Result := false;
end;

procedure TVRXMeshLoader.SetBoundingBox(const Enable: Boolean);
begin
  if Self.BoundingBox = nil then
  begin
    Self.BoundingBox := Self._CreateBoundingBox;
  end;
  Self.BoundingBoxVisible := Enable;
end;


procedure TVRXMeshLoader.SetMatrix(const Matrix: TD3DMatrix);
begin
  Self.Matrix := Matrix;
end;

procedure TVRXMeshLoader.transfer(const Distance: TPoint3);
var
  local : TD3DMatrix;
begin
 // Local := D3DX9.D3DXMatrix(1,0,0,0,0,1,0,0,0,0,1,0,Distance.x,Distance.y,Distance.z,1);
  D3DX9.D3DXMatrixTranslation(Local,Distance.x,Distance.y,Distance.z);
  //D3DX9.D3DXMatrixTranslation(local,Distance.x,Distance.y,Distance.z);
  D3DX9.D3DXMatrixMultiply(Matrix,Matrix,Local);
  Position := Point3Add( Position, Distance ); 
end;

procedure TVRXMeshLoader.UnInit;
var
 index : integer;
begin

 for index := 0 to Length(MaterialList) - 1 do
 begin
   MaterialList[index] := nil;
 end;
 Self.BoundingBoxVisible := false;
 Self.BoundingBox := nil;
  FMesh := nil;
end;

procedure TVRXMeshLoader.UnLockAttriBuffer;
begin
    if Assigned( FMesh ) then
  begin
    FMesh.UnlockAttributeBuffer;
  end;
end;

procedure TVRXMeshLoader.UnLockIndexBuffer;
begin
    if Assigned( FMesh ) then
  begin
    FMesh.UnlockIndexBuffer;
  end;
end;

procedure TVRXMeshLoader.UnLockVertexBuffer;
begin
    if Assigned( FMesh )then
  begin
    FMesh.UnlockVertexBuffer;
  end;
end;

function TVRXMeshLoader._CreateBoundingBox: IVRModelObject;
var
  Data : Pointer;
  Min,Max : TD3DVector;
  Mesh : ID3DXMesh;
begin
  Result := nil;
  if Failed(FMesh.LockVertexBuffer(D3DLOCK_READONLY,Data)) then exit;
  try
    if Failed(D3DX9.D3DXComputeBoundingBox( PD3DXVector3( Data ), FMesh.GetNumVertices, FMesh.GetNumBytesPerVertex, Min, Max)) then exit;
    if Failed( D3DXCreateBox( DXDevice, Max.x - Min.x, Max.y - Min.y, Max.z - Min.z,Mesh,nil) ) then exit;
    Result := TVRModelObject.Create;
    Result._setD3DXMesh(Mesh);
  Finally
    Mesh := nil;
    FMesh.UnlockVertexBuffer;
  end;
end;

function TVRXMeshLoader._getD3DXMesh: ID3DXMesh;
begin
   result := Self.FMesh;
end;

procedure TVRXMeshLoader._setD3DXMesh(const Mesh: ID3DXMesh);
begin
    Self.FMesh := Mesh;
    Self.optimizeInPlace;
end;

end.