e7a2ff4bb8eb30402daba3fe3dc9c226528d890c
[sdk] / ecere / src / gfx / 3D / models / Object3DSFormat.ec
1 namespace gfx3D::models;
2
3 import "Object"
4
5 #if defined(__EMSCRIPTEN__)
6 #if !defined(_GLES2)
7 #define _GLES2
8 #endif
9 #endif
10
11 #if (defined(__ANDROID__) || defined(__ODROID__)) && !defined(_GLES)
12 #define _GLES
13 #endif
14
15 #if !defined(_GLES) && !defined(_GLES2)
16 #define USE_32_BIT_INDICES true
17 #define indicesMember indices32
18 #define uintindex uint32
19 #else
20 #define USE_32_BIT_INDICES false
21 #define indicesMember indices
22 #define uintindex uint16
23 #endif
24
25 #define MAXNAMELEN   64
26
27 static enum ChunkID3DS : uint16
28 {
29    // RGB Chunks
30    RGB_FLOAT             = 0x0010,
31    RGB_BYTE              = 0x0011,
32    RGB_BYTE_GAMMA        = 0x0012,
33    RGB_FLOAT_GAMMA       = 0x0013,
34
35    // Amount Of Chunks
36    AMOUNT_OF             = 0x0030,
37
38    MAIN3DS               = 0x4D4D,
39    EDIT3DS               = 0x3D3D,
40    EDIT_AMBIENT          = 0x2100,
41    EDIT_MATERIAL         = 0xAFFF,
42    EDIT_OBJECT           = 0x4000,
43    OBJ_HIDDEN            = 0x4010,
44    OBJ_TRIMESH           = 0x4100,
45    OBJ_LIGHT             = 0x4600,
46    OBJ_CAMERA            = 0x4700,
47
48    // Triangular Mesh Chunks
49    TRI_VERTEXL           = 0x4110,
50    TRI_FACEL1            = 0x4120,
51    TRI_MATERIAL          = 0x4130,
52    TRI_MAPPINGCOORS      = 0x4140,
53    TRI_SMOOTHING         = 0x4150,
54    TRI_LOCAL             = 0x4160,
55
56    // Light Chunks
57    LIT_SPOT              = 0x4610,
58    LIT_ONOFF             = 0x4620,
59    LIT_ATTENUATION       = 0x4625,
60    LIT_START             = 0x4659,
61    LIT_END               = 0x465A,
62    LIT_MULTIPLIER        = 0x465B,
63
64    // Camera Chunks
65    CAM_SEECONE           = 0x4710,
66    CAM_RANGES            = 0x4720,
67
68    // Material Chunks
69    MAT_NAME              = 0xA000,
70    MAT_AMBIENT           = 0xA010,
71    MAT_DIFFUSE           = 0xA020,
72    MAT_SPECULAR          = 0xA030,
73    MAT_SHININESS         = 0xA040,
74    MAT_SHINSTRENGTH      = 0xA041,
75    MAT_SHIN3PC           = 0xA042,
76    MAT_TRANSPARENCY      = 0xA050,
77    MAT_XPFALL            = 0xA052,
78    MAT_REFBLUR           = 0xA053,
79    MAT_SELFILLUM         = 0xA080,
80    MAT_DOUBLESIDED       = 0xA081,
81    MAT_ADDITIVE          = 0xA083,
82    MAT_SELFILPCT         = 0xA084,
83    MAT_WIRE              = 0xA085,
84    MAT_SUPERSMP          = 0xA086,
85    MAT_WIRETHICKNESS     = 0xA087,
86    MAT_FACEMAP           = 0xA088,
87    MAT_XPFALLIN          = 0xA08A,
88    MAT_PHONG             = 0xA08C,
89    MAT_WIREABS           = 0xA08E,
90    MAT_SHADING           = 0xA100,
91    MAT_MAPTEXTURE1       = 0xA200,
92    MAT_SPECULARMAP       = 0xA204,
93    MAT_MAPOPACITY        = 0xA210,
94    MAT_REFLECTIONMAP     = 0xA220,
95    MAT_BUMPMAP           = 0xA230,
96
97    // Map Chunks
98    MAP_FILENAME          = 0xA300,
99
100    MAT_SHININESSMAP      = 0xA33C,
101    MAT_EMISSIVEMAP       = 0xA33D,
102
103    MAP_OPTIONS           = 0xA351,
104    MAP_1_U_SCALE         = 0xA354,
105    MAP_1_V_SCALE         = 0xA356,
106    MAP_U_OFFSET          = 0xA358,
107    MAP_V_OFFSET          = 0xA35A,
108    MAP_ROTATION          = 0xA35C,
109
110    MAP_FILTERBLUR        = 0xA353,
111    MAP_1_U_SCALE         = 0xA354,
112    MAP_1_V_SCALE         = 0xA356,
113    MAP_U_OFFSET          = 0xA358,
114    MAP_V_OFFSET          = 0xA35A,
115    MAP_ROTATION          = 0xA35C,
116
117    MAP_LUMTINT1          = 0xA360,
118    MAP_LUMTINT2          = 0xA362,
119
120    MAP_TINTR             = 0xA364,
121    MAP_TINTG             = 0xA366,
122    MAP_TINTB             = 0xA368,
123
124    // Keyframer Chunks
125    KEYFRAME3DS           = 0xB000,
126    FRM_AMBIENT           = 0xB001,
127    FRM_MESHINFO          = 0xB002,
128    FRM_CAMERA            = 0xB003,
129    FRM_CAMERATARGET      = 0xB004,
130    FRM_OMNILIGHT         = 0xB005,
131    FRM_SPOTLIGHTTARGET   = 0xB006,
132    FRM_SPOTLIGHT         = 0xB007,
133    FRM_FRAMES            = 0xB008,
134    FRM_PARAM             = 0xB010,
135    FRM_DUMMYNAME         = 0xB011,
136    FRM_PIVOT             = 0xB013,
137    FRM_TRACKPOS          = 0xB020,
138    FRM_TRACKROT          = 0xB021,
139    FRM_TRACKSCALE        = 0xB022,
140    FRM_TRACKFOV          = 0xB023,
141    FRM_TRACKROLL         = 0xB024,
142    FRM_TRACKCOLOR        = 0xB025,
143    FRM_TRACKMORPH        = 0xB026,  // What does this do?
144    FRM_TRACKHOTSPOT      = 0xB027,
145    FRM_TRACKFALLOFF      = 0xB028,
146    FRM_TRACKHIDE         = 0xB029,
147    FRM_HIERARCHY         = 0xB030
148 };
149
150 static class MapOptions : uint32
151 {
152    bool decal:1; // (related to dontTile)
153    bool mirror:1;
154    bool negative:1;
155    bool dontTile:1;
156    bool summedFiltering:1;
157    bool useAlpha:1;
158    bool lumOrAlphaTint:1;
159    bool ignoreAlpha:1;
160    bool rgbTint:1;
161 };
162
163 typedef struct FileInfo FileInfo;
164
165 typedef struct
166 {
167    uint indices[3];
168    uint origIndices[3];
169    uint smoothGroups;
170    Material material;
171    Vector3D normal;
172    double dots[3];
173    bool done:1;
174 } Face;
175
176 static struct FileInfo
177 {
178    File f;
179    DisplaySystem displaySystem;
180    Object rootObject;
181    const String fileName;
182
183    ChunkID3DS chunkId;
184    uint pos, end;
185
186    FileInfo * parent;
187    int nFaces;
188    Face * faces;
189    char textureDirectory[MAX_DIRECTORY];
190    Map<uintptr, Array<int>> matFaces;
191 };
192
193 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
194                       | (((unsigned short)(word) & 0xff00) >> 8)
195
196 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
197                          | (((unsigned int)(dword) & 0x0000ff00) <<  8) \
198                          | (((unsigned int)(dword) & 0x00ff0000) >>  8) \
199                          | (((unsigned int)(dword) & 0xff000000) >> 24))
200
201 #ifndef __BIG_ENDIAN__
202 #define BIGENDSWAP_WORD(word)
203 #define BIGENDSWAP_DWORD(dword)
204 #else
205 #define BIGENDSWAP_WORD(word)      (*(uint16 *)(&(word)))  = SWAP_WORD((*(uint16 *)(&(word))));
206 #define BIGENDSWAP_DWORD(dword)    (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
207 #endif
208
209 // Zero Terminated String
210 static int ReadASCIIZ(File f, char ** string)
211 {
212    // *** Read String ***
213    int c;
214    char temp[1024] = "";
215    for(c=0; c<1024;)
216    {
217       f.Getc(&temp[c]);
218       if(!temp[c++]) break;
219    }
220    *string = new char[c];
221    if(*string)
222       strcpy(*string, temp);
223    return c;
224 }
225
226 static float ReadFloat(File f)
227 {
228    float floatValue = 0;
229    f.Read(&floatValue, sizeof(float), 1);
230    BIGENDSWAP_DWORD(floatValue);
231    return floatValue;
232 }
233
234 static uint16 ReadWORD(File f)
235 {
236    uint16 wordValue = 0;
237    f.Read(&wordValue, sizeof(uint16), 1);
238    BIGENDSWAP_WORD(wordValue);
239    return wordValue;
240 }
241
242 static uint ReadDWORD(File f)
243 {
244    uint dwordValue = 0;
245    f.Read(&dwordValue, sizeof(uint), 1);
246    BIGENDSWAP_DWORD(dwordValue);
247    return dwordValue;
248 }
249
250 // Sub Chunks
251 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
252 {
253    for(;info->pos < info->end;)
254    {
255       FileInfo childInfo = *info;
256       uint length;
257
258       childInfo.parent = info;
259
260       info->f.Seek(info->pos, start);
261       childInfo.chunkId = (ChunkID3DS)ReadWORD(info->f);
262       length = ReadDWORD(info->f);
263
264       childInfo.pos += sizeof(uint16) + sizeof(uint);
265       childInfo.end = info->pos + length;
266
267       if(!chunkParser(&childInfo, data))
268          return false;
269
270       info->pos = childInfo.end;
271    }
272    return true;
273 }
274
275 // General Chunks
276 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
277 {
278    if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
279    {
280       byte value = 0;
281       info->f.Getc((char *)&value); rgb->r = value / 255.0f;
282       info->f.Getc((char *)&value); rgb->g = value / 255.0f;
283       info->f.Getc((char *)&value); rgb->b = value / 255.0f;
284    }
285    else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
286    {
287       rgb->r = ReadFloat(info->f);
288       rgb->g = ReadFloat(info->f);
289       rgb->b = ReadFloat(info->f);
290    }
291    return true;
292 }
293
294 static bool Read3DVertex(File f, Vector3Df vertex)
295 {
296    vertex.x = ReadFloat(f);
297    vertex.y = ReadFloat(f);
298    vertex.z = ReadFloat(f);
299    return true;
300 }
301
302 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
303 {
304    if(info->chunkId == AMOUNT_OF)
305       *amountOf = ReadWORD(info->f);
306    return true;
307 }
308
309 #define WELD_TRESHOLD        0.000001
310 #define SMOOTH_CUTOFF   0  // 45
311
312 struct SharedSourceVertexInfo
313 {
314    int index;
315    Vector3Df value;
316    uint unique;
317    Face * face;
318
319    int OnCompare(SharedSourceVertexInfo b)
320    {
321       if(unique < b.unique) return -1;
322       if(unique > b.unique) return 1;
323
324       if(unique)
325       {
326          if(face < b.face) return -1;
327          if(face > b.face) return 1;
328       }
329       if(index == b.index) return 0;
330       if(WELD_TRESHOLD > 0)
331       {
332          if(value.x < b.value.x - WELD_TRESHOLD) return -1;
333          if(value.x > b.value.x + WELD_TRESHOLD) return 1;
334          if(value.y < b.value.y - WELD_TRESHOLD) return -1;
335          if(value.y > b.value.y + WELD_TRESHOLD) return 1;
336          if(value.z < b.value.z - WELD_TRESHOLD) return -1;
337          if(value.z > b.value.z + WELD_TRESHOLD) return 1;
338       }
339       else
340       {
341          if(index < b.index) return -1;
342          if(index > b.index) return 1;
343       }
344       return 0;
345    }
346 };
347
348 class SharedDestVertexInfo
349 {
350    Array<int> faces { };
351 };
352
353 struct SourceVertexInfo
354 {
355    SharedSourceVertexInfo * shared;
356    Pointf texCoord;
357    uint smoothGroups;
358
359    int OnCompare(SourceVertexInfo b)
360    {
361       int r = (*shared).OnCompare(*b.shared);
362       if(!r) r = texCoord.OnCompare(b.texCoord);
363       if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
364       return r;
365    }
366 };
367
368 class DestVertexInfo
369 {
370    int index, copyFromIndex;
371    Vector3Df normal, tangent1, tangent2;
372 };
373
374 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
375 {
376    int c;
377    Face * faces = info->faces;
378    int nVertices = mesh.nVertices;
379    int index;
380    int nNewVertices;
381    Vector3Df * mVertices;
382    double cutOff = cos(Degrees { SMOOTH_CUTOFF });
383
384    Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
385    Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
386    Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
387
388    MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
389    MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
390
391    nNewVertices = nVertices;
392    mVertices = mesh->vertices;
393
394    for(c = 0; c<info->nFaces; c++)
395    {
396       Face * face = &faces[c];
397       Plane plane;
398       uint * indices = face->indices;
399       Vector3D edges[3], rEdges[3];
400       computeNormalWeights(3, mVertices, indices, true, 0, face->dots, edges, rEdges);
401
402       plane.FromPointsf(mVertices[indices[2]], mVertices[indices[1]], mVertices[indices[0]]);
403       face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
404    }
405
406    for(c = 0; c < info->nFaces; c++)
407    {
408       Face * face = &faces[c];
409       int i;
410
411       // Zero space points
412       if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
413          !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
414          continue;
415
416       for(i = 0; i<3; i++)
417       {
418          SharedSourceVertexInfo * source;
419          SharedDestVertexInfo svInfo;
420          DestVertexInfo vInfo;
421
422          index = face->indices[i];
423
424          if(face->smoothGroups)
425             itShared.Index({ index = index, mVertices[index], face = face }, true);
426          else
427             itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
428          svInfo = itShared.data;
429          if(!svInfo) itShared.data = svInfo = { };
430          svInfo.faces.Add(c);
431
432          source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
433          // TODO: Allow obtaining address of MapIterator::key
434          // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
435          it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
436          vInfo = it.data;
437          if(!vInfo)
438          {
439             vInfo = { };
440             it.data = vInfo;
441             vInfo.copyFromIndex = index;
442             vInfo.index = index;
443          }
444
445          if(!vertices[index])
446             vertices[index] = (void *)it.pointer;
447          else if(vertices[index] != it.pointer)
448          {
449             // If it's a different smoothing group, we'll need extra vertices
450             index = vertices.size;
451             vInfo.index = index;
452             vertices.Add((void *)it.pointer);
453             nNewVertices++;
454          }
455          face->indices[i] = vInfo.index;
456       }
457    }
458
459    for(index = 0; index < nNewVertices; index++)
460    {
461       it.pointer = vertices[index];
462       if(it.pointer)
463       {
464          int numShared = 0;
465          double dotSum = 0;
466          DestVertexInfo vInfo = it.data;
467          Vector3D normal { };
468          Vector3D tangent1 { };
469          Vector3D tangent2 { };
470          SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
471          uint smoothing = inf->smoothGroups;
472          bool added = true;
473          SharedSourceVertexInfo * shared = inf->shared;
474          SharedDestVertexInfo svInfo = sharedVertices[*shared];
475          int origIndex;
476          if(!svInfo || vInfo.index != index)
477             continue;
478
479          for(i : svInfo.faces)
480          {
481             Face * face = &info->faces[i];
482             face->done = false;
483             if(smoothing & face->smoothGroups)
484                smoothing |= face->smoothGroups;
485          }
486
487          // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
488          if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
489          {
490             for(i : svInfo.faces)
491             {
492                Face * face = &info->faces[i];
493                if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
494                {
495                   int j;
496                   for(j = 0; j < 3; j++)
497                   {
498                      if(face->indices[j] == vInfo.index)
499                      {
500                         origIndex = face->origIndices[j];
501                         face->done = true;
502                         normal.x += face->normal.x;
503                         normal.y += face->normal.y;
504                         normal.z += face->normal.z;
505                         numShared++;
506                         break;
507                      }
508                   }
509                }
510                if(numShared) break;
511             }
512          }
513
514          while(added)
515          {
516             added = false;
517             for(i : svInfo.faces)
518             {
519                Face * face = &info->faces[i];
520                if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
521                {
522                   bool valid = true;
523
524                   if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
525                   {
526                      int origIndexB = -1;
527                      int k;
528
529                      for(k = 0; k < 3; k++)
530                      {
531                         if(face->indices[k] == vInfo.index)
532                         {
533                            origIndexB = face->origIndices[k];
534                            break;
535                         }
536                      }
537                      valid = origIndex == origIndexB;
538                      if(!valid)
539                      {
540                         for(j : svInfo.faces)
541                         {
542                            if(info->faces[j].done)
543                            {
544                               double dot = info->faces[j].normal.DotProduct(face->normal);
545                               if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
546                               valid = fabs(dot) > cutOff;
547                               if(valid) break;
548                            }
549                         }
550                      }
551                   }
552
553                   if(valid)
554                   {
555                      uint * indices = face->origIndices;
556                      uint ix = vInfo.copyFromIndex;
557                      uint ix0 = indices[0], ix1 = indices[1], ix2 = indices[2];
558                      double dot = ix == ix0 ? face->dots[0] : ix == ix1 ? face->dots[1] : ix == ix2 ? face->dots[2] : 1;
559                      if(ix != indices[0] && ix != indices[1] && ix != indices[2])
560                      {
561                              if(!mVertices[ix0].OnCompare(mVertices[ix])) dot = face->dots[0];
562                         else if(!mVertices[ix1].OnCompare(mVertices[ix])) dot = face->dots[1];
563                         else if(!mVertices[ix2].OnCompare(mVertices[ix])) dot = face->dots[2];
564                      }
565                      normal.x += face->normal.x * dot;
566                      normal.y += face->normal.y * dot;
567                      normal.z += face->normal.z * dot;
568
569                      if(mesh.texCoords)
570                      {
571                         Vector3Df * p0 = &mVertices[ix0], * p1 = &mVertices[ix1], * p2 = &mVertices[ix2];
572                         Pointf * t0 = &mesh->texCoords[ix0], * t1 = &mesh->texCoords[ix1], * t2 = &mesh->texCoords[ix2];
573                         Vector3D v01 { (double)p1->x - (double)p0->x, (double)p1->y - (double)p0->y, (double)p1->z - (double)p0->z };
574                         Vector3D v02 { (double)p2->x - (double)p0->x, (double)p2->y - (double)p0->y, (double)p2->z - (double)p0->z };
575
576                         Vector3D t01 { (double)t1->x - (double)t0->x, (double)t1->y - (double)t0->y };
577                         Vector3D t02 { (double)t2->x - (double)t0->x, (double)t2->y - (double)t0->y };
578                         double f = dot / (t01.x * t02.y - t02.x * t01.y);
579
580                         tangent1.x += f * (v01.x * t02.y - v02.x * t01.y);
581                         tangent1.y += f * (v01.y * t02.y - v02.y * t01.y);
582                         tangent1.z += f * (v01.z * t02.y - v02.z * t01.y);
583
584                         tangent2.x += f * (v02.x * t01.x - v01.x * t02.x);
585                         tangent2.y += f * (v02.y * t01.x - v01.y * t02.x);
586                         tangent2.z += f * (v02.z * t01.x - v01.z * t02.x);
587                      }
588
589                      dotSum += dot;
590                      numShared++;
591                      added = true;
592                      face->done = true;
593                   }
594                }
595             }
596             if(!SMOOTH_CUTOFF || !(WELD_TRESHOLD > 0)) break;
597          }
598          // normal.Scale(normal, 1.0f / numShared);
599          normal.Scale(normal, 1.0 / dotSum);
600          normal.Normalize(normal);
601
602          //tangent1.Scale(tangent1, 1.0 / dotSum);
603          tangent1.Normalize(tangent1);
604
605          //tangent2.Scale(tangent2, 1.0 / dotSum);
606          tangent2.Normalize(tangent2);
607
608          if(vInfo.index == index)
609          {
610             vInfo.normal = { (float)normal.x, (float)normal.y, (float)normal.z };
611             vInfo.tangent1 = { (float)tangent1.x, (float)tangent1.y, (float)tangent1.z };
612             vInfo.tangent2 = { (float)tangent2.x, (float)tangent2.y, (float)tangent2.z };
613          }
614
615          // Auto welding/smoothing requires extra vertices because angle is too steep
616          if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
617          {
618             SharedDestVertexInfo newSharedInfo = null;
619             int index;
620             for(i : svInfo.faces)
621             {
622                Face * face = &info->faces[i];
623                if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
624                {
625                   int j;
626                   for(j = 0; j < 3; j++)
627                   {
628                      if(face->indices[j] == vInfo.index)
629                      {
630                         if(!newSharedInfo)
631                         {
632                            DestVertexInfo newVert;
633                            SharedSourceVertexInfo * source;
634
635                            index = nNewVertices++;
636                            itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
637                            source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
638                            itShared.data = newSharedInfo = { };
639
640                            it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
641                            newVert = { };
642                            it.data = newVert;
643                            newVert.copyFromIndex = vInfo.copyFromIndex;
644                            newVert.index = index;
645
646                            vertices.Add((void *)it.pointer);
647                         }
648                         face->indices[j] = index;
649                         newSharedInfo.faces.Add(i);
650                         break;
651                      }
652                   }
653                }
654             }
655          }
656       }
657    }
658
659    // Allocate some extra vertices
660    {
661       Vector3Df * oldVertices = mesh.vertices;
662       Pointf * oldTexCoords = mesh.texCoords;
663
664       // TODO: Support reallocation?
665       *((void **)&mesh.vertices) = null;
666       *((void **)&mesh.texCoords) = null;
667       *((int *)&mesh.nVertices) = 0;
668
669       mesh.Allocate( { vertices = true, normals = true, tangents = oldTexCoords ? true : false, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
670
671       // Fill in the new vertices
672       for(index = 0; index < nNewVertices; index++)
673       {
674          DestVertexInfo vInfo;
675          it.pointer = vertices[index];
676          vInfo = it.data;
677
678          // Duplicate vertex
679          mesh.normals[index] = vInfo ? vInfo.normal : { };
680          mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
681          if(mesh.tangents)
682          {
683             mesh.tangents[2*index+0] = vInfo ? vInfo.tangent1 : { };
684             mesh.tangents[2*index+1] = vInfo ? vInfo.tangent2 : { };
685          }
686          if(mesh.texCoords)
687             mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
688       }
689
690       delete oldVertices;
691       delete oldTexCoords;
692    }
693
694    {
695       int i;
696       for(i = 0; i < info->nFaces; i++)
697          info->faces[i].done = false;
698    }
699
700    mesh.Unlock({ normals = true, tangents = true });
701
702    // Free all the temporary stuff
703
704    delete vertices;
705    vertexMap.Free();
706    delete vertexMap;
707    sharedVertices.Free();
708    delete sharedVertices;
709 }
710
711 // Meshes
712 static bool ReadSmoothing(FileInfo * info, Object object)
713 {
714    switch(info->chunkId)
715    {
716       case TRI_SMOOTHING:
717       {
718          int c;
719          for(c = 0; c<info->nFaces; c++)
720             info->faces[c].smoothGroups = ReadDWORD(info->f);
721          break;
722       }
723    }
724    return true;
725 }
726
727 static bool ReadFacesListChunks(FileInfo * info, Object object)
728 {
729    DisplaySystem displaySystem = info->displaySystem;
730    switch(info->chunkId)
731    {
732       case TRI_MATERIAL:
733       {
734          char * name;
735          Material mat;
736          int i, c;
737          int count;
738          Array<int> faces;
739          char matName[MAX_LOCATION + 100];
740
741          strcpy(matName, info->fileName);
742          ReadASCIIZ(info->f, &name);
743          count = ReadWORD(info->f);
744          strcat(matName, name);
745
746          mat = displaySystem.GetMaterial(matName);
747          faces = info->matFaces[(uintptr)mat];
748          if(!faces)
749             info->matFaces[(uintptr)mat] = faces = { };
750          i = faces.size;
751          faces.size += count;
752
753          for(c = 0; c<count; c++)
754          {
755             uint16 face = ReadWORD(info->f);
756             faces[i + c] = face;
757             info->faces[face].material = mat;
758          }
759          delete name;
760          break;
761       }
762    }
763    return true;
764 }
765
766 static bool ReadTriMesh(FileInfo * info, Object object)
767 {
768    Mesh mesh = object.mesh;
769    switch(info->chunkId)
770    {
771       case TRI_VERTEXL:
772       {
773          int c;
774          uint16 nVertices = ReadWORD(info->f);
775          //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
776          *((int *) &mesh.nVertices) = nVertices;
777          *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
778          if(mesh.vertices)
779          {
780             for(c = 0; c<mesh.nVertices; c++)
781             {
782                Vector3Df vertex;
783                Read3DVertex(info->f, vertex);
784                mesh.vertices[c].x = vertex.x;
785                mesh.vertices[c].y =-vertex.z;
786                mesh.vertices[c].z = vertex.y;
787             }
788          }
789          else
790             return false;
791          break;
792       }
793       case TRI_MAPPINGCOORS:
794       {
795          int c;
796          uint16 count = ReadWORD(info->f);
797          count = (uint16)Min(mesh.nVertices, count);
798
799          //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
800          *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
801          mesh.flags.texCoords1 = true;
802          if(mesh.texCoords)
803          {
804             for(c = 0; c<count; c++)
805             {
806                mesh.texCoords[c].x = ReadFloat(info->f);
807                mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
808             }
809          }
810          else
811             return false;
812          break;
813       }
814       case TRI_FACEL1:
815       {
816          int c;
817          uint16 nFaces = 0;
818          uint count;
819          uint pos;
820
821          info->nFaces = nFaces = ReadWORD(info->f);
822          info->pos += sizeof(uint16);
823
824          info->faces = new0 Face[nFaces];
825          for(c = 0; c<nFaces; c++)
826          {
827             int i;
828             for(i = 0; i<3; i++)
829                info->faces[c].origIndices[i] =
830                info->faces[c].indices[i] = ReadWORD(info->f);
831             ReadWORD(info->f);
832             info->pos += 4*sizeof(uint16);
833          }
834          pos = info->pos;
835          ReadChunks(ReadSmoothing, info, object);
836          info->pos = pos;
837
838          if(info->matFaces)
839             info->matFaces.Free();
840          info->matFaces = { };
841
842          ReadChunks(ReadFacesListChunks, info, object);
843
844          ComputeNormals(mesh, info, object);
845
846          // Create Groups
847          for(m : info->matFaces)
848          {
849             Material mat = (Material)&m;
850             Array<int> faces = m;
851             if(mat.flags.translucent)
852             {
853                mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
854                for(i : faces)
855                {
856                   Face * face = &info->faces[i];
857                   PrimitiveSingle * triangle;
858
859                   triangle = &mesh.primitives[mesh.nPrimitives++];
860                   if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
861                   {
862                      triangle->indicesMember[0] = (uintindex)face->indices[0];
863                      triangle->indicesMember[1] = (uintindex)face->indices[1];
864                      triangle->indicesMember[2] = (uintindex)face->indices[2];
865                      triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
866                      triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
867                      triangle->plane.FromPointsf(
868                         mesh.vertices[triangle->indicesMember[2]],
869                         mesh.vertices[triangle->indicesMember[1]],
870                         mesh.vertices[triangle->indicesMember[0]]);
871
872                      mesh.UnlockPrimitive(triangle);
873                   }
874                   triangle->middle.x /= 3;
875                   triangle->middle.y /= 3;
876                   triangle->middle.z /= 3;
877
878                   triangle->material = mat;
879
880                   face->done = true;
881                   object.flags.translucent = true;
882                }
883             }
884             else
885             {
886                PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
887                if(group)
888                {
889                   c = 0;
890                   group.material = mat;
891                   for(i : faces)
892                   {
893                      Face * face = &info->faces[i];
894
895                      if(object.flags.flipWindings)
896                      {
897                         group.indicesMember[c*3]   = (uintindex)face->indices[2];
898                         group.indicesMember[c*3+1] = (uintindex)face->indices[1];
899                         group.indicesMember[c*3+2] = (uintindex)face->indices[0];
900                      }
901                      else
902                      {
903                         group.indicesMember[c*3]   = (uintindex)face->indices[0];
904                         group.indicesMember[c*3+1] = (uintindex)face->indices[1];
905                         group.indicesMember[c*3+2] = (uintindex)face->indices[2];
906                      }
907                      face->done = true;
908                      c++;
909                   }
910                   mesh.UnlockPrimitiveGroup(group);
911                }
912             }
913          }
914
915          // Add faces without a material all together
916          count = 0;
917          for(c = 0; c<nFaces; c++)
918             if(!info->faces[c].done)
919                count++;
920          if(count)
921          {
922             PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
923             if(group)
924             {
925                for(c = 0; c<nFaces; c++)
926                {
927                   Face * face = &info->faces[c];
928                   if(!face->done)
929                   {
930                      group.indicesMember[c*3]   = (uintindex)face->indices[0];
931                      group.indicesMember[c*3+1] = (uintindex)face->indices[1];
932                      group.indicesMember[c*3+2] = (uintindex)face->indices[2];
933                   }
934                }
935                mesh.UnlockPrimitiveGroup(group);
936             }
937          }
938
939          delete info->faces;
940          if(info->matFaces)
941          {
942             info->matFaces.Free();
943             delete info->matFaces;
944          }
945          break;
946       }
947       case TRI_LOCAL:
948       {
949          int c;
950          Vector3Df xAxis, yAxis, zAxis, center;
951          Vector3Df scaling;
952          Vector3Df orth;
953          Matrix inverse/*, source = { 0 }*/;
954
955          // Local Axes
956          Read3DVertex(info->f, xAxis);
957          Read3DVertex(info->f, yAxis);
958          Read3DVertex(info->f, zAxis);
959          Read3DVertex(info->f, center);
960
961          scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
962          scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
963          scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
964
965          // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
966 /*
967          source.m[3][3] = 1;
968          source.m[0][0] = xAxis.x; source.m[0][1]  = -xAxis.z;  source.m[0][2] = xAxis.y;
969          source.m[1][0] =-zAxis.x; source.m[1][1]  = zAxis.z;  source.m[1][2] = -zAxis.y;
970          source.m[2][0] = yAxis.x; source.m[2][1]  = -yAxis.z;  source.m[2][2] = yAxis.y;
971          source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
972
973          inverse.Inverse(source);
974 */
975
976          object.flags.flipWindings = false;
977
978          xAxis.Normalize(xAxis);
979          yAxis.Normalize(yAxis);
980          zAxis.Normalize(zAxis);
981
982          orth.CrossProduct(yAxis, zAxis);
983          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
984             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
985             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
986          {
987             object.flags.flipWindings ^= true;
988             xAxis = orth;
989          }
990
991          orth.CrossProduct(zAxis, xAxis);
992          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
993             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
994             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
995          {
996             object.flags.flipWindings ^= true;
997             yAxis = orth;
998          }
999
1000          orth.CrossProduct(xAxis, yAxis);
1001          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
1002             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
1003             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
1004          {
1005             object.flags.flipWindings ^= true;
1006             zAxis = orth;
1007          }
1008
1009          {
1010             Matrix rotation;
1011
1012             rotation.m[0][0] = xAxis.x;
1013             rotation.m[0][1] =-xAxis.z;
1014             rotation.m[0][2] = xAxis.y;
1015             rotation.m[0][3] = 0;
1016             rotation.m[1][0] =-zAxis.x;
1017             rotation.m[1][1] = zAxis.z;
1018             rotation.m[1][2] =-zAxis.y;
1019             rotation.m[1][3] = 0;
1020             rotation.m[2][0] = yAxis.x;
1021             rotation.m[2][1] =-yAxis.z;
1022             rotation.m[2][2] = yAxis.y;
1023             rotation.m[2][3] = 0;
1024             rotation.m[3][0] = 0;
1025             rotation.m[3][1] = 0;
1026             rotation.m[3][2] = 0;
1027             rotation.m[3][3] = 1;
1028
1029             {
1030                Matrix temp, temp2;
1031                inverse.Transpose(rotation);
1032                temp.Identity();
1033                temp.Translate(-center.x, center.z, -center.y);
1034                temp2.Multiply(temp, inverse);
1035                temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
1036                inverse = temp2;
1037             }
1038
1039             object.transform.scaling = scaling;
1040             // TODO: Improve language to support deep properties on non constant functions
1041             // object.transform.orientation.RotationMatrix(rotation);
1042             {
1043                Quaternion orientation;
1044                orientation.RotationMatrix(rotation);
1045                object.transform.orientation = orientation;
1046             }
1047             object.transform.position = { center.x, -center.z, center.y };
1048          }
1049
1050          // Localize All Vertices
1051          for(c = 0; c<mesh.nVertices; c++)
1052          {
1053             Vector3Df vertex = mesh.vertices[c];
1054
1055             mesh.vertices[c].MultMatrix(vertex, inverse);
1056
1057             mesh.vertices[c].x -= object.pivot.x;
1058             mesh.vertices[c].y -= object.pivot.y;
1059             mesh.vertices[c].z -= object.pivot.z;
1060          }
1061          break;
1062       }
1063    }
1064    return true;
1065 }
1066
1067 // Material Library
1068 static bool ReadMap(FileInfo * info, Material mat)
1069 {
1070    DisplaySystem displaySystem = info->displaySystem;
1071    switch(info->chunkId)
1072    {
1073       case MAP_FILENAME:
1074       {
1075          char * name;
1076          char location[MAX_LOCATION];
1077          char bumpName[MAX_LOCATION+5];
1078
1079          ReadASCIIZ(info->f, &name);
1080
1081          strcpy(location, info->textureDirectory);
1082          PathCat(location, name);
1083          if(!FileExists(location))
1084          {
1085             // Attempt all lowercase if original case does not exist
1086             strlwr(name);
1087             strcpy(location, info->textureDirectory);
1088             PathCat(location, name);
1089          }
1090          strcpy(bumpName, "BUMP:");
1091          strcat(bumpName, location);
1092
1093          if(info->parent->chunkId == MAT_BUMPMAP)
1094          {
1095             // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1096             if(displaySystem.GetTexture(location))
1097                mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map...
1098             else if(!mat.bumpMap)
1099             {
1100                mat.bumpMap = displaySystem.GetTexture(bumpName);
1101                if(!mat.bumpMap)
1102                {
1103                   mat.bumpMap = Bitmap { };
1104                   if(!mat.bumpMap.Load(location, null, null) ||
1105                      !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1106                      !displaySystem.AddTexture(bumpName, mat.bumpMap))
1107                      delete mat.bumpMap;
1108                   if(mat.bumpMap)
1109                   {
1110                      ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1111                      int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1112                      int y, x;
1113
1114                      for(y = 0; y < bh; y++)
1115                         for(x = 0; x < bw; x++)
1116                         {
1117                            uint bc = y * bw + x;
1118                            Color color = picture[bc].color;
1119                            picture[bc] = { 255, { color.r, color.g, color.b } };
1120                         }
1121                   }
1122                }
1123             }
1124          }
1125          else if(info->parent->chunkId == MAT_SPECULARMAP)
1126          {
1127             // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1128             char specName[MAX_LOCATION+5];
1129             strcpy(specName, "SPEC:");
1130             strcat(specName, location);
1131             if(!mat.specularMap)
1132             {
1133                mat.specularMap = displaySystem.GetTexture(specName);
1134                if(!mat.specularMap)
1135                {
1136                   mat.specularMap = Bitmap { };
1137                   if(!mat.specularMap.Load(location, null, null) ||
1138                      !mat.specularMap.Convert(null, pixelFormat888, null) ||
1139                      !displaySystem.AddTexture(specName, mat.specularMap))
1140                      delete mat.specularMap;
1141                   if(mat.specularMap)
1142                   {
1143                      ColorAlpha * picture = (ColorAlpha *)mat.specularMap.picture;
1144                      int bw = mat.specularMap.width, bh = mat.specularMap.height;
1145                      int y, x;
1146
1147                      for(y = 0; y < bh; y++)
1148                         for(x = 0; x < bw; x++)
1149                         {
1150                            uint bc = y * bw + x;
1151                            Color color = picture[bc].color;
1152                            picture[bc] = { 255, { color.r, color.g, color.b } };
1153                         }
1154                   }
1155                }
1156             }
1157          }
1158          else
1159          {
1160             Bitmap opacityMap = null;
1161             bool alphaOnly = true;
1162             bool translucent = false;
1163             if(!mat.baseMap)
1164             {
1165                mat.baseMap = displaySystem.GetTexture(location);
1166                if(!mat.baseMap)
1167                {
1168                   mat.baseMap = Bitmap { };
1169                   if(displaySystem.GetTexture(bumpName))
1170                   {
1171                      mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map...
1172                   }
1173                   if(!mat.baseMap.Load(location, null, null) ||
1174                      !mat.baseMap.Convert(null, pixelFormat888, null) ||
1175                      !displaySystem.AddTexture(location, mat.baseMap))
1176                   {
1177                      delete mat.baseMap;
1178                   }
1179                   opacityMap = mat.baseMap;
1180                }
1181             }
1182             else if(info->parent->chunkId == MAT_MAPOPACITY)
1183             {
1184                opacityMap = Bitmap { };
1185                if(opacityMap.Load(location, null, null))
1186                {
1187                   if(opacityMap.pixelFormat == pixelFormatRGBA)
1188                      alphaOnly = false;
1189                   if(!opacityMap.Convert(null, pixelFormat888, null))
1190                      delete opacityMap;
1191                }
1192             }
1193
1194             if(mat.baseMap)
1195             {
1196                if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1197                {
1198                   ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1199                   ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1200                   uint x, y;
1201                   int ow = opacityMap.width, oh = opacityMap.height;
1202                   int bw = mat.baseMap.width, bh = mat.baseMap.height;
1203
1204                   for(y = 0; y < bh; y++)
1205                      for(x = 0; x < bw; x++)
1206                      {
1207                         int bc = ((y % bh) * bw + (x % bw));
1208                         int oc = ((y % oh) * bw + (x % ow));
1209                         byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1210                         if(alpha && alpha < 255)
1211                            translucent = true;
1212                         picture[bc] = ColorAlpha { alpha, picture[bc].color };
1213                      }
1214                }
1215                if(translucent)
1216                   mat.flags.translucent = true;
1217                mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1218                mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1219             }
1220             if(opacityMap != mat.baseMap)
1221                delete opacityMap;
1222          }
1223          delete name;
1224          break;
1225       }
1226       case MAP_OPTIONS:
1227       {
1228          MapOptions options = (MapOptions)ReadWORD(info->f);
1229          if(!options.dontTile) mat.flags.tile = true;
1230          break;
1231       }
1232       case MAP_1_U_SCALE:
1233          mat.uScale = ReadFloat(info->f);
1234          break;
1235       case MAP_1_V_SCALE:
1236          mat.vScale = ReadFloat(info->f);
1237          break;
1238       /*
1239       case AMOUNT_OF:
1240          break;
1241       default:
1242          PrintLn("Unhandled Map block");
1243          break;
1244       */
1245    }
1246    return true;
1247 }
1248
1249 static bool ReadMaterial(FileInfo * info, Material mat)
1250 {
1251    switch(info->chunkId)
1252    {
1253       case MAT_NAME:
1254       {
1255          String name;
1256          char matName[MAX_LOCATION + 100];
1257          strcpy(matName, info->fileName);
1258          ReadASCIIZ(info->f, &name);
1259          strcat(matName, name);
1260          mat.name = CopyString(matName);
1261          delete name;
1262          break;
1263       }
1264       case MAT_TRANSPARENCY:
1265       {
1266          uint16 transparency;
1267          ReadChunks(ReadAmountOf, info, &transparency);
1268          mat.opacity = 1.0f - transparency / 100.0f;
1269          if(mat.opacity < 1.0)
1270             mat.flags.translucent = true;
1271          break;
1272       }
1273       case MAT_DIFFUSE:
1274       {
1275          ReadChunks(ReadRGB, info, &mat.diffuse);
1276          ReadChunks(ReadRGB, info, &mat.diffuse);
1277          break;
1278       }
1279       case MAT_AMBIENT:
1280       {
1281          ReadChunks(ReadRGB, info, &mat.ambient);
1282          ReadChunks(ReadRGB, info, &mat.ambient);
1283          break;
1284       }
1285       case MAT_SPECULAR:
1286       {
1287          ReadChunks(ReadRGB, info, &mat.specular);
1288          ReadChunks(ReadRGB, info, &mat.specular);
1289          break;
1290       }
1291       case MAT_SELFILLUM:
1292       {
1293          uint16 emissive;
1294          ReadChunks(ReadAmountOf, info, &emissive);
1295          mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1296          mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1297          mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1298          break;
1299       }
1300       case MAT_SHINSTRENGTH:
1301       {
1302          uint16 shininess;
1303          ReadChunks(ReadAmountOf, info, &shininess);
1304          mat.reflectivity = shininess / 100.0f;
1305          /*
1306          mat.specular.r *= shininess / 100.0f;
1307          mat.specular.g *= shininess / 100.0f;
1308          mat.specular.b *= shininess / 100.0f;
1309          */
1310          break;
1311       }
1312       case MAT_SHININESS:
1313       {
1314          uint16 power;
1315          ReadChunks(ReadAmountOf, info, &power);
1316          mat.power = power;
1317          break;
1318       }
1319       case MAT_MAPTEXTURE1:
1320          ReadChunks(ReadMap, info, mat);
1321          break;
1322       case MAT_MAPOPACITY:
1323          ReadChunks(ReadMap, info, mat);
1324          break;
1325       case MAT_DOUBLESIDED:
1326          mat.flags.doubleSided = true;
1327          break;
1328       case MAT_BUMPMAP:
1329          ReadChunks(ReadMap, info, mat);
1330          break;
1331       /*
1332       default:
1333          PrintLn("Unhandled MAT type ID", info->chunkId);
1334       */
1335    }
1336    return true;
1337 }
1338
1339 // Lights
1340 static bool ReadLight(FileInfo * info, Object object)
1341 {
1342    Light * light = &object.light;
1343    switch(info->chunkId)
1344    {
1345       case RGB_BYTE:
1346       case RGB_BYTE_GAMMA:
1347       case RGB_FLOAT:
1348       case RGB_FLOAT_GAMMA:
1349          ReadRGB(info, &light->diffuse);
1350          light->specular = light->diffuse;
1351          break;
1352       case LIT_SPOT:
1353       {
1354          Object target;
1355          char targetName[MAXNAMELEN];
1356
1357          strcpy(targetName, object.name);
1358          strcat(targetName, ".target");
1359
1360          light->flags.omni = false;
1361          light->flags.spot = true;
1362
1363          target = Object { };
1364          target.name = CopyString(targetName);
1365          info->rootObject.children.AddName(target);
1366          target.parent = info->rootObject;
1367
1368          light->target = target;
1369
1370          target.transform.position.x = ReadFloat(info->f);
1371          target.transform.position.z = ReadFloat(info->f);
1372          target.transform.position.y =-ReadFloat(info->f);
1373
1374          light->hotSpot = ReadFloat(info->f);
1375          light->fallOff = ReadFloat(info->f);
1376          break;
1377       }
1378       case LIT_ONOFF:
1379       {
1380          light->flags.off = true;
1381          break;
1382       }
1383       case LIT_ATTENUATION:
1384       {
1385          /* solve (
1386             {
1387                d = 300, small = 0.001,
1388                Kl = 0, Kc = 0,
1389                d * (Kl + Kq * d) = (1 / small) - Kc
1390             },
1391             { Kc, Kl, Kq, small, d });
1392          */
1393
1394          /*
1395          #define MINLIGHT     0.08
1396          light->Kq = 1/(light->end*light->end*MINLIGHT);
1397          */
1398
1399          #define MINLIGHT     0.15f
1400          // #define MINLIGHT     0.1
1401          //light->Kl = (float)(1/(light->end*MINLIGHT));
1402
1403          float c = 1.0, l = 0.005, q = 0.0005, small = 500, end = light->end;
1404          light->Kc = (c + l * end + q * end * end) / small;
1405          light->Kl = light->Kc * l/c;
1406          light->Kq = light->Kc * q/c;
1407          light->flags.attenuation = true;
1408          break;
1409       }
1410       case LIT_START:
1411       {
1412          light->start = ReadFloat(info->f);
1413          break;
1414       }
1415       case LIT_END:
1416       {
1417          light->end = ReadFloat(info->f);
1418          break;
1419       }
1420       case LIT_MULTIPLIER:
1421       {
1422          light->multiplier = ReadFloat(info->f);
1423          break;
1424       }
1425    }
1426    return true;
1427 }
1428
1429 // Cameras
1430 static bool ReadCamera(FileInfo * info, Object object)
1431 {
1432    switch(info->chunkId)
1433    {
1434       case CAM_SEECONE:
1435       {
1436          break;
1437       }
1438       case CAM_RANGES:
1439       {
1440          //Camera camera = object.camera;
1441          /*float nearRange = */ReadFloat(info->f);
1442          /*float farRange = */ReadFloat(info->f);
1443          /*
1444          camera.zMin = Max(0.1, nearRange);
1445          camera.zMax = farRange;
1446          */
1447          break;
1448       }
1449    }
1450    return true;
1451 }
1452
1453 // Edit Chunks
1454 static bool ReadEditObject(FileInfo * info, char * name)
1455 {
1456    DisplaySystem displaySystem = info->displaySystem;
1457    switch(info->chunkId)
1458    {
1459       case OBJ_TRIMESH:
1460       {
1461          Object object = info->rootObject.Find(name);
1462          if(!object)
1463          {
1464             object = Object { };
1465             object.name = CopyString(name);
1466             info->rootObject.children.AddName(object);
1467             object.parent = info->rootObject;
1468          }
1469          object.InitializeMesh(displaySystem);
1470          ReadChunks(ReadTriMesh, info, object);
1471          object.flags.mesh = true;
1472          object.mesh.Unlock(0);
1473          break;
1474       }
1475       case OBJ_LIGHT:
1476       {
1477          Object object = info->rootObject.Find(name);
1478          Light * light;
1479          Vector3Df position;
1480
1481          if(!object)
1482          {
1483             object = Object { };
1484             object.name = CopyString(name);
1485             info->rootObject.children.AddName(object);
1486             object.parent = info->rootObject;
1487          }
1488          object.flags.light = true;
1489
1490          light = &object.light;
1491          light->lightObject = object;
1492          light->flags.omni = true;
1493          light->multiplier = 1.0f;
1494
1495          // This is not used?
1496          Read3DVertex(info->f, position);
1497          light->direction = { position.x, position.y, position.z };
1498          info->pos += sizeof(float) * 3;
1499
1500          ReadChunks(ReadLight, info, object);
1501          break;
1502       }
1503       case OBJ_CAMERA:
1504       {
1505          char targetName[MAXNAMELEN];
1506          Object object = info->rootObject.Find(name);
1507          Object target;
1508          Camera camera;
1509          float /*bankAngle, */focus;
1510          double mm;
1511
1512          strcpy(targetName, name);
1513          strcat(targetName, ".target");
1514          target = info->rootObject.Find(targetName);
1515
1516          if(!object)
1517          {
1518             object = Object { };
1519             object.name = CopyString(name);
1520             info->rootObject.children.AddName(object);
1521
1522             object.parent = info->rootObject;
1523             object.camera = Camera { };
1524             object.camera.type = lookAtObject;
1525          }
1526
1527          if(!target)
1528          {
1529             target = Object { };
1530             target.name = CopyString(targetName);
1531             info->rootObject.children.AddName(target);
1532             target.parent = info->rootObject;
1533          }
1534
1535          object.flags.camera = true;
1536          object.cameraTarget = target;
1537
1538          camera = object.camera;
1539          camera.cameraObject = object;
1540          camera.target = target;
1541
1542          //Read3DVertex(info->f, camera.position);
1543          object.transform.position.x = ReadFloat(info->f);
1544          object.transform.position.z = ReadFloat(info->f);
1545          object.transform.position.y =-ReadFloat(info->f);
1546
1547          info->pos += sizeof(float) * 3;
1548          //Read3DVertex(info->f, object.cameraTarget.position);
1549          target.transform.position.x = ReadFloat(info->f);
1550          target.transform.position.z = ReadFloat(info->f);
1551          target.transform.position.y =-ReadFloat(info->f);
1552
1553          info->pos += sizeof(float) * 3;
1554          /*bankAngle = */ReadFloat(info->f);
1555          info->pos += sizeof(float);
1556          focus = ReadFloat(info->f);
1557          info->pos += sizeof(float);
1558
1559          mm = (focus - 5.05659508373109) / 1.13613250717301;
1560          camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1561
1562          ReadChunks(ReadCamera, info, object);
1563          break;
1564       }
1565       case OBJ_HIDDEN: break;
1566    }
1567    return true;
1568 }
1569
1570 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1571
1572 static bool ReadEditChunks(FileInfo * info, void * data)
1573 {
1574    switch(info->chunkId)
1575    {
1576       case EDIT_AMBIENT:
1577       {
1578          // Read the ambient color
1579          ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1580          break;
1581       }
1582       case EDIT_MATERIAL:
1583       {
1584          Material material { /*flags = { singleSideLight = true }*/ };
1585          Material mat;
1586          ReadChunks(ReadMaterial, info, material);
1587
1588          mat = info->displaySystem.AddNamedMaterial(material.name);
1589          if(mat)
1590          {
1591             if(material.baseMap)
1592                material.baseMap.MakeMipMaps(info->displaySystem);
1593             if(material.bumpMap)
1594                material.bumpMap.MakeMipMaps(info->displaySystem);
1595             if(material.specularMap)
1596                material.specularMap.MakeMipMaps(info->displaySystem);
1597
1598             // COPY_NITEM(mat, material);
1599             CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1600          }
1601          else
1602          {
1603             delete material.baseMap;
1604          }
1605          delete material.name;
1606          delete material;
1607          break;
1608       }
1609       case EDIT_OBJECT:
1610       {
1611          char * name;
1612          info->pos += ReadASCIIZ(info->f, &name);
1613          ReadChunks(ReadEditObject, info, name);
1614          delete name;
1615          break;
1616       }
1617    }
1618    return true;
1619 }
1620
1621 struct ObjectInfoBlock
1622 {
1623    OldList tracks;
1624    short hierarchy;
1625    short parent;
1626    char * name;
1627    char * dummyName;
1628    Vector3Df pivot;
1629 };
1630
1631 // Key Framer Chunks
1632 static class AccelerationFlags : uint32
1633 {
1634    bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
1635 };
1636
1637 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1638 {
1639    switch(info->chunkId)
1640    {
1641       case FRM_PARAM:
1642       {
1643          //uint16 flags1, flags2;
1644          ReadASCIIZ(info->f, &block->name);
1645          /*flags1 = */ReadWORD(info->f);
1646          /*flags2 = */ReadWORD(info->f);
1647          block->parent = ReadWORD(info->f);
1648          break;
1649       }
1650       case FRM_DUMMYNAME:
1651          ReadASCIIZ(info->f, &block->dummyName);
1652          break;
1653       case FRM_PIVOT:
1654          Read3DVertex(info->f, block->pivot);
1655          break;
1656       case FRM_HIERARCHY:
1657          block->hierarchy = ReadWORD(info->f);
1658          break;
1659       case FRM_TRACKPOS:
1660       case FRM_TRACKROT:
1661       case FRM_TRACKSCALE:
1662       case FRM_TRACKROLL:
1663       case FRM_TRACKFOV:
1664       case FRM_TRACKCOLOR:
1665       case FRM_TRACKHOTSPOT:
1666       case FRM_TRACKFALLOFF:
1667       {
1668          FrameTrack track { };
1669          if(track)
1670          {
1671             uint16 flags;
1672             byte unknown[8];
1673             uint c;
1674
1675             block->tracks.Add(track);
1676
1677             flags = ReadWORD(info->f);
1678
1679             info->f.Read(unknown, sizeof(unknown), 1);
1680
1681             track.numKeys = ReadDWORD(info->f);
1682
1683             switch(info->chunkId)
1684             {
1685                case FRM_TRACKPOS: track.type.type = position; break;
1686                case FRM_TRACKROT: track.type.type = rotation; break;
1687                case FRM_TRACKSCALE: track.type.type = scaling; break;
1688                case FRM_TRACKROLL: track.type.type = roll; break;
1689                case FRM_TRACKFOV: track.type.type = fov; break;
1690                case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1691                case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1692                case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1693             }
1694             if((flags & 0x0003) == 3)
1695                track.type.loop = true;
1696
1697             track.keys = new0 FrameKey[track.numKeys];
1698             for(c = 0; c<track.numKeys; c++)
1699             {
1700                AccelerationFlags accelerationFlags;
1701                FrameKey * key = track.keys + c;
1702
1703                key->frame = ReadDWORD(info->f);
1704                accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
1705
1706                if(accelerationFlags.tension)
1707                   key->tension = ReadFloat(info->f);
1708                if(accelerationFlags.continuity)
1709                   key->continuity = ReadFloat(info->f);
1710                if(accelerationFlags.bias)
1711                   key->bias = ReadFloat(info->f);
1712                if(accelerationFlags.easeTo)
1713                   key->easeTo = ReadFloat(info->f);
1714                if(accelerationFlags.easeFrom)
1715                   key->easeFrom = ReadFloat(info->f);
1716
1717                switch(info->chunkId)
1718                {
1719                   case FRM_TRACKPOS:
1720                   {
1721                      Vector3Df position;
1722                      Read3DVertex(info->f, position);
1723                      key->position = { position.x, -position.z, position.y };
1724                      break;
1725                   }
1726                   case FRM_TRACKROT:
1727                   {
1728                      Vector3Df axis;
1729                      Angle angle = ReadFloat(info->f);
1730                      Vector3D fixedAxis;
1731
1732                      Read3DVertex(info->f, axis);
1733                      fixedAxis = { axis.x, -axis.z, axis.y };
1734
1735                      if(c > 0)
1736                      {
1737                         Quaternion rotation;
1738                         rotation.RotationAxis(fixedAxis, angle);
1739                         key->orientation.Multiply((key - 1)->orientation, rotation);
1740                      }
1741                      else
1742                         key->orientation.RotationAxis(fixedAxis, angle);
1743                      break;
1744                   }
1745                   case FRM_TRACKSCALE:
1746                   {
1747                      Vector3Df scaling;
1748                      Read3DVertex(info->f, scaling);
1749                      key->scaling = { scaling.x, scaling.z, scaling.y };
1750                      break;
1751                   }
1752                   case FRM_TRACKFOV:
1753                   {
1754                      key->fov = ReadFloat(info->f);
1755                      break;
1756                   }
1757                   case FRM_TRACKROLL:
1758                   {
1759                      key->roll = -ReadFloat(info->f);
1760                      break;
1761                   }
1762                   case FRM_TRACKCOLOR:
1763                   {
1764                      FileInfo childInfo = *info;
1765                      childInfo.chunkId = RGB_FLOAT;
1766                      ReadRGB(&childInfo, &key->color);
1767                      break;
1768                   }
1769                   case FRM_TRACKHOTSPOT:
1770                   {
1771                      key->hotSpot = ReadFloat(info->f);
1772                      break;
1773                   }
1774                   case FRM_TRACKFALLOFF:
1775                   {
1776                      key->fallOff = ReadFloat(info->f);
1777                      break;
1778                   }
1779                }
1780             }
1781          }
1782          break;
1783       }
1784    }
1785    return true;
1786 }
1787
1788 static Object FindObjectID(Object object, int id)
1789 {
1790    Object result = null;
1791    Object child;
1792    for(child = object.children.first; child; child = child.next)
1793    {
1794       if(child.flags.hierarchy == (uint16) id)
1795       {
1796          result = child;
1797          break;
1798       }
1799       else
1800       {
1801          result = FindObjectID(child, id);
1802          if(result) break;
1803       }
1804    }
1805    return result;
1806 }
1807
1808 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1809 {
1810    switch(info->chunkId)
1811    {
1812       case FRM_MESHINFO:
1813       {
1814          ObjectInfoBlock block { };
1815          Object object = null;
1816
1817          ReadChunks(ReadFrameInfoBlock, info, &block);
1818
1819          if(block.dummyName && block.dummyName[0])
1820          {
1821             if(!strcmp(block.name, "$$$DUMMY"))
1822             {
1823                object = Object { };
1824                object.name = block.dummyName;
1825                info->rootObject.children.AddName(object);
1826                object.transform.scaling = { 1,1,1 };
1827             }
1828             else
1829             {
1830                Object model = info->rootObject.Find(block.name);
1831                if(model)
1832                {
1833                   object = Object { };
1834                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1835                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1836                   object.flags = model.flags;
1837                   object.flags.ownMesh = false;
1838                   object.mesh = model.mesh;
1839                   /*
1840                   object.min = model.min;
1841                   object.max = model.max;
1842                   object.radius = model.radius;
1843                   */
1844                   object.transform = model.transform;
1845                   info->rootObject.children.AddName(object);
1846                }
1847                delete block.dummyName;
1848             }
1849             if(object)
1850                object.parent = info->rootObject;
1851          }
1852          else
1853             object = info->rootObject.Find(block.name);
1854
1855          if(object)
1856          {
1857             Mesh mesh = object.mesh;
1858             object.flags.hierarchy = block.hierarchy + 1;
1859             if(block.parent != -1)
1860             {
1861                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1862                if(parent)
1863                {
1864                   object.parent.children.Remove(object);
1865                   parent.children.AddName(object);
1866                   object.parent = parent;
1867                }
1868             }
1869             object.pivot.x = block.pivot.x;
1870             object.pivot.y =-block.pivot.z;
1871             object.pivot.z = block.pivot.y;
1872
1873             if(mesh && object.flags.ownMesh)
1874             {
1875                if(mesh.Lock({ vertices = true }))
1876                {
1877                   int c;
1878                   // Take pivot into account
1879                   for(c = 0; c<mesh.nVertices; c++)
1880                   {
1881                      mesh.vertices[c].x -= object.pivot.x;
1882                      mesh.vertices[c].y -= object.pivot.y;
1883                      mesh.vertices[c].z -= object.pivot.z;
1884                   }
1885                   mesh.Unlock({ vertices = true });
1886                }
1887             }
1888
1889             object.tracks = block.tracks;
1890             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1891             object.endFrame = info->rootObject.endFrame;
1892          }
1893          /*else
1894             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1895          delete block.name;
1896          break;
1897       }
1898       case FRM_CAMERA:
1899       {
1900          ObjectInfoBlock block { };
1901          Object object = null;
1902
1903          ReadChunks(ReadFrameInfoBlock, info, &block);
1904
1905          if(block.dummyName && block.dummyName[0])
1906          {
1907             if(!strcmp(block.name, "$$$DUMMY"))
1908             {
1909                object = Object { };
1910                object.name = block.dummyName;
1911                info->rootObject.children.AddName(object);
1912                object.transform.scaling = { 1, 1, 1 };
1913                object.flags.camera = true;
1914             }
1915             else
1916             {
1917                Object model = info->rootObject.Find(block.name);
1918                if(model)
1919                {
1920                   object = Object { };
1921                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1922                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1923                   object.flags = model.flags;
1924                   object.flags.ownMesh = false;
1925                   object.camera = model.camera;
1926                   object.flags.camera = true;
1927                   info->rootObject.children.AddName(object);
1928                }
1929                delete block.dummyName;
1930             }
1931             if(object)
1932                object.parent = info->rootObject;
1933          }
1934          else
1935             object = info->rootObject.Find(block.name);
1936
1937          if(object)
1938          {
1939             object.flags.hierarchy = block.hierarchy + 1;
1940             if(block.parent != -1)
1941             {
1942                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1943                if(parent)
1944                {
1945                   object.parent.children.Remove(object);
1946                   parent.children.AddName(object);
1947                   object.parent = parent;
1948                }
1949             }
1950             object.pivot.x = block.pivot.x;
1951             object.pivot.y =-block.pivot.z;
1952             object.pivot.z = block.pivot.y;
1953
1954             object.tracks = block.tracks;
1955             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1956             object.endFrame = info->rootObject.endFrame;
1957          }
1958          /*else
1959             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1960          delete block.name;
1961          break;
1962       }
1963       case FRM_CAMERATARGET:
1964       {
1965          ObjectInfoBlock block { };
1966          Object object = null;
1967          char targetName[MAXNAMELEN];
1968
1969          ReadChunks(ReadFrameInfoBlock, info, &block);
1970
1971          strcpy(targetName, block.name);
1972          strcat(targetName, ".target");
1973
1974          if(block.dummyName && block.dummyName[0])
1975          {
1976             if(!strcmp(block.name, "$$$DUMMY"))
1977             {
1978                object = Object { };
1979                object.name = block.dummyName;
1980                info->rootObject.children.AddName(object);
1981                object.transform.scaling = { 1,1,1 };
1982             }
1983             else
1984             {
1985                Object model = info->rootObject.Find(targetName);
1986                if(model)
1987                {
1988                   object = Object { };
1989                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1990                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1991                   object.flags = model.flags;
1992                   object.flags.ownMesh = false;
1993                   object.camera = model.camera;
1994                   info->rootObject.children.AddName(object);
1995                }
1996                delete block.dummyName;
1997             }
1998             if(object)
1999                object.parent = info->rootObject;
2000          }
2001          else
2002             object = info->rootObject.Find(targetName);
2003
2004          if(object)
2005          {
2006             object.flags.hierarchy = block.hierarchy + 1;
2007             if(block.parent != -1)
2008             {
2009                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2010                if(parent)
2011                {
2012                   object.parent.children.Remove(object);
2013                   parent.children.AddName(object);
2014                   object.parent = parent;
2015                }
2016             }
2017             object.pivot.x = block.pivot.x;
2018             object.pivot.y =-block.pivot.z;
2019             object.pivot.z = block.pivot.y;
2020
2021             object.tracks = block.tracks;
2022             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2023             object.endFrame = info->rootObject.endFrame;
2024          }
2025          /*else
2026             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2027          delete block.name;
2028          break;
2029       }
2030       case FRM_AMBIENT:
2031       {
2032          ObjectInfoBlock block { };
2033
2034          ReadChunks(ReadFrameInfoBlock, info, &block);
2035
2036          info->rootObject.tracks = block.tracks;
2037          break;
2038       }
2039       case FRM_OMNILIGHT:
2040       case FRM_SPOTLIGHT:
2041       {
2042          ObjectInfoBlock block { };
2043          Object object = null;
2044
2045          ReadChunks(ReadFrameInfoBlock, info, &block);
2046
2047          if(block.dummyName && block.dummyName[0])
2048          {
2049             if(!strcmp(block.name, "$$$DUMMY"))
2050             {
2051                object = Object { };
2052                object.name = block.dummyName;
2053                info->rootObject.children.AddName(object);
2054                object.transform.scaling = { 1, 1, 1 };
2055                object.flags.light = true;
2056             }
2057             else
2058             {
2059                Object model = info->rootObject.Find(block.name);
2060                if(model)
2061                {
2062                   object = Object { };
2063                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2064                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
2065                   object.flags = model.flags;
2066                   object.flags.ownMesh = false;
2067                   object.light = model.light;
2068                   object.flags.light = true;
2069                   info->rootObject.children.AddName(object);
2070                }
2071                delete block.dummyName;
2072             }
2073             if(object)
2074                object.parent = info->rootObject;
2075          }
2076          else
2077             object = info->rootObject.Find(block.name);
2078
2079          if(object)
2080          {
2081             object.flags.hierarchy = block.hierarchy + 1;
2082             if(block.parent != -1)
2083             {
2084                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2085                if(parent)
2086                {
2087                   object.parent.children.Remove(object);
2088                   parent.children.AddName(object);
2089                   object.parent = parent;
2090                }
2091             }
2092             object.pivot.x = block.pivot.x;
2093             object.pivot.y =-block.pivot.z;
2094             object.pivot.z = block.pivot.y;
2095
2096             object.tracks = block.tracks;
2097             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2098             object.endFrame = info->rootObject.endFrame;
2099          }
2100          /*else
2101             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2102          delete block.name;
2103          break;
2104       }
2105       case FRM_SPOTLIGHTTARGET:
2106       {
2107          ObjectInfoBlock block { };
2108          Object object = null;
2109          char targetName[MAXNAMELEN];
2110
2111          ReadChunks(ReadFrameInfoBlock, info, &block);
2112
2113          strcpy(targetName, block.name);
2114          strcat(targetName, ".target");
2115
2116          if(block.dummyName && block.dummyName[0])
2117          {
2118             if(!strcmp(block.name, "$$$DUMMY"))
2119             {
2120                object = Object { };
2121                object.name = block.dummyName;
2122                info->rootObject.children.AddName(object);
2123                object.transform.scaling = { 1,1,1 };
2124             }
2125             else
2126             {
2127                Object model = info->rootObject.Find(targetName);
2128                if(model)
2129                {
2130                   object = Object { };
2131                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2132                   // TODO: When passing a String to a const String, use member if property is const String but member is String
2133                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
2134                   object.flags = model.flags;
2135                   object.flags.ownMesh = false;
2136                   object.light = model.light;
2137                   info->rootObject.children.AddName(object);
2138                }
2139                delete block.dummyName;
2140             }
2141             if(object)
2142                object.parent = info->rootObject;
2143          }
2144          else
2145             object = info->rootObject.Find(targetName);
2146
2147          if(object)
2148          {
2149             object.flags.hierarchy = block.hierarchy + 1;
2150             if(block.parent != -1)
2151             {
2152                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2153                if(parent)
2154                {
2155                   object.parent.children.Remove(object);
2156                   parent.children.AddName(object);
2157                   object.parent = parent;
2158                }
2159             }
2160             object.pivot.x = block.pivot.x;
2161             object.pivot.y =-block.pivot.z;
2162             object.pivot.z = block.pivot.y;
2163
2164             object.tracks = block.tracks;
2165             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2166             object.endFrame = info->rootObject.endFrame;
2167          }
2168          /*else
2169             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2170          delete block.name;
2171          break;
2172       }
2173       case FRM_FRAMES:
2174       {
2175          info->rootObject.startFrame = ReadDWORD(info->f);
2176          info->rootObject.endFrame = ReadDWORD(info->f);
2177          *&(info->rootObject.frame) = info->rootObject.startFrame;
2178          break;
2179       }
2180    }
2181    return true;
2182 }
2183
2184 // Main Chunks
2185 static bool ReadMainChunks(FileInfo * info, void * data)
2186 {
2187    switch(info->chunkId)
2188    {
2189       case EDIT3DS:
2190          ReadChunks(ReadEditChunks, info, null);
2191          break;
2192       case KEYFRAME3DS:
2193       {
2194          Object object = data;
2195          if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2196          {
2197             ReadChunks(ReadKeyFrameChunks, info, null);
2198             object.flags.keysLoaded = true;
2199          }
2200          break;
2201       }
2202    }
2203    return true;
2204 }
2205
2206 static bool ReadMain(FileInfo * info, void * data)
2207 {
2208    switch(info->chunkId)
2209    {
2210       case MAIN3DS:
2211          ReadChunks(ReadMainChunks, info, data);
2212          break;
2213    }
2214    return true;
2215 }
2216
2217 class Object3DSFormat : ObjectFormat
2218 {
2219    class_property(extension) = "3ds";
2220
2221    bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2222    {
2223       bool result = false;
2224       if(fileName)
2225       {
2226          FileInfo info = {0};
2227          info.rootObject = object;
2228          info.displaySystem = displaySystem;
2229          info.pos = 0;
2230          info.end = 1;
2231          info.fileName = fileName;
2232          StripLastDirectory(fileName, info.textureDirectory);
2233          info.f = FileOpen(fileName, read);
2234          if(info.f)
2235          {
2236             // TOFIX: eC reorders that badly
2237             // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2238             if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
2239             {
2240                object.flags.root = true;
2241                object.SetMinMaxRadius(true);
2242                object._Animate(object.frame);
2243                object.UpdateTransform();
2244                result = true;
2245             }
2246             delete info.f;
2247          }
2248          if(info.matFaces)
2249             info.matFaces.Free();
2250          delete info.matFaces;
2251       }
2252       if(!result)
2253          object.Free(displaySystem);
2254       return result;
2255    }
2256 }