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