4e8b972afaf7f529a0c49651d879d9db74edad5a
[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
1078          ReadASCIIZ(info->f, &name);
1079
1080          strcpy(location, info->textureDirectory);
1081          PathCat(location, name);
1082          if(!FileExists(location))
1083          {
1084             // Attempt all lowercase if original case does not exist
1085             strlwr(name);
1086             strcpy(location, info->textureDirectory);
1087             PathCat(location, name);
1088          }
1089
1090          if(info->parent->chunkId == MAT_BUMPMAP)
1091          {
1092             // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1093             char bumpName[MAX_LOCATION+5];
1094             strcpy(bumpName, "BUMP:");
1095             strcat(bumpName, location);
1096             if(!mat.bumpMap)
1097             {
1098                mat.bumpMap = displaySystem.GetTexture(bumpName);
1099                if(!mat.bumpMap)
1100                {
1101                   mat.bumpMap = Bitmap { };
1102                   if(!mat.bumpMap.Load(location, null, null) ||
1103                      !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1104                      !displaySystem.AddTexture(bumpName, mat.bumpMap))
1105                      delete mat.bumpMap;
1106                   if(mat.bumpMap)
1107                   {
1108                      ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1109                      int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1110                      int y, x;
1111
1112                      for(y = 0; y < bh; y++)
1113                         for(x = 0; x < bw; x++)
1114                         {
1115                            uint bc = y * bw + x;
1116                            Color color = picture[bc].color;
1117                            picture[bc] = { 255, { color.r, color.g, color.b } };
1118                         }
1119                   }
1120                }
1121             }
1122          }
1123          else if(info->parent->chunkId == MAT_SPECULARMAP)
1124          {
1125             // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1126             char specName[MAX_LOCATION+5];
1127             strcpy(specName, "SPEC:");
1128             strcat(specName, location);
1129             if(!mat.specularMap)
1130             {
1131                mat.specularMap = displaySystem.GetTexture(specName);
1132                if(!mat.specularMap)
1133                {
1134                   mat.specularMap = Bitmap { };
1135                   if(!mat.specularMap.Load(location, null, null) ||
1136                      !mat.specularMap.Convert(null, pixelFormat888, null) ||
1137                      !displaySystem.AddTexture(specName, mat.specularMap))
1138                      delete mat.specularMap;
1139                   if(mat.specularMap)
1140                   {
1141                      ColorAlpha * picture = (ColorAlpha *)mat.specularMap.picture;
1142                      int bw = mat.specularMap.width, bh = mat.specularMap.height;
1143                      int y, x;
1144
1145                      for(y = 0; y < bh; y++)
1146                         for(x = 0; x < bw; x++)
1147                         {
1148                            uint bc = y * bw + x;
1149                            Color color = picture[bc].color;
1150                            picture[bc] = { 255, { color.r, color.g, color.b } };
1151                         }
1152                   }
1153                }
1154             }
1155          }
1156          else
1157          {
1158             Bitmap opacityMap = null;
1159             bool alphaOnly = true;
1160             bool translucent = false;
1161             if(!mat.baseMap)
1162             {
1163                mat.baseMap = displaySystem.GetTexture(location);
1164                if(!mat.baseMap)
1165                {
1166                   mat.baseMap = Bitmap { };
1167                   if(!mat.baseMap.Load(location, null, null) ||
1168                      !mat.baseMap.Convert(null, pixelFormat888, null) ||
1169                      !displaySystem.AddTexture(location, mat.baseMap))
1170                   {
1171                      delete mat.baseMap;
1172                   }
1173                   opacityMap = mat.baseMap;
1174                }
1175             }
1176             else if(info->parent->chunkId == MAT_MAPOPACITY)
1177             {
1178                opacityMap = Bitmap { };
1179                if(opacityMap.Load(location, null, null))
1180                {
1181                   if(opacityMap.pixelFormat == pixelFormatRGBA)
1182                      alphaOnly = false;
1183                   if(!opacityMap.Convert(null, pixelFormat888, null))
1184                      delete opacityMap;
1185                }
1186             }
1187
1188             if(mat.baseMap)
1189             {
1190                if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1191                {
1192                   ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1193                   ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1194                   uint x, y;
1195                   int ow = opacityMap.width, oh = opacityMap.height;
1196                   int bw = mat.baseMap.width, bh = mat.baseMap.height;
1197
1198                   for(y = 0; y < bh; y++)
1199                      for(x = 0; x < bw; x++)
1200                      {
1201                         int bc = ((y % bh) * bw + (x % bw));
1202                         int oc = ((y % oh) * bw + (x % ow));
1203                         byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1204                         if(alpha && alpha < 255)
1205                            translucent = true;
1206                         picture[bc] = ColorAlpha { alpha, picture[bc].color };
1207                      }
1208                }
1209                if(translucent)
1210                   mat.flags.translucent = true;
1211                mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1212                mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1213             }
1214             if(opacityMap != mat.baseMap)
1215                delete opacityMap;
1216          }
1217          delete name;
1218          break;
1219       }
1220       case MAP_OPTIONS:
1221       {
1222          MapOptions options = (MapOptions)ReadWORD(info->f);
1223          if(!options.dontTile) mat.flags.tile = true;
1224          break;
1225       }
1226       case MAP_1_U_SCALE:
1227          mat.uScale = ReadFloat(info->f);
1228          break;
1229       case MAP_1_V_SCALE:
1230          mat.vScale = ReadFloat(info->f);
1231          break;
1232       /*
1233       case AMOUNT_OF:
1234          break;
1235       default:
1236          PrintLn("Unhandled Map block");
1237          break;
1238       */
1239    }
1240    return true;
1241 }
1242
1243 static bool ReadMaterial(FileInfo * info, Material mat)
1244 {
1245    switch(info->chunkId)
1246    {
1247       case MAT_NAME:
1248       {
1249          String name;
1250          char matName[MAX_LOCATION + 100];
1251          strcpy(matName, info->fileName);
1252          ReadASCIIZ(info->f, &name);
1253          strcat(matName, name);
1254          mat.name = CopyString(matName);
1255          delete name;
1256          break;
1257       }
1258       case MAT_TRANSPARENCY:
1259       {
1260          uint16 transparency;
1261          ReadChunks(ReadAmountOf, info, &transparency);
1262          mat.opacity = 1.0f - transparency / 100.0f;
1263          if(mat.opacity < 1.0)
1264             mat.flags.translucent = true;
1265          break;
1266       }
1267       case MAT_DIFFUSE:
1268       {
1269          ReadChunks(ReadRGB, info, &mat.diffuse);
1270          ReadChunks(ReadRGB, info, &mat.diffuse);
1271          break;
1272       }
1273       case MAT_AMBIENT:
1274       {
1275          ReadChunks(ReadRGB, info, &mat.ambient);
1276          ReadChunks(ReadRGB, info, &mat.ambient);
1277          break;
1278       }
1279       case MAT_SPECULAR:
1280       {
1281          ReadChunks(ReadRGB, info, &mat.specular);
1282          ReadChunks(ReadRGB, info, &mat.specular);
1283          break;
1284       }
1285       case MAT_SELFILLUM:
1286       {
1287          uint16 emissive;
1288          ReadChunks(ReadAmountOf, info, &emissive);
1289          mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1290          mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1291          mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1292          break;
1293       }
1294       case MAT_SHINSTRENGTH:
1295       {
1296          uint16 shininess;
1297          ReadChunks(ReadAmountOf, info, &shininess);
1298          mat.reflectivity = shininess / 100.0f;
1299          /*
1300          mat.specular.r *= shininess / 100.0f;
1301          mat.specular.g *= shininess / 100.0f;
1302          mat.specular.b *= shininess / 100.0f;
1303          */
1304          break;
1305       }
1306       case MAT_SHININESS:
1307       {
1308          uint16 power;
1309          ReadChunks(ReadAmountOf, info, &power);
1310          mat.power = power;
1311          break;
1312       }
1313       case MAT_MAPTEXTURE1:
1314          ReadChunks(ReadMap, info, mat);
1315          break;
1316       case MAT_MAPOPACITY:
1317          ReadChunks(ReadMap, info, mat);
1318          break;
1319       case MAT_DOUBLESIDED:
1320          mat.flags.doubleSided = true;
1321          break;
1322       case MAT_BUMPMAP:
1323          ReadChunks(ReadMap, info, mat);
1324          break;
1325       /*
1326       default:
1327          PrintLn("Unhandled MAT type ID", info->chunkId);
1328       */
1329    }
1330    return true;
1331 }
1332
1333 // Lights
1334 static bool ReadLight(FileInfo * info, Object object)
1335 {
1336    Light * light = &object.light;
1337    switch(info->chunkId)
1338    {
1339       case RGB_BYTE:
1340       case RGB_BYTE_GAMMA:
1341       case RGB_FLOAT:
1342       case RGB_FLOAT_GAMMA:
1343          ReadRGB(info, &light->diffuse);
1344          light->specular = light->diffuse;
1345          break;
1346       case LIT_SPOT:
1347       {
1348          Object target;
1349          char targetName[MAXNAMELEN];
1350
1351          strcpy(targetName, object.name);
1352          strcat(targetName, ".target");
1353
1354          light->flags.omni = false;
1355          light->flags.spot = true;
1356
1357          target = Object { };
1358          target.name = CopyString(targetName);
1359          info->rootObject.children.AddName(target);
1360          target.parent = info->rootObject;
1361
1362          light->target = target;
1363
1364          target.transform.position.x = ReadFloat(info->f);
1365          target.transform.position.z = ReadFloat(info->f);
1366          target.transform.position.y =-ReadFloat(info->f);
1367
1368          light->hotSpot = ReadFloat(info->f);
1369          light->fallOff = ReadFloat(info->f);
1370          break;
1371       }
1372       case LIT_ONOFF:
1373       {
1374          light->flags.off = true;
1375          break;
1376       }
1377       case LIT_ATTENUATION:
1378       {
1379          /* solve (
1380             {
1381                d = 300, small = 0.001,
1382                Kl = 0, Kc = 0,
1383                d * (Kl + Kq * d) = (1 / small) - Kc
1384             },
1385             { Kc, Kl, Kq, small, d });
1386          */
1387
1388          /*
1389          #define MINLIGHT     0.08
1390          light->Kq = 1/(light->end*light->end*MINLIGHT);
1391          */
1392
1393          #define MINLIGHT     0.15f
1394          // #define MINLIGHT     0.1
1395          //light->Kl = (float)(1/(light->end*MINLIGHT));
1396
1397          float c = 1.0, l = 0.005, q = 0.0005, small = 500, end = light->end;
1398          light->Kc = (c + l * end + q * end * end) / small;
1399          light->Kl = light->Kc * l/c;
1400          light->Kq = light->Kc * q/c;
1401          light->flags.attenuation = true;
1402          break;
1403       }
1404       case LIT_START:
1405       {
1406          light->start = ReadFloat(info->f);
1407          break;
1408       }
1409       case LIT_END:
1410       {
1411          light->end = ReadFloat(info->f);
1412          break;
1413       }
1414       case LIT_MULTIPLIER:
1415       {
1416          light->multiplier = ReadFloat(info->f);
1417          break;
1418       }
1419    }
1420    return true;
1421 }
1422
1423 // Cameras
1424 static bool ReadCamera(FileInfo * info, Object object)
1425 {
1426    switch(info->chunkId)
1427    {
1428       case CAM_SEECONE:
1429       {
1430          break;
1431       }
1432       case CAM_RANGES:
1433       {
1434          //Camera camera = object.camera;
1435          /*float nearRange = */ReadFloat(info->f);
1436          /*float farRange = */ReadFloat(info->f);
1437          /*
1438          camera.zMin = Max(0.1, nearRange);
1439          camera.zMax = farRange;
1440          */
1441          break;
1442       }
1443    }
1444    return true;
1445 }
1446
1447 // Edit Chunks
1448 static bool ReadEditObject(FileInfo * info, char * name)
1449 {
1450    DisplaySystem displaySystem = info->displaySystem;
1451    switch(info->chunkId)
1452    {
1453       case OBJ_TRIMESH:
1454       {
1455          Object object = info->rootObject.Find(name);
1456          if(!object)
1457          {
1458             object = Object { };
1459             object.name = CopyString(name);
1460             info->rootObject.children.AddName(object);
1461             object.parent = info->rootObject;
1462          }
1463          object.InitializeMesh(displaySystem);
1464          ReadChunks(ReadTriMesh, info, object);
1465          object.flags.mesh = true;
1466          object.mesh.Unlock(0);
1467          break;
1468       }
1469       case OBJ_LIGHT:
1470       {
1471          Object object = info->rootObject.Find(name);
1472          Light * light;
1473          Vector3Df position;
1474
1475          if(!object)
1476          {
1477             object = Object { };
1478             object.name = CopyString(name);
1479             info->rootObject.children.AddName(object);
1480             object.parent = info->rootObject;
1481          }
1482          object.flags.light = true;
1483
1484          light = &object.light;
1485          light->lightObject = object;
1486          light->flags.omni = true;
1487          light->multiplier = 1.0f;
1488
1489          // This is not used?
1490          Read3DVertex(info->f, position);
1491          light->direction = { position.x, position.y, position.z };
1492          info->pos += sizeof(float) * 3;
1493
1494          ReadChunks(ReadLight, info, object);
1495          break;
1496       }
1497       case OBJ_CAMERA:
1498       {
1499          char targetName[MAXNAMELEN];
1500          Object object = info->rootObject.Find(name);
1501          Object target;
1502          Camera camera;
1503          float /*bankAngle, */focus;
1504          double mm;
1505
1506          strcpy(targetName, name);
1507          strcat(targetName, ".target");
1508          target = info->rootObject.Find(targetName);
1509
1510          if(!object)
1511          {
1512             object = Object { };
1513             object.name = CopyString(name);
1514             info->rootObject.children.AddName(object);
1515
1516             object.parent = info->rootObject;
1517             object.camera = Camera { };
1518             object.camera.type = lookAtObject;
1519          }
1520
1521          if(!target)
1522          {
1523             target = Object { };
1524             target.name = CopyString(targetName);
1525             info->rootObject.children.AddName(target);
1526             target.parent = info->rootObject;
1527          }
1528
1529          object.flags.camera = true;
1530          object.cameraTarget = target;
1531
1532          camera = object.camera;
1533          camera.cameraObject = object;
1534          camera.target = target;
1535
1536          //Read3DVertex(info->f, camera.position);
1537          object.transform.position.x = ReadFloat(info->f);
1538          object.transform.position.z = ReadFloat(info->f);
1539          object.transform.position.y =-ReadFloat(info->f);
1540
1541          info->pos += sizeof(float) * 3;
1542          //Read3DVertex(info->f, object.cameraTarget.position);
1543          target.transform.position.x = ReadFloat(info->f);
1544          target.transform.position.z = ReadFloat(info->f);
1545          target.transform.position.y =-ReadFloat(info->f);
1546
1547          info->pos += sizeof(float) * 3;
1548          /*bankAngle = */ReadFloat(info->f);
1549          info->pos += sizeof(float);
1550          focus = ReadFloat(info->f);
1551          info->pos += sizeof(float);
1552
1553          mm = (focus - 5.05659508373109) / 1.13613250717301;
1554          camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1555
1556          ReadChunks(ReadCamera, info, object);
1557          break;
1558       }
1559       case OBJ_HIDDEN: break;
1560    }
1561    return true;
1562 }
1563
1564 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1565
1566 static bool ReadEditChunks(FileInfo * info, void * data)
1567 {
1568    switch(info->chunkId)
1569    {
1570       case EDIT_AMBIENT:
1571       {
1572          // Read the ambient color
1573          ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1574          break;
1575       }
1576       case EDIT_MATERIAL:
1577       {
1578          Material material { /*flags = { singleSideLight = true }*/ };
1579          Material mat;
1580          ReadChunks(ReadMaterial, info, material);
1581
1582          mat = info->displaySystem.AddNamedMaterial(material.name);
1583          if(mat)
1584          {
1585             if(material.baseMap)
1586                material.baseMap.MakeMipMaps(info->displaySystem);
1587             if(material.bumpMap)
1588                material.bumpMap.MakeMipMaps(info->displaySystem);
1589             if(material.specularMap)
1590                material.specularMap.MakeMipMaps(info->displaySystem);
1591
1592             // COPY_NITEM(mat, material);
1593             CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1594          }
1595          else
1596          {
1597             delete material.baseMap;
1598          }
1599          delete material.name;
1600          delete material;
1601          break;
1602       }
1603       case EDIT_OBJECT:
1604       {
1605          char * name;
1606          info->pos += ReadASCIIZ(info->f, &name);
1607          ReadChunks(ReadEditObject, info, name);
1608          delete name;
1609          break;
1610       }
1611    }
1612    return true;
1613 }
1614
1615 struct ObjectInfoBlock
1616 {
1617    OldList tracks;
1618    short hierarchy;
1619    short parent;
1620    char * name;
1621    char * dummyName;
1622    Vector3Df pivot;
1623 };
1624
1625 // Key Framer Chunks
1626 static class AccelerationFlags : uint32
1627 {
1628    bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
1629 };
1630
1631 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1632 {
1633    switch(info->chunkId)
1634    {
1635       case FRM_PARAM:
1636       {
1637          //uint16 flags1, flags2;
1638          ReadASCIIZ(info->f, &block->name);
1639          /*flags1 = */ReadWORD(info->f);
1640          /*flags2 = */ReadWORD(info->f);
1641          block->parent = ReadWORD(info->f);
1642          break;
1643       }
1644       case FRM_DUMMYNAME:
1645          ReadASCIIZ(info->f, &block->dummyName);
1646          break;
1647       case FRM_PIVOT:
1648          Read3DVertex(info->f, block->pivot);
1649          break;
1650       case FRM_HIERARCHY:
1651          block->hierarchy = ReadWORD(info->f);
1652          break;
1653       case FRM_TRACKPOS:
1654       case FRM_TRACKROT:
1655       case FRM_TRACKSCALE:
1656       case FRM_TRACKROLL:
1657       case FRM_TRACKFOV:
1658       case FRM_TRACKCOLOR:
1659       case FRM_TRACKHOTSPOT:
1660       case FRM_TRACKFALLOFF:
1661       {
1662          FrameTrack track { };
1663          if(track)
1664          {
1665             uint16 flags;
1666             byte unknown[8];
1667             uint c;
1668
1669             block->tracks.Add(track);
1670
1671             flags = ReadWORD(info->f);
1672
1673             info->f.Read(unknown, sizeof(unknown), 1);
1674
1675             track.numKeys = ReadDWORD(info->f);
1676
1677             switch(info->chunkId)
1678             {
1679                case FRM_TRACKPOS: track.type.type = position; break;
1680                case FRM_TRACKROT: track.type.type = rotation; break;
1681                case FRM_TRACKSCALE: track.type.type = scaling; break;
1682                case FRM_TRACKROLL: track.type.type = roll; break;
1683                case FRM_TRACKFOV: track.type.type = fov; break;
1684                case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1685                case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1686                case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1687             }
1688             if((flags & 0x0003) == 3)
1689                track.type.loop = true;
1690
1691             track.keys = new0 FrameKey[track.numKeys];
1692             for(c = 0; c<track.numKeys; c++)
1693             {
1694                AccelerationFlags accelerationFlags;
1695                FrameKey * key = track.keys + c;
1696
1697                key->frame = ReadDWORD(info->f);
1698                accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
1699
1700                if(accelerationFlags.tension)
1701                   key->tension = ReadFloat(info->f);
1702                if(accelerationFlags.continuity)
1703                   key->continuity = ReadFloat(info->f);
1704                if(accelerationFlags.bias)
1705                   key->bias = ReadFloat(info->f);
1706                if(accelerationFlags.easeTo)
1707                   key->easeTo = ReadFloat(info->f);
1708                if(accelerationFlags.easeFrom)
1709                   key->easeFrom = ReadFloat(info->f);
1710
1711                switch(info->chunkId)
1712                {
1713                   case FRM_TRACKPOS:
1714                   {
1715                      Vector3Df position;
1716                      Read3DVertex(info->f, position);
1717                      key->position = { position.x, -position.z, position.y };
1718                      break;
1719                   }
1720                   case FRM_TRACKROT:
1721                   {
1722                      Vector3Df axis;
1723                      Angle angle = ReadFloat(info->f);
1724                      Vector3D fixedAxis;
1725
1726                      Read3DVertex(info->f, axis);
1727                      fixedAxis = { axis.x, -axis.z, axis.y };
1728
1729                      if(c > 0)
1730                      {
1731                         Quaternion rotation;
1732                         rotation.RotationAxis(fixedAxis, angle);
1733                         key->orientation.Multiply((key - 1)->orientation, rotation);
1734                      }
1735                      else
1736                         key->orientation.RotationAxis(fixedAxis, angle);
1737                      break;
1738                   }
1739                   case FRM_TRACKSCALE:
1740                   {
1741                      Vector3Df scaling;
1742                      Read3DVertex(info->f, scaling);
1743                      key->scaling = { scaling.x, scaling.z, scaling.y };
1744                      break;
1745                   }
1746                   case FRM_TRACKFOV:
1747                   {
1748                      key->fov = ReadFloat(info->f);
1749                      break;
1750                   }
1751                   case FRM_TRACKROLL:
1752                   {
1753                      key->roll = -ReadFloat(info->f);
1754                      break;
1755                   }
1756                   case FRM_TRACKCOLOR:
1757                   {
1758                      FileInfo childInfo = *info;
1759                      childInfo.chunkId = RGB_FLOAT;
1760                      ReadRGB(&childInfo, &key->color);
1761                      break;
1762                   }
1763                   case FRM_TRACKHOTSPOT:
1764                   {
1765                      key->hotSpot = ReadFloat(info->f);
1766                      break;
1767                   }
1768                   case FRM_TRACKFALLOFF:
1769                   {
1770                      key->fallOff = ReadFloat(info->f);
1771                      break;
1772                   }
1773                }
1774             }
1775          }
1776          break;
1777       }
1778    }
1779    return true;
1780 }
1781
1782 static Object FindObjectID(Object object, int id)
1783 {
1784    Object result = null;
1785    Object child;
1786    for(child = object.children.first; child; child = child.next)
1787    {
1788       if(child.flags.hierarchy == (uint16) id)
1789       {
1790          result = child;
1791          break;
1792       }
1793       else
1794       {
1795          result = FindObjectID(child, id);
1796          if(result) break;
1797       }
1798    }
1799    return result;
1800 }
1801
1802 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1803 {
1804    switch(info->chunkId)
1805    {
1806       case FRM_MESHINFO:
1807       {
1808          ObjectInfoBlock block { };
1809          Object object = null;
1810
1811          ReadChunks(ReadFrameInfoBlock, info, &block);
1812
1813          if(block.dummyName && block.dummyName[0])
1814          {
1815             if(!strcmp(block.name, "$$$DUMMY"))
1816             {
1817                object = Object { };
1818                object.name = block.dummyName;
1819                info->rootObject.children.AddName(object);
1820                object.transform.scaling = { 1,1,1 };
1821             }
1822             else
1823             {
1824                Object model = info->rootObject.Find(block.name);
1825                if(model)
1826                {
1827                   object = Object { };
1828                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1829                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1830                   object.flags = model.flags;
1831                   object.flags.ownMesh = false;
1832                   object.mesh = model.mesh;
1833                   /*
1834                   object.min = model.min;
1835                   object.max = model.max;
1836                   object.radius = model.radius;
1837                   */
1838                   object.transform = model.transform;
1839                   info->rootObject.children.AddName(object);
1840                }
1841                delete block.dummyName;
1842             }
1843             if(object)
1844                object.parent = info->rootObject;
1845          }
1846          else
1847             object = info->rootObject.Find(block.name);
1848
1849          if(object)
1850          {
1851             Mesh mesh = object.mesh;
1852             object.flags.hierarchy = block.hierarchy + 1;
1853             if(block.parent != -1)
1854             {
1855                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1856                if(parent)
1857                {
1858                   object.parent.children.Remove(object);
1859                   parent.children.AddName(object);
1860                   object.parent = parent;
1861                }
1862             }
1863             object.pivot.x = block.pivot.x;
1864             object.pivot.y =-block.pivot.z;
1865             object.pivot.z = block.pivot.y;
1866
1867             if(mesh && object.flags.ownMesh)
1868             {
1869                if(mesh.Lock({ vertices = true }))
1870                {
1871                   int c;
1872                   // Take pivot into account
1873                   for(c = 0; c<mesh.nVertices; c++)
1874                   {
1875                      mesh.vertices[c].x -= object.pivot.x;
1876                      mesh.vertices[c].y -= object.pivot.y;
1877                      mesh.vertices[c].z -= object.pivot.z;
1878                   }
1879                   mesh.Unlock({ vertices = true });
1880                }
1881             }
1882
1883             object.tracks = block.tracks;
1884             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1885             object.endFrame = info->rootObject.endFrame;
1886          }
1887          /*else
1888             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1889          delete block.name;
1890          break;
1891       }
1892       case FRM_CAMERA:
1893       {
1894          ObjectInfoBlock block { };
1895          Object object = null;
1896
1897          ReadChunks(ReadFrameInfoBlock, info, &block);
1898
1899          if(block.dummyName && block.dummyName[0])
1900          {
1901             if(!strcmp(block.name, "$$$DUMMY"))
1902             {
1903                object = Object { };
1904                object.name = block.dummyName;
1905                info->rootObject.children.AddName(object);
1906                object.transform.scaling = { 1, 1, 1 };
1907                object.flags.camera = true;
1908             }
1909             else
1910             {
1911                Object model = info->rootObject.Find(block.name);
1912                if(model)
1913                {
1914                   object = Object { };
1915                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1916                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1917                   object.flags = model.flags;
1918                   object.flags.ownMesh = false;
1919                   object.camera = model.camera;
1920                   object.flags.camera = true;
1921                   info->rootObject.children.AddName(object);
1922                }
1923                delete block.dummyName;
1924             }
1925             if(object)
1926                object.parent = info->rootObject;
1927          }
1928          else
1929             object = info->rootObject.Find(block.name);
1930
1931          if(object)
1932          {
1933             object.flags.hierarchy = block.hierarchy + 1;
1934             if(block.parent != -1)
1935             {
1936                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1937                if(parent)
1938                {
1939                   object.parent.children.Remove(object);
1940                   parent.children.AddName(object);
1941                   object.parent = parent;
1942                }
1943             }
1944             object.pivot.x = block.pivot.x;
1945             object.pivot.y =-block.pivot.z;
1946             object.pivot.z = block.pivot.y;
1947
1948             object.tracks = block.tracks;
1949             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1950             object.endFrame = info->rootObject.endFrame;
1951          }
1952          /*else
1953             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1954          delete block.name;
1955          break;
1956       }
1957       case FRM_CAMERATARGET:
1958       {
1959          ObjectInfoBlock block { };
1960          Object object = null;
1961          char targetName[MAXNAMELEN];
1962
1963          ReadChunks(ReadFrameInfoBlock, info, &block);
1964
1965          strcpy(targetName, block.name);
1966          strcat(targetName, ".target");
1967
1968          if(block.dummyName && block.dummyName[0])
1969          {
1970             if(!strcmp(block.name, "$$$DUMMY"))
1971             {
1972                object = Object { };
1973                object.name = block.dummyName;
1974                info->rootObject.children.AddName(object);
1975                object.transform.scaling = { 1,1,1 };
1976             }
1977             else
1978             {
1979                Object model = info->rootObject.Find(targetName);
1980                if(model)
1981                {
1982                   object = Object { };
1983                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1984                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1985                   object.flags = model.flags;
1986                   object.flags.ownMesh = false;
1987                   object.camera = model.camera;
1988                   info->rootObject.children.AddName(object);
1989                }
1990                delete block.dummyName;
1991             }
1992             if(object)
1993                object.parent = info->rootObject;
1994          }
1995          else
1996             object = info->rootObject.Find(targetName);
1997
1998          if(object)
1999          {
2000             object.flags.hierarchy = block.hierarchy + 1;
2001             if(block.parent != -1)
2002             {
2003                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2004                if(parent)
2005                {
2006                   object.parent.children.Remove(object);
2007                   parent.children.AddName(object);
2008                   object.parent = parent;
2009                }
2010             }
2011             object.pivot.x = block.pivot.x;
2012             object.pivot.y =-block.pivot.z;
2013             object.pivot.z = block.pivot.y;
2014
2015             object.tracks = block.tracks;
2016             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2017             object.endFrame = info->rootObject.endFrame;
2018          }
2019          /*else
2020             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2021          delete block.name;
2022          break;
2023       }
2024       case FRM_AMBIENT:
2025       {
2026          ObjectInfoBlock block { };
2027
2028          ReadChunks(ReadFrameInfoBlock, info, &block);
2029
2030          info->rootObject.tracks = block.tracks;
2031          break;
2032       }
2033       case FRM_OMNILIGHT:
2034       case FRM_SPOTLIGHT:
2035       {
2036          ObjectInfoBlock block { };
2037          Object object = null;
2038
2039          ReadChunks(ReadFrameInfoBlock, info, &block);
2040
2041          if(block.dummyName && block.dummyName[0])
2042          {
2043             if(!strcmp(block.name, "$$$DUMMY"))
2044             {
2045                object = Object { };
2046                object.name = block.dummyName;
2047                info->rootObject.children.AddName(object);
2048                object.transform.scaling = { 1, 1, 1 };
2049                object.flags.light = true;
2050             }
2051             else
2052             {
2053                Object model = info->rootObject.Find(block.name);
2054                if(model)
2055                {
2056                   object = Object { };
2057                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2058                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
2059                   object.flags = model.flags;
2060                   object.flags.ownMesh = false;
2061                   object.light = model.light;
2062                   object.flags.light = true;
2063                   info->rootObject.children.AddName(object);
2064                }
2065                delete block.dummyName;
2066             }
2067             if(object)
2068                object.parent = info->rootObject;
2069          }
2070          else
2071             object = info->rootObject.Find(block.name);
2072
2073          if(object)
2074          {
2075             object.flags.hierarchy = block.hierarchy + 1;
2076             if(block.parent != -1)
2077             {
2078                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2079                if(parent)
2080                {
2081                   object.parent.children.Remove(object);
2082                   parent.children.AddName(object);
2083                   object.parent = parent;
2084                }
2085             }
2086             object.pivot.x = block.pivot.x;
2087             object.pivot.y =-block.pivot.z;
2088             object.pivot.z = block.pivot.y;
2089
2090             object.tracks = block.tracks;
2091             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2092             object.endFrame = info->rootObject.endFrame;
2093          }
2094          /*else
2095             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2096          delete block.name;
2097          break;
2098       }
2099       case FRM_SPOTLIGHTTARGET:
2100       {
2101          ObjectInfoBlock block { };
2102          Object object = null;
2103          char targetName[MAXNAMELEN];
2104
2105          ReadChunks(ReadFrameInfoBlock, info, &block);
2106
2107          strcpy(targetName, block.name);
2108          strcat(targetName, ".target");
2109
2110          if(block.dummyName && block.dummyName[0])
2111          {
2112             if(!strcmp(block.name, "$$$DUMMY"))
2113             {
2114                object = Object { };
2115                object.name = block.dummyName;
2116                info->rootObject.children.AddName(object);
2117                object.transform.scaling = { 1,1,1 };
2118             }
2119             else
2120             {
2121                Object model = info->rootObject.Find(targetName);
2122                if(model)
2123                {
2124                   object = Object { };
2125                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2126                   // TODO: When passing a String to a const String, use member if property is const String but member is String
2127                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
2128                   object.flags = model.flags;
2129                   object.flags.ownMesh = false;
2130                   object.light = model.light;
2131                   info->rootObject.children.AddName(object);
2132                }
2133                delete block.dummyName;
2134             }
2135             if(object)
2136                object.parent = info->rootObject;
2137          }
2138          else
2139             object = info->rootObject.Find(targetName);
2140
2141          if(object)
2142          {
2143             object.flags.hierarchy = block.hierarchy + 1;
2144             if(block.parent != -1)
2145             {
2146                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2147                if(parent)
2148                {
2149                   object.parent.children.Remove(object);
2150                   parent.children.AddName(object);
2151                   object.parent = parent;
2152                }
2153             }
2154             object.pivot.x = block.pivot.x;
2155             object.pivot.y =-block.pivot.z;
2156             object.pivot.z = block.pivot.y;
2157
2158             object.tracks = block.tracks;
2159             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2160             object.endFrame = info->rootObject.endFrame;
2161          }
2162          /*else
2163             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2164          delete block.name;
2165          break;
2166       }
2167       case FRM_FRAMES:
2168       {
2169          info->rootObject.startFrame = ReadDWORD(info->f);
2170          info->rootObject.endFrame = ReadDWORD(info->f);
2171          *&(info->rootObject.frame) = info->rootObject.startFrame;
2172          break;
2173       }
2174    }
2175    return true;
2176 }
2177
2178 // Main Chunks
2179 static bool ReadMainChunks(FileInfo * info, void * data)
2180 {
2181    switch(info->chunkId)
2182    {
2183       case EDIT3DS:
2184          ReadChunks(ReadEditChunks, info, null);
2185          break;
2186       case KEYFRAME3DS:
2187       {
2188          Object object = data;
2189          if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2190          {
2191             ReadChunks(ReadKeyFrameChunks, info, null);
2192             object.flags.keysLoaded = true;
2193          }
2194          break;
2195       }
2196    }
2197    return true;
2198 }
2199
2200 static bool ReadMain(FileInfo * info, void * data)
2201 {
2202    switch(info->chunkId)
2203    {
2204       case MAIN3DS:
2205          ReadChunks(ReadMainChunks, info, data);
2206          break;
2207    }
2208    return true;
2209 }
2210
2211 class Object3DSFormat : ObjectFormat
2212 {
2213    class_property(extension) = "3ds";
2214
2215    bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2216    {
2217       bool result = false;
2218       if(fileName)
2219       {
2220          FileInfo info = {0};
2221          info.rootObject = object;
2222          info.displaySystem = displaySystem;
2223          info.pos = 0;
2224          info.end = 1;
2225          info.fileName = fileName;
2226          StripLastDirectory(fileName, info.textureDirectory);
2227          info.f = FileOpen(fileName, read);
2228          if(info.f)
2229          {
2230             // TOFIX: eC reorders that badly
2231             // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2232             if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
2233             {
2234                object.flags.root = true;
2235                object.SetMinMaxRadius(true);
2236                object._Animate(object.frame);
2237                object.UpdateTransform();
2238                result = true;
2239             }
2240             delete info.f;
2241          }
2242          if(info.matFaces)
2243             info.matFaces.Free();
2244          delete info.matFaces;
2245       }
2246       if(!result)
2247          object.Free(displaySystem);
2248       return result;
2249    }
2250 }