123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.FileMD5;
- (* Doom3 MD5 mesh and animation vector file format implementation *)
- interface
- uses
- System.Classes,
- System.SysUtils,
- GXS.VectorFileObjects,
- Stage.Utils,
- GXS.ApplicationFileIO,
- Stage.VectorTypes,
- Stage.VectorGeometry,
- GXS.VectorLists;
- type
- TgxMD5VectorFile = class(TgxVectorFile)
- private
- FMD5String, FTempString, FBoneNames: TStringList;
- FCurrentPos: Integer;
- FBasePose: TgxSkeletonFrame;
- FFramePositions: TgxAffineVectorList;
- FFrameQuaternions: TgxQuaternionList;
- FJointFlags: TgxIntegerList;
- FNumFrames, FFirstFrame, FFrameRate, FNumJoints: Integer;
- function ReadLine: String;
- public
- class function Capabilities: TDataFileCapabilities; override;
- procedure LoadFromStream(aStream: TStream); override;
- end;
- var
- vMD5TextureExtensions: TStringList;
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- // -----------
- // ----------- TgxMD5VectorFile -----------
- // -----------
- function TgxMD5VectorFile.ReadLine: String;
- begin
- Result := '';
- if not Assigned(FMD5String) then
- exit;
- if FCurrentPos >= FMD5String.Count then
- exit;
- repeat
- Result := FMD5String[FCurrentPos];
- Result := StringReplace(Result, '(', '', [rfReplaceAll]);
- Result := StringReplace(Result, ')', '', [rfReplaceAll]);
- Result := Trim(Result);
- Inc(FCurrentPos);
- until (Result <> '') or (FCurrentPos >= FMD5String.Count);
- end;
- class function TgxMD5VectorFile.Capabilities: TDataFileCapabilities;
- begin
- Result := [dfcRead];
- end;
- procedure TgxMD5VectorFile.LoadFromStream(aStream: TStream);
- procedure AllocateMaterial(var shader: String);
- { const
- cTexType : array[0..2] of String = ('_local', '_d', '_s');
- var
- shader_nopath, temp : String;
- libmat : TgxLibMaterial;
- i, j : Integer;// }
- begin
- { if Assigned(Owner.MaterialLibrary) then begin
- shader:=StringReplace(shader,'/','\',[rfReplaceAll]);
- if not DirectoryExists(ExtractFilePath(shader)) then
- shader:=ExtractFileName(shader);
- if not Assigned(Owner.MaterialLibrary.Materials.GetLibMaterialByName(shader)) then begin
- libmat:=Owner.MaterialLibrary.Materials.Add;
- libmat.Name:=shader;
- for i:=0 to High(cTexType) do begin
- temp:=ChangeFileExt(shader, '')+cTexType[i];
- for j:=0 to vMD5TextureExtensions.Count-1 do begin
- if FileExists(temp+vMD5TextureExtensions[j]) then begin
- with libmat.Material.TextureEx.Add do begin
- Texture.Image.LoadFromFile(temp+vMD5TextureExtensions[j]);
- Texture.Enabled:=True;
- end;
- Break;
- end;
- end;
- end;
- end;
- end else// }
- shader := '';
- end;
- function QuaternionMakeFromImag(ix, iy, iz: Single): TQuaternion;
- var
- rr: Single;
- begin
- with Result do
- begin
- ImagPart.X := ix;
- ImagPart.Y := iy;
- ImagPart.Z := iz;
- rr := 1 - (ix * ix) - (iy * iy) - (iz * iz);
- if rr < 0 then
- RealPart := 0
- else
- RealPart := sqrt(rr);
- end;
- end;
- procedure ReadBone(BoneNum: Integer; BoneString: String);
- var
- bonename: String;
- pos: TAffineVector;
- quat: TQuaternion;
- mat, rmat: TMatrix4f;
- ParentBoneID: Integer;
- bone, parentbone: TgxSkeletonBone;
- begin
- FTempString.CommaText := BoneString;
- bonename := FTempString[0];
- ParentBoneID := StrToInt(FTempString[1]);
- pos.X := StrToFloatDef(FTempString[2]);
- pos.Y := StrToFloatDef(FTempString[4]);
- pos.Z := StrToFloatDef(FTempString[3]);
- quat := QuaternionMakeFromImag(StrToFloatDef(FTempString[5]),
- StrToFloatDef(FTempString[7]),
- StrToFloatDef(FTempString[6]));
- FFramePositions.Add(pos);
- FFrameQuaternions.Add(quat);
- if bonename <> '' then
- begin
- FBoneNames.Add(bonename);
- if ParentBoneID = -1 then
- bone := TgxSkeletonBone.CreateOwned(Owner.Skeleton.RootBones)
- else
- begin
- parentbone := Owner.Skeleton.RootBones.BoneByID(ParentBoneID);
- bone := TgxSkeletonBone.CreateOwned(parentbone);
- mat := QuaternionToMatrix(quat);
- mat.W := PointMake(pos);
- rmat := QuaternionToMatrix(FFrameQuaternions[ParentBoneID]);
- rmat.W := PointMake(FFramePositions[ParentBoneID]);
- InvertMatrix(rmat);
- mat := MatrixMultiply(mat, rmat);
- pos := AffineVectorMake(mat.W);
- quat := QuaternionFromMatrix(mat);
- end;
- with bone do
- begin
- BoneID := BoneNum;
- Name := bonename;
- end;
- end;
- FBasePose.Position[BoneNum] := pos;
- FBasePose.Quaternion[BoneNum] := quat;
- end;
- procedure ReadJoints;
- var
- temp: String;
- i: Integer;
- begin
- i := 0;
- repeat
- temp := ReadLine;
- if temp <> '}' then
- begin
- ReadBone(i, temp);
- Inc(i);
- end;
- until temp = '}';
- Owner.Skeleton.CurrentFrame.Assign(FBasePose);
- Owner.Skeleton.CurrentFrame.FlushLocalMatrixList;
- Owner.Skeleton.RootBones.PrepareGlobalMatrices;
- end;
- procedure ReadMesh;
- var
- temp, shader: String;
- mesh: TgxSkeletonMeshObject;
- fg: TgxFGVertexIndexList;
- vnum, wnum, numverts, numweights: Integer;
- VertexWeightID, VertexWeightCount, VertexBoneRef: TgxIntegerList;
- VertexWeight: TgxSingleList;
- VertexWeighted: TgxAffineVectorList;
- blendedVert, transformedVert: TAffineVector;
- i, j, k: Integer;
- mat: TMatrix4f;
- begin
- VertexWeightID := TgxIntegerList.Create;
- VertexWeightCount := TgxIntegerList.Create;
- VertexBoneRef := TgxIntegerList.Create;
- VertexWeight := TgxSingleList.Create;
- VertexWeighted := TgxAffineVectorList.Create;
- numverts := 0;
- mesh := TgxSkeletonMeshObject.CreateOwned(Owner.MeshObjects);
- fg := TgxFGVertexIndexList.CreateOwned(mesh.FaceGroups);
- mesh.Mode := momFaceGroups;
- fg.Mode := fgmmTriangles;
- repeat
- temp := ReadLine;
- FTempString.CommaText := temp;
- if FTempString.Count > 1 then
- begin
- temp := LowerCase(FTempString[0]);
- if temp = 'shader' then
- begin
- shader := FTempString[1];
- AllocateMaterial(shader);
- fg.MaterialName := shader;
- end
- else if temp = 'numverts' then
- begin
- numverts := StrToInt(FTempString[1]);
- mesh.TexCoords.Count := numverts;
- VertexWeightID.Count := numverts;
- VertexWeightCount.Count := numverts;
- end
- else if temp = 'vert' then
- begin
- if FTempString.Count >= 6 then
- begin
- vnum := StrToInt(FTempString[1]);
- mesh.TexCoords[vnum] :=
- AffineVectorMake(StrToFloatDef(FTempString[2]),
- 1 - StrToFloatDef(FTempString[3]), 0);
- VertexWeightID[vnum] := StrToInt(FTempString[4]);
- VertexWeightCount[vnum] := StrToInt(FTempString[5]);
- if VertexWeightCount[vnum] > mesh.BonesPerVertex then
- mesh.BonesPerVertex := VertexWeightCount[vnum];
- end;
- end
- else if temp = 'numtris' then
- begin
- fg.VertexIndices.Capacity := StrToInt(FTempString[1]) * 3;
- end
- else if temp = 'tri' then
- begin
- if FTempString.Count >= 5 then
- begin
- fg.VertexIndices.Add(StrToInt(FTempString[2]));
- fg.VertexIndices.Add(StrToInt(FTempString[3]));
- fg.VertexIndices.Add(StrToInt(FTempString[4]));
- end;
- end
- else if temp = 'numweights' then
- begin
- numweights := StrToInt(FTempString[1]);
- VertexBoneRef.Count := numweights;
- VertexWeight.Count := numweights;
- VertexWeighted.Count := numweights;
- end
- else if temp = 'weight' then
- begin
- if FTempString.Count >= 7 then
- begin
- wnum := StrToInt(FTempString[1]);
- VertexBoneRef[wnum] := StrToInt(FTempString[2]);
- VertexWeight[wnum] := StrToFloatDef(FTempString[3]);
- VertexWeighted[wnum] :=
- AffineVectorMake(StrToFloatDef(FTempString[4]),
- StrToFloatDef(FTempString[6]),
- StrToFloatDef(FTempString[5]));
- end;
- end;
- end;
- until temp = '}';
- mesh.Vertices.Count := numverts;
- mesh.VerticeBoneWeightCount := numverts;
- for i := 0 to numverts - 1 do
- begin
- blendedVert := NullVector;
- for j := 0 to mesh.BonesPerVertex - 1 do
- begin
- if j < VertexWeightCount[i] then
- begin
- k := VertexWeightID[i] + j;
- mesh.VerticesBonesWeights^[i]^[j].BoneID := VertexBoneRef[k];
- mesh.VerticesBonesWeights^[i]^[j].Weight := VertexWeight[k];
- mat := Owner.Skeleton.RootBones.BoneByID(VertexBoneRef[k])
- .GlobalMatrix;
- transformedVert := VectorTransform(VertexWeighted[k], mat);
- AddVector(blendedVert, VectorScale(transformedVert, VertexWeight[k]));
- end
- else
- begin
- mesh.VerticesBonesWeights^[i]^[j].BoneID := 0;
- mesh.VerticesBonesWeights^[i]^[j].Weight := 0;
- end;
- end;
- mesh.Vertices[i] := blendedVert;
- end;
- mesh.BuildNormals(fg.VertexIndices, momTriangles);
- VertexWeightID.Free;
- VertexWeightCount.Free;
- VertexBoneRef.Free;
- VertexWeight.Free;
- VertexWeighted.Free;
- end;
- procedure ReadHierarchy;
- var
- temp: String;
- bone: TgxSkeletonBone;
- begin
- if not Assigned(FJointFlags) then
- begin
- FJointFlags := TgxIntegerList.Create;
- Assert(Owner.Skeleton.Frames.Count > 0,
- 'The md5mesh file must be loaded before md5anim files!');
- FJointFlags.Count := Owner.Skeleton.Frames[0].Position.Count;
- end;
- repeat
- temp := ReadLine;
- FTempString.CommaText := temp;
- if FTempString.Count >= 3 then
- begin
- bone := Owner.Skeleton.BoneByName(FTempString[0]);
- if Assigned(bone) then
- FJointFlags[bone.BoneID] := StrToInt(FTempString[2]);
- end;
- until temp = '}';
- end;
- procedure ReadBaseFrame;
- var
- temp: String;
- pos: TAffineVector;
- quat: TQuaternion;
- begin
- FFramePositions.Clear;
- FFrameQuaternions.Clear;
- repeat
- temp := ReadLine;
- FTempString.CommaText := temp;
- if FTempString.Count >= 6 then
- begin
- pos := AffineVectorMake(StrToFloatDef(FTempString[0]),
- StrToFloatDef(FTempString[1]),
- StrToFloatDef(FTempString[2]));
- quat := QuaternionMakeFromImag(StrToFloatDef(FTempString[3]),
- StrToFloatDef(FTempString[4]),
- StrToFloatDef(FTempString[5]));
- FFramePositions.Add(pos);
- FFrameQuaternions.Add(quat);
- end;
- until temp = '}';
- end;
- procedure ReadFrame(framenum: Integer);
- var
- temp: String;
- i, j: Integer;
- frame: TgxSkeletonFrame;
- pos: TAffineVector;
- quat: TQuaternion;
- begin
- frame := Owner.Skeleton.Frames[FFirstFrame + framenum];
- frame.TransformMode := sftQuaternion;
- frame.Position.Count := FNumJoints;
- frame.Quaternion.Count := FNumJoints;
- for i := 0 to FJointFlags.Count - 1 do
- begin
- pos := FFramePositions[i];
- quat := FFrameQuaternions[i];
- if FJointFlags[i] > 0 then
- begin
- temp := ReadLine;
- FTempString.CommaText := temp;
- j := 0;
- if FJointFlags[i] and 1 > 0 then
- begin
- pos.X := StrToFloatDef(FTempString[j]);
- Inc(j);
- end;
- if FJointFlags[i] and 2 > 0 then
- begin
- pos.Y := StrToFloatDef(FTempString[j]);
- Inc(j);
- end;
- if FJointFlags[i] and 4 > 0 then
- begin
- pos.Z := StrToFloatDef(FTempString[j]);
- Inc(j);
- end;
- if FJointFlags[i] and 8 > 0 then
- begin
- quat.ImagPart.X := StrToFloatDef(FTempString[j]);
- Inc(j);
- end;
- if FJointFlags[i] and 16 > 0 then
- begin
- quat.ImagPart.Y := StrToFloatDef(FTempString[j]);
- Inc(j);
- end;
- if FJointFlags[i] and 32 > 0 then
- quat.ImagPart.Z := StrToFloatDef(FTempString[j]);
- end;
- pos := AffineVectorMake(pos.X, pos.Z, pos.Y);
- quat := QuaternionMakeFromImag(quat.ImagPart.X, quat.ImagPart.Z,
- quat.ImagPart.Y);
- frame.Position[i] := pos;
- frame.Quaternion[i] := quat;
- end;
- end;
- procedure InitializeMeshes;
- var
- i: Integer;
- begin
- for i := 0 to Owner.MeshObjects.Count - 1 do
- TgxSkeletonMeshObject(Owner.MeshObjects[i])
- .PrepareBoneMatrixInvertedMeshes;
- end;
- var
- str, temp: String;
- nummeshes, md5Version, meshid, i: Integer;
- begin
- FCurrentPos := 0;
- FMD5String := TStringList.Create;
- FTempString := TStringList.Create;
- FBoneNames := TStringList.Create;
- meshid := 0;
- nummeshes := 0;
- md5Version := 0;
- try
- FMD5String.LoadFromStream(aStream);
- // Version checking
- str := ReadLine;
- FTempString.CommaText := str;
- if FTempString.Count >= 2 then
- if LowerCase(FTempString[0]) = 'md5version' then
- md5Version := StrToInt(FTempString[1]);
- Assert(md5Version = 10, 'Invalid or missing md5Version number.');
- repeat
- str := ReadLine;
- FTempString.CommaText := str;
- if FTempString.Count > 1 then
- begin
- temp := LowerCase(FTempString[0]);
- if (temp = 'numjoints') then
- begin
- FNumJoints := StrToInt(FTempString[1]);
- FFramePositions := TgxAffineVectorList.Create;
- FFrameQuaternions := TgxQuaternionList.Create;
- if Owner.Skeleton.Frames.Count = 0 then
- begin
- FBasePose := TgxSkeletonFrame.CreateOwned(Owner.Skeleton.Frames);
- FBasePose.Position.Count := FNumJoints;
- FBasePose.TransformMode := sftQuaternion;
- FBasePose.Quaternion.Count := FNumJoints;
- end
- else
- FBasePose := Owner.Skeleton.Frames[0];
- end
- else if (temp = 'joints') then
- begin
- ReadJoints;
- if Owner is TgxActor then
- TgxActor(Owner).Reference := aarSkeleton;
- end
- else if (temp = 'nummeshes') then
- begin
- nummeshes := StrToInt(FTempString[1]);
- end
- else if (temp = 'mesh') then
- begin
- if meshid < nummeshes then
- begin
- ReadMesh;
- if meshid = nummeshes - 1 then
- InitializeMeshes;
- Inc(meshid);
- end
- else
- begin
- repeat
- str := ReadLine;
- until str = '}';
- end;
- end
- else if (temp = 'hierarchy') then
- begin
- ReadHierarchy;
- end
- else if (temp = 'numframes') then
- begin
- FNumFrames := StrToInt(FTempString[1]);
- if FNumFrames > 0 then
- begin
- FFirstFrame := Owner.Skeleton.Frames.Count;
- for i := 1 to FNumFrames do
- TgxSkeletonFrame.CreateOwned(Owner.Skeleton.Frames);
- if Owner is TgxActor then
- begin
- with TgxActor(Owner).Animations.Add do
- begin
- Name := ChangeFileExt(ExtractFileName(ResourceName), '');
- Reference := aarSkeleton;
- StartFrame := FFirstFrame;
- EndFrame := FFirstFrame + FNumFrames - 1;
- end;
- end;
- end;
- end
- else if (temp = 'framerate') then
- begin
- FFrameRate := StrToInt(FTempString[1]);
- end
- else if (temp = 'baseframe') then
- begin
- ReadBaseFrame;
- end
- else if (temp = 'frame') then
- begin
- ReadFrame(StrToInt(FTempString[1]));
- end;
- end;
- until str = '';
- finally
- if Assigned(FFramePositions) then
- FreeAndNil(FFramePositions);
- if Assigned(FFrameQuaternions) then
- FreeAndNil(FFrameQuaternions);
- if Assigned(FJointFlags) then
- FreeAndNil(FJointFlags);
- FBoneNames.Free;
- FTempString.Free;
- FMD5String.Free;
- end;
- end;
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- RegisterVectorFileFormat('md5mesh', 'Doom3 mesh files', TgxMD5VectorFile);
- RegisterVectorFileFormat('md5anim', 'Doom3 animation files', TgxMD5VectorFile);
- vMD5TextureExtensions := TStringList.Create;
- with vMD5TextureExtensions do
- begin
- Add('.bmp');
- Add('.dds');
- Add('.jpg');
- Add('.tga');
- end;
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- finalization
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- // ------------------------------------------------------------------
- vMD5TextureExtensions.Free;
- end.
|