在以前版本上做了修改,完善了功能,修改了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.