2
// This unit is part of the GLScene Engine https://github.com/glscene
6
31/05/10 - Yar - Fixes for Linux x64
7
22/01/10 - Yar - Added GLTextureFormat to uses
8
25/07/07 - DaStr - Replaced some types to get rid of compiler warnings
9
08/10/08 - DanB - fix for different Char size in Delphi 2009+
10
22/06/08 - DaStr - Fixups after converting TGLMeshObject.LightMapTexCoords
11
to TAffineVectorList (thanks Ast) (Bugtracker ID = 2000089)
12
29/05/08 - DaStr - Replaced GLUtils with GLGraphics (BugTracker ID = 1923844)
14
13/08/07 - fig - Added checks for DDS textures in LoadFromStream()
15
23/03/07 - fig - Fixed exception when material properties were loaded without a material library being assigned.
16
15/01/07 - fig - If available, material data is now imported/exported.
17
15/01/07 - fig - Added checks in the loader for invalid material indices. LMTools can return meshes like this for some reason.
18
14/01/07 - fig - Material/facegroup name is now changed to the available filename instead of stripping the extention.
19
12/01/07 - fig - Fixed LoadFromStream() to handle duplicate and null textures correctly.
20
07/01/07 - fig - Fixed the file extention stripping. extra periods in the filenames were causing conflicts.
21
06/01/07 - fig - Strip all texture file extentions on load/save
22
03/01/07 - fig - can now use different texture types from the ones stated in the file,
23
missing texture exception handling, normals are built on load,
24
support for more facegroup types added.
25
02/01/07 - fig - Added SavetoStream() and Capabilities function.
26
02/01/07 - PvD - Dealing with non empty material libraries.
27
02/01/07 - PvD - Mirrored mesh in X to original orientation.
28
01/01/07 - Dave Gravel - Modification to make it work.
29
10/09/03 - Domin - Creation
41
GLVectorFileObjects, GLApplicationFileIO, GLVectorLists, GLVectorGeometry,
42
GLTexture, GLPersistentClasses, GLGraphics, GLMaterial;
45
C_LMTS_ID = $53544D4C;
47
C_LMTS_SUBS = $53425553;
48
C_LMTS_TEXT = $54584554;
49
C_LMTS_TRIS = $53495254;
51
C_LMTS_TEXFNLEN = 255; // max texture filename length
54
PLMTS_Header = ^TLMTS_Header;
56
TLMTS_Header = record // packed
60
nTexts: word; // # of textures
67
PLMTS_TexData = ^TLMTS_TexData;
69
TLMTS_TexData = record // packed
70
fName: array [0 .. C_LMTS_TEXFNLEN] of ansichar;
74
PLMTS_Subset = ^TLMTS_Subset;
76
TLMTS_Subset = record // packed
83
PLMTS_Vertex = ^TLMTS_Vertex;
85
TLMTS_Vertex = record // packed
87
u1, v1, u2, v2: single;
102
TMaterialInfo = record
103
FShininess, BShininess: TShininess;
104
FAmbient, FDiffuse, FEmission, FSpecular, BAmbient, BDiffuse, BEmission,
106
ImageAlpha: TGLTextureImageAlpha;
107
magFilter: TGLMagFilter;
108
minFilter: TGLMinFilter;
109
TextureMode: TGLTextureMode;
110
TextureWrap: TGLTextureWrap;
111
Blendingmode: TBlendingMode;
112
FaceCulling: TFaceCulling;
116
TGLLMTSVectorFile = class(TGLVectorFile)
118
class function Capabilities: TGLDataFileCapabilities; override;
120
procedure LoadFromStream(aStream: TStream); override;
121
procedure SaveToStream(aStream: TStream); override;
131
// ------------------ TGLLMTSVectorFile ------------------
137
class function TGLLMTSVectorFile.Capabilities: TGLDataFileCapabilities;
139
Result := [dfcRead, dfcWrite];
145
procedure TGLLMTSVectorFile.LoadFromStream(aStream: TStream);
148
FG: TFGVertexIndexList;
149
LL: TGLMaterialLibrary;
150
ML: TGLMaterialLibrary;
153
V: array [0 .. 2] of TLMTS_Vertex;
159
libmat: TGLLibmaterial;
160
lmnames, matnames: TStringlist;
161
MatInfoHeader: array [0 .. 3] of ansichar;
162
MatInfoCount: cardinal;
163
Matinfo: array of TMaterialInfo;
166
owner.MeshObjects.Clear;
168
MO := TGLMeshObject.CreateOwned(owner.MeshObjects);
169
MO.Mode := momFaceGroups;
171
vi := Tintegerlist.create;
173
LL := owner.LightmapLibrary;
174
ML := owner.MaterialLibrary;
176
lmnames := TStringlist.create;
177
matnames := TStringlist.create;
181
aStream.Read(LMTS.header, SizeOf(TLMTS_Header));
183
if (LMTS.header.ID <> C_LMTS_ID) or (LMTS.header.Ver <> C_LMTS_VER) or
184
(LMTS.header.headerSize < SizeOf(TLMTS_Header)) or
185
(LMTS.header.subSize < SizeOf(TLMTS_Subset)) or
186
(LMTS.header.vtxSize < SizeOf(TLMTS_Vertex)) then
187
raise Exception.create('Error in header');
189
// read "user" data - actually skip this data...
190
LMTS.usrSize := LMTS.header.headerSize - SizeOf(TLMTS_Header);
191
if (LMTS.usrSize > 7) then
193
aStream.Read(MatInfoHeader, 4);
194
if MatInfoHeader = 'MATI' then
196
aStream.Read(MatInfoCount, 4);
197
if MatInfoCount > 0 then
199
setlength(Matinfo, MatInfoCount);
200
for i := 0 to MatInfoCount - 1 do
202
aStream.Read(Matinfo[i], SizeOf(Matinfo[i]));
205
if LMTS.usrSize > ((MatInfoCount * SizeOf(TMaterialInfo)) + 8) then
207
LMTS.usrSize := LMTS.usrSize -
208
((MatInfoCount * SizeOf(TMaterialInfo)) + 8);
209
aStream.Seek(LMTS.usrSize, soFromCurrent);
213
aStream.Seek(LMTS.usrSize - 4, soFromCurrent);
215
else if (LMTS.usrSize > 0) then
216
aStream.Seek(LMTS.usrSize, soFromCurrent);
218
// read texture filenames data...
219
aStream.Read(_4cc, SizeOf(_4cc));
220
if (_4cc <> C_LMTS_TEXT) then
221
raise Exception.create('Texture data not found');
223
for C := 0 to LMTS.header.nTexts - 1 do
225
aStream.Read(T, SizeOf(TLMTS_TexData));
228
if Assigned(ML) and (trim(String(T.fName)) <> '') then
230
fName := String(T.fName);
232
if lastdelimiter('.', fName) <> length(fName) - 3 then
233
fName := fName + '.tga'
235
fName := changefileext(fName, '.tga');
236
if not fileexists(fName) then
238
fName := changefileext(fName, '.jpg');
239
if not fileexists(fName) then
241
fName := changefileext(fName, '.png');
242
if not fileexists(fName) then
244
fName := changefileext(fName, '.bmp');
245
if not fileexists(fName) then
247
fName := changefileext(fName, '.dds');
248
if not fileexists(fName) then
250
fName := String(T.fName);
252
// fName:=fName+' (missing)';
258
libmat := ML.Materials.GetLibMaterialByName(fName);
259
if not Assigned(libmat) then
261
with ML.AddTextureMaterial(fName, fName) do
262
Material.Texture.TextureMode := tmModulate;
269
if not Owner.IgnoreMissingTextures then
275
matnames.add(String(T.fName));
279
if Assigned(LL) and (trim(String(T.fName)) <> '') then
281
fName := String(T.fName);
283
if lastdelimiter('.', fName) <> length(fName) - 3 then
284
fName := fName + '.tga'
286
fName := changefileext(fName, '.tga');
287
if not fileexists(fName) then
289
fName := changefileext(fName, '.jpg');
290
if not fileexists(fName) then
292
fName := changefileext(fName, '.png');
293
if not fileexists(fName) then
295
fName := changefileext(fName, '.bmp');
296
if not fileexists(fName) then
298
fName := changefileext(fName, '.dds');
299
if not fileexists(fName) then
301
fName := String(T.fName);
303
// fName:=fName+' (missing)';
309
libmat := LL.Materials.GetLibMaterialByName(fName);
310
if not Assigned(libmat) then
312
with LL.AddTextureMaterial(fName, fName).Material.Texture do
314
minFilter := miLinear;
315
TextureWrap := twNone;
316
TextureFormat := tfRGB;
317
TextureMode := tmModulate;
325
if not Owner.IgnoreMissingTextures then
331
lmnames.add(String(T.fName));
335
if Assigned(owner.MaterialLibrary) then
336
for i := 0 to MatInfoCount - 1 do
339
for j := 0 to owner.MaterialLibrary.Materials.Count - 1 do
340
if owner.MaterialLibrary.Materials[j].NameHashKey = Matinfo[i].mathash
343
libmat := owner.MaterialLibrary.Materials[j];
347
if Assigned(libmat) then
351
libmat.Material.FrontProperties.Shininess := FShininess;
352
libmat.Material.BackProperties.Shininess := BShininess;
353
libmat.Material.FrontProperties.Ambient.Color := FAmbient;
354
libmat.Material.FrontProperties.Diffuse.Color := FDiffuse;
355
libmat.Material.FrontProperties.Emission.Color := FEmission;
356
libmat.Material.FrontProperties.Specular.Color := FSpecular;
358
libmat.Material.BackProperties.Ambient.Color := BAmbient;
359
libmat.Material.BackProperties.Diffuse.Color := BDiffuse;
360
libmat.Material.BackProperties.Emission.Color := BEmission;
361
libmat.Material.BackProperties.Specular.Color := BSpecular;
363
libmat.Material.Texture.ImageAlpha := ImageAlpha;
364
libmat.Material.Texture.magFilter := magFilter;
365
libmat.Material.Texture.minFilter := minFilter;
366
libmat.Material.Texture.TextureMode := TextureMode;
367
libmat.Material.Texture.TextureWrap := TextureWrap;
368
libmat.Material.Blendingmode := Blendingmode;
369
libmat.Material.FaceCulling := FaceCulling;
374
// read subset data...
375
aStream.Read(_4cc, SizeOf(_4cc));
376
if (_4cc <> C_LMTS_SUBS) then
377
raise Exception.create('Subset data not found');
378
for C := LMTS.header.nSubsets - 1 downto 0 do
380
aStream.Read(S, LMTS.header.subSize);
381
FG := TFGVertexIndexList.CreateOwned(MO.FaceGroups);
382
FG.Mode := fgmmTriangles;
383
FG.vertexindices.AddSerie(S.Offset * 3, 1, S.Count * 3);
384
vi.AddSerie(S.Offset * 3, 1, S.Count * 3);
386
if Assigned(ML) and (S.TextID1 <> $FFFF) then
388
if (S.TextID1 < matnames.Count) then
390
libmat := ML.Materials.GetLibMaterialByName(matnames[S.TextID1]);
391
if Assigned(libmat) then
392
FG.MaterialName := libmat.Name;
396
if Assigned(LL) and (S.TextID2 <> $FFFF) then
398
if (S.TextID2 - matnames.Count < lmnames.Count) and
399
(S.TextID2 - matnames.Count > -1) then
401
libmat := LL.Materials.GetLibMaterialByName
402
(lmnames[S.TextID2 - matnames.Count]);
403
if Assigned(libmat) then
404
FG.lightmapindex := libmat.Index;
408
// read vertex data...
409
aStream.Read(_4cc, SizeOf(_4cc));
410
if (_4cc <> C_LMTS_TRIS) then
411
raise Exception.create('Vertex data not found');
412
for C := 0 to integer(LMTS.header.nTris) - 1 do
414
aStream.Read(V[0], LMTS.header.vtxSize);
415
aStream.Read(V[1], LMTS.header.vtxSize);
416
aStream.Read(V[2], LMTS.header.vtxSize);
418
MO.Vertices.add(-V[0].x, V[0].y, V[0].z);
419
MO.TexCoords.add(V[0].u1, -V[0].v1);
420
MO.LightmapTexCoords.add(V[0].u2, 1 - V[0].v2);
422
MO.Vertices.add(-V[2].x, V[2].y, V[2].z);
423
MO.TexCoords.add(V[2].u1, -V[2].v1);
424
MO.LightmapTexCoords.add(V[2].u2, 1 - V[2].v2);
426
MO.Vertices.add(-V[1].x, V[1].y, V[1].z);
427
MO.TexCoords.add(V[1].u1, -V[1].v1);
428
MO.LightmapTexCoords.add(V[1].u2, 1 - V[1].v2);
430
MO.BuildNormals(vi, momtriangles);
434
setlength(Matinfo, 0);
445
procedure TGLLMTSVectorFile.SaveToStream(aStream: TStream);
448
FG: TFGVertexIndexList;
449
i, j, k, l, lmstartindex, C, matindex: integer;
451
V: array [0 .. 2] of TLMTS_Vertex;
452
texData: array of TLMTS_TexData;
453
subsets: array of TLMTS_Subset;
454
tris: array of TLMTS_Vertex;
458
Matinfo: array of TMaterialInfo;
459
MatInfoCount: integer;
460
libmat: TGLLibmaterial;
463
setlength(subsets, 0);
464
setlength(texData, 0);
466
lmstartindex := maxint;
467
for i := 0 to owner.MeshObjects.Count - 1 do
469
MO := owner.MeshObjects[i];
470
for j := 0 to MO.FaceGroups.Count - 1 do
472
FG := TFGVertexIndexList(MO.FaceGroups[j]);
474
matname := AnsiString(FG.MaterialName);
476
// no duplicate textures please
478
for k := 0 to high(texData) do
479
if texData[k].fName = matname then
485
if matindex = -1 then // not a duplicate, so add it
487
setlength(texData, length(texData) + 1);
488
with texData[high(texData)] do
490
matindex := high(texData);
492
strpcopy(pansichar(@fName), matname);
496
inc(C); // used to offest the lightmap index
499
// set some of the facegroup (subsets) info here.
500
setlength(subsets, length(subsets) + 1);
501
with subsets[high(subsets)] do
503
if (matname <> '') then
509
if (FG.lightmapindex > -1) and (lmstartindex > FG.lightmapindex) then
510
lmstartindex := FG.lightmapindex; // used to offest the lightmap index
514
if lmstartindex = maxint then
515
lmstartindex := 0; // cool, lightmaps start from the first index
517
for i := 0 to owner.MeshObjects.Count - 1 do
519
MO := owner.MeshObjects[i];
520
for j := 0 to MO.FaceGroups.Count - 1 do
522
FG := TFGVertexIndexList(MO.FaceGroups[j]);
524
// subset already created earlier, just finish filling the data.
525
// we needed the "c" and "lmstartindex" to be able to do this
528
Offset := length(tris) div 3;
529
Count := FG.vertexindices.Count div 3;
531
if (FG.lightmapindex > -1) and Assigned(owner.LightmapLibrary) then
532
TextID2 := C + FG.lightmapindex - lmstartindex
537
// fill the vertex data
539
while k < FG.vertexindices.Count do
546
x := -MO.Vertices[FG.vertexindices[k + l]].V[0];
547
y := MO.Vertices[FG.vertexindices[k + l]].V[1];
548
z := MO.Vertices[FG.vertexindices[k + l]].V[2];
553
if FG is TFGVertexNormalTexIndexList then
555
if MO.TexCoords.Count > TFGVertexNormalTexIndexList(FG)
556
.texcoordIndices[k + l] then
558
u1 := MO.TexCoords[TFGVertexNormalTexIndexList(FG)
559
.texcoordIndices[k + l]].V[0];
560
v1 := -MO.TexCoords[TFGVertexNormalTexIndexList(FG)
561
.texcoordIndices[k + l]].V[1];
564
else if FG is TFGIndexTexCoordList then
566
u1 := TFGIndexTexCoordList(FG).TexCoords[k + l].V[0];
567
v1 := -TFGIndexTexCoordList(FG).TexCoords[k + l].V[1];
569
else if MO.TexCoords.Count > FG.vertexindices[k + l] then
571
u1 := MO.TexCoords[FG.vertexindices[k + l]].V[0];
572
v1 := -MO.TexCoords[FG.vertexindices[k + l]].V[1];
575
// lightmap texcoords
578
if MO.LightmapTexCoords.Count > FG.vertexindices[k + l] then
580
u2 := MO.LightmapTexCoords[FG.vertexindices[k + l]].V[0];
581
v2 := 1 - MO.LightmapTexCoords[FG.vertexindices[k + l]].V[1];
585
setlength(tris, length(tris) + 3);
587
tris[high(tris) - 2] := V[0];
588
tris[high(tris) - 1] := V[2];
589
tris[high(tris)] := V[1];
597
setlength(Matinfo, 0);
598
// store the material properties..
599
if Assigned(owner.MaterialLibrary) then
601
for i := 0 to high(texData) do
603
libmat := owner.MaterialLibrary.Materials.GetLibMaterialByName
604
(String(texData[i].fName));
605
if Assigned(libmat) then
607
setlength(Matinfo, length(Matinfo) + 1);
608
with Matinfo[high(Matinfo)] do
610
FShininess := libmat.Material.FrontProperties.Shininess;
611
BShininess := libmat.Material.BackProperties.Shininess;
612
FAmbient := libmat.Material.FrontProperties.Ambient.Color;
613
FDiffuse := libmat.Material.FrontProperties.Diffuse.Color;
614
FEmission := libmat.Material.FrontProperties.Emission.Color;
615
FSpecular := libmat.Material.FrontProperties.Specular.Color;
617
BAmbient := libmat.Material.BackProperties.Ambient.Color;
618
BDiffuse := libmat.Material.BackProperties.Diffuse.Color;
619
BEmission := libmat.Material.BackProperties.Emission.Color;
620
BSpecular := libmat.Material.BackProperties.Specular.Color;
622
ImageAlpha := libmat.Material.Texture.ImageAlpha;
623
magFilter := libmat.Material.Texture.magFilter;
624
minFilter := libmat.Material.Texture.minFilter;
625
TextureMode := libmat.Material.Texture.TextureMode;
626
TextureWrap := libmat.Material.Texture.TextureWrap;
627
Blendingmode := libmat.Material.Blendingmode;
628
FaceCulling := libmat.Material.FaceCulling;
629
mathash := libmat.NameHashKey;
635
// add the lightmap texture names to the texdata list
636
C := length(texData);
637
if Assigned(owner.LightmapLibrary) then
638
for i := 0 to owner.MeshObjects.Count - 1 do
640
MO := owner.MeshObjects[i];
641
for j := 0 to MO.FaceGroups.Count - 1 do
643
FG := TFGVertexIndexList(MO.FaceGroups[j]);
644
if FG.lightmapindex > -1 then
646
matname := AnsiString(owner.LightmapLibrary.Materials
647
[FG.lightmapindex].Name);
648
// no duplicate textures please
650
for k := C to high(texData) do
651
if texData[k].fName = matname then
656
if matindex = -1 then // not a duplicate, so add it
658
setlength(texData, length(texData) + 1);
659
with texData[high(texData)] do
661
strpcopy(pansichar(@fName), matname);
669
// fill and write the file header
674
headerSize := 24 + 8 + (length(Matinfo) * SizeOf(TMaterialInfo));
675
nTexts := length(texData);
676
nSubsets := length(subsets);
677
nTris := length(tris) div 3;
678
subSize := SizeOf(TLMTS_Subset);
679
vtxSize := SizeOf(TLMTS_Vertex);
681
aStream.Write(h, SizeOf(h));
683
aStream.Write('MATI', 4);
684
MatInfoCount := length(Matinfo);
685
aStream.Write(MatInfoCount, SizeOf(MatInfoCount));
686
// write the materials info
687
for i := 0 to high(Matinfo) do
688
aStream.Write(Matinfo[i], SizeOf(Matinfo[i]));
690
// write the texture names
692
aStream.Write(_4cc, SizeOf(_4cc));
693
for i := 0 to high(texData) do
694
aStream.Write(texData[i], SizeOf(texData[i]));
698
aStream.Write(_4cc, SizeOf(_4cc));
699
for i := 0 to high(subsets) do
700
aStream.Write(subsets[i], SizeOf(subsets[i]));
704
aStream.Write(_4cc, SizeOf(_4cc));
705
for i := 0 to high(tris) do
706
aStream.Write(tris[i], SizeOf(tris[i]));
708
// free up used memory
710
setlength(subsets, 0);
711
setlength(texData, 0);
712
setlength(Matinfo, 0);
717
RegisterVectorFileFormat('lmts', 'Pulsar Studio LMTS File Format',