2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Ghoul2 (GLM/GLA) file format loading structures
8
Also referred to as MDX (MDXM/MDXA) format in C source.
11
09/05/03 - SG - Fixed compressed bone pool
12
01/04/03 - SG - Finished GLM/GLA loading procedures
13
25/03/03 - SG - Creation
21
Classes, GLVectorTypes, SysUtils, GLVectorGeometry;
25
fileID : array[0..3] of char;
28
animName : array[0..63] of char;
38
TGLMSurfaceHeirachyOffsets = array of integer;
40
TGLMSurfaceHeirachy = record
41
name : array[0..63] of Char;
43
shader : array[0..63] of Char;
46
numChildren : integer;
47
childIndices : array of integer;
50
TGLMSurfaceHeader = record
64
indices : array[0..2] of integer;
70
uiNumWeightsAndBoneIndices : Cardinal; // packed int
71
BoneWeightings : array[0..3] of Byte;
75
SurfaceHeader : TGLMSurfaceHeader;
76
Triangles : array of TGLMTriangle;
77
Vertices : array of TGLMVertex;
78
TexCoords : array of TVector2f;
79
BoneReferences : array of Integer;
86
TGLMLODSurfaceOffsets = array of integer;
89
LODInfo : TGLMLODInfo;
90
LODSurfaceOffsets : TGLMLODSurfaceOffsets;
91
Surfaces : array of TGLMSurface;
95
fileID : array[0..3] of char;
97
Name : array[0..63] of char;
107
TGLABone = array[0..2] of TVector4f;
108
TGLACompQuatBone = array[0..6] of Word; {14 bytes}
110
TGLASkeletonOffsets = array of integer;
112
TGLASkeleton = record
113
name : array[0..63] of char;
117
BasePoseMatInv : TGLABone;
118
numChildren : Integer;
119
children : array of Integer;
122
// Ghoul2 Model structure
125
ModelHeader : TGLMHeader;
126
SurfaceHeirachyOffsets : TGLMSurfaceHeirachyOffsets;
127
SurfaceHeirachy : array of TGLMSurfaceHeirachy;
128
LODs : array of TGLMLODs;
129
procedure LoadFromStream(aStream : TStream);
132
// Ghoul2 Animation structure
135
AnimHeader : TGLAHeader;
136
SkeletonOffsets : TGLASkeletonOffsets;
137
Skeleton : array of TGLASkeleton;
138
BoneIndices : array of Integer;
139
CompBonePool : array of TGLACompQuatBone;
140
function GetCompressedMatrix(Frame,Bone : Integer):TGLACompQuatBone;
141
function GetUnCompressedMatrix(Frame,Bone : Integer):TMatrix;
142
procedure LoadFromStream(aStream : TStream);
145
function G2_GetVertWeights(vert:TGLMVertex):Integer;
146
function G2_GetVertBoneIndex(vert:TGLMVertex; iWeightNum:Integer):Integer;
147
function G2_GetVertBoneWeight(vert:TGLMVertex; iWeightNum:Cardinal;
148
var fTotalWeight:Single; const iNumWeights:Cardinal):single;
150
procedure MC_UnCompressQuat(var mat : TMatrix; const comp : TGLACompQuatBone);
152
// ------------------------------------------------------------------
153
// ------------------------------------------------------------------
154
// ------------------------------------------------------------------
156
// ------------------------------------------------------------------
157
// ------------------------------------------------------------------
158
// ------------------------------------------------------------------
161
// ------------------ Misc routines ------------------
164
// Adapted from mdx_format.h
165
// static inline int G2_GetVertWeights( const mdxmVertex_t *pVert )
166
// static inline int G2_GetVertBoneIndex( const mdxmVertex_t *pVert, const int iWeightNum)
167
// static inline float G2_GetVertBoneWeight( const mdxmVertex_t *pVert, const int iWeightNum, float &fTotalWeight, int iNumWeights )
169
function G2_GetVertWeights(vert:TGLMVertex):Integer;
171
// Get number of bones per vertex (0..3)+1 = (1..4)
172
result:=(vert.uiNumWeightsAndBoneIndices shr 30)+1;
175
function G2_GetVertBoneIndex(vert:TGLMVertex; iWeightNum:Integer):Integer;
177
// Extract the bone reference array index, a 5-bit integer
178
result:=(vert.uiNumWeightsAndBoneIndices shr (5*iWeightNum)) and 31;
181
function G2_GetVertBoneWeight(vert:TGLMVertex; iWeightNum:Cardinal;
182
var fTotalWeight:Single; const iNumWeights:Cardinal):single;
184
fBoneWeight : Single;
187
if (iWeightNum = iNumWeights-1) then begin
188
// No need to calculate final weight value, return the
189
// weight left over out of 1
190
fBoneWeight:=1-fTotalWeight;
192
// Get the initial 8-bit bone weight
193
iTemp:=vert.BoneWeightings[iWeightNum];
194
// Get the 2-bit overflow and 'or' it to the front of the
195
// weight to get 10-bit integer weight (0..1023)
196
iTemp:=iTemp or ((vert.uiNumWeightsAndBoneIndices shr (12+(iWeightNum*2))) and $300);
197
// Convert to floating point weight (0..1)
198
fBoneWeight:=iTemp/1023;
199
// Accumulate total weight
200
fTotalWeight:=fTotalWeight+fBoneWeight;
205
// Adapted from matcomp.c
206
// void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp)
208
procedure MC_UnCompressQuat(var mat : TMatrix; const comp : TGLACompQuatBone);
210
mat:=QuaternionToMatrix(QuaternionMake([comp[1]-32726,comp[2]-32726,comp[3]-32726],comp[0]-32726));
211
mat.V[3]:=VectorMake(comp[4]/64-512,comp[5]/64-512,comp[6]/64-512,1);
216
// ------------------ TFileGLM ------------------
221
procedure TFileGLM.LoadFromStream(aStream: TStream);
223
idstr : array[0..3] of char;
228
aStream.Read(idstr,sizeof(idstr));
231
if not (idstr='2LGM') then begin
232
raise Exception.Create(Format('Unknown or incorrect identity tag: [%s]',[idstr]));
236
aStream.Read(ModelHeader,SizeOf(ModelHeader));
238
if ModelHeader.version<>6 then
239
raise Exception.Create(Format('Only GLM (MDXM) version 6 is supported. File is version %d.',[ModelHeader.version]));
241
SetLength(SurfaceHeirachyOffsets,ModelHeader.numSurfaces);
242
aStream.Read(SurfaceHeirachyOffsets[0],sizeof(Integer)*ModelHeader.numSurfaces);
244
SetLength(SurfaceHeirachy,ModelHeader.numSurfaces);
245
for i:=0 to ModelHeader.numSurfaces-1 do
246
with SurfaceHeirachy[i] do begin
247
aStream.Read(name,Length(name));
248
aStream.Read(flags,sizeof(LongWord));
249
aStream.Read(shader,Length(shader));
250
aStream.Read(shaderindex,sizeof(Integer));
251
aStream.Read(parentindex,sizeof(Integer));
252
aStream.Read(numChildren,sizeof(Integer));
253
if numChildren>0 then begin
254
SetLength(childIndices,numChildren);
255
aStream.Read(childIndices[0],numChildren*sizeof(Integer));
256
end else SetLength(childIndices,0);
259
SetLength(LODs,ModelHeader.numLODs);
260
for i:=0 to ModelHeader.numLODs-1 do
261
with LODs[i] do begin
262
LODofs:=aStream.Position;
263
aStream.Read(LODInfo,sizeof(LODInfo));
264
SetLength(LODSurfaceOffsets,ModelHeader.numSurfaces);
265
aStream.Read(LODSurfaceOffsets[0],sizeof(integer)*ModelHeader.numSurfaces);
266
SetLength(Surfaces,ModelHeader.numSurfaces);
267
for j:=0 to ModelHeader.numSurfaces-1 do
268
with Surfaces[j] do begin
269
ofs:=aStream.Position;
270
aStream.Read(SurfaceHeader,SizeOf(TGLMSurfaceHeader));
271
SetLength(Triangles,SurfaceHeader.numTriangles);
272
SetLength(Vertices,SurfaceHeader.numVerts);
273
SetLength(TexCoords,SurfaceHeader.numVerts);
274
SetLength(BoneReferences,SurfaceHeader.numBoneReferences);
275
aStream.Position:=ofs+SurfaceHeader.ofsTriangles;
276
aStream.Read(Triangles[0],SurfaceHeader.numTriangles*SizeOf(TGLMTriangle));
277
aStream.Position:=ofs+SurfaceHeader.ofsVerts;
278
aStream.Read(Vertices[0],SurfaceHeader.numVerts*SizeOf(TGLMVertex));
279
aStream.Read(TexCoords[0],SurfaceHeader.numVerts*SizeOf(TVector2f));
280
aStream.Position:=ofs+SurfaceHeader.ofsBoneReferences;
281
aStream.Read(BoneReferences[0],SurfaceHeader.numBoneReferences*SizeOf(Integer));
282
aStream.Position:=ofs+SurfaceHeader.ofsEnd;
284
aStream.Position:=LODofs+LODInfo.ofsEnd;
290
// ------------------ TFileGLA ------------------
293
// GetCompressedMatrix
295
function TFileGLA.GetCompressedMatrix(Frame, Bone: Integer): TGLACompQuatBone;
297
Result:=CompBonePool[BoneIndices[Frame*AnimHeader.numBones+Bone]];
300
// GetUnCompressedMatrix
302
function TFileGLA.GetUnCompressedMatrix(Frame, Bone: Integer): TMatrix;
304
MC_UnCompressQuat(Result,CompBonePool[BoneIndices[Frame*AnimHeader.numBones+Bone]]);
309
procedure TFileGLA.LoadFromStream(aStream: TStream);
311
idstr : array[0..3] of char;
313
buf : array of array[0..2] of Byte;
315
aStream.Read(idstr,sizeof(idstr));
318
if not (idstr='2LGA') then begin
319
raise Exception.Create(Format('Unknown or incorrect identity tag: [%s]',[idstr]));
323
aStream.Read(AnimHeader,SizeOf(AnimHeader));
325
if AnimHeader.version<>6 then
326
raise Exception.Create(Format('Only GLA (MDXA) version 6 is supported. File is version %d.',[AnimHeader.version]));
328
SetLength(SkeletonOffsets,AnimHeader.numBones);
329
aStream.Read(SkeletonOffsets[0],sizeof(Integer)*AnimHeader.numBones);
331
SetLength(Skeleton,AnimHeader.numBones);
332
for i:=0 to AnimHeader.numBones-1 do
333
with Skeleton[i] do begin
334
aStream.Read(name,Length(name));
335
aStream.Read(flags,sizeof(LongWord));
336
aStream.Read(Parent,SizeOf(Integer));
337
aStream.Read(basePoseMat,sizeof(TGLABone));
338
aStream.Read(basePoseMatInv,sizeof(TGLABone));
339
aStream.Read(numChildren,sizeof(Integer));
340
if numChildren>0 then begin
341
SetLength(children,numChildren);
342
aStream.Read(children[0],numChildren*sizeof(Integer));
343
end else SetLength(children,0);
346
aStream.Position:=AnimHeader.ofsFrames;
347
SetLength(BoneIndices,AnimHeader.numFrames*AnimHeader.numBones);
348
SetLength(buf,AnimHeader.numFrames*AnimHeader.numBones*3);
349
aStream.Read(buf[0],AnimHeader.numFrames*AnimHeader.numBones*3);
350
for i:=0 to AnimHeader.numFrames*AnimHeader.numBones-1 do
351
BoneIndices[i]:=(buf[i][2] shl 16) or (buf[i][1] shl 8) or buf[i][0];
354
aStream.Position:=AnimHeader.ofsCompBonePool;
355
temp:=AnimHeader.ofsEnd-AnimHeader.ofsCompBonePool;
356
SetLength(CompBonePool,temp div SizeOf(TGLACompQuatBone));
357
aStream.Read(CompBonePool[0],temp);