ecere/gfx/drivers/OpenGL; 3DS: Added support for scaling texture coordinates
[sdk] / ecere / src / gfx / 3D / models / Object3DSFormat.ec
1 namespace gfx3D::models;
2
3 import "Object"
4
5 #define MAXNAMELEN   64
6
7 // RGB Chunks
8 #define RGB_FLOAT             0x0010
9 #define RGB_BYTE              0x0011
10 #define RGB_BYTE_GAMMA        0x0012
11 #define RGB_FLOAT_GAMMA       0x0013
12
13 // Amount Of Chunks
14 #define AMOUNT_OF             0x0030
15
16 #define MAIN3DS               0x4D4D
17 #define EDIT3DS               0x3D3D
18 #define EDIT_AMBIENT          0x2100
19 #define EDIT_MATERIAL         0xAFFF
20 #define EDIT_OBJECT           0x4000
21 #define OBJ_HIDDEN            0x4010
22 #define OBJ_TRIMESH           0x4100
23 #define OBJ_LIGHT             0x4600
24 #define OBJ_CAMERA            0x4700
25
26 // Triangular Mesh Chunks
27 #define TRI_VERTEXL           0x4110
28 #define TRI_FACEL1            0x4120
29 #define TRI_MATERIAL          0x4130
30 #define TRI_MAPPINGCOORS      0x4140
31 #define TRI_SMOOTHING         0x4150
32 #define TRI_LOCAL             0x4160
33
34 // Light Chunks
35 #define LIT_SPOT              0x4610
36 #define LIT_ONOFF             0x4620
37 #define LIT_ATTENUATION       0x4625
38 #define LIT_START             0x4659
39 #define LIT_END               0x465A
40 #define LIT_MULTIPLIER        0x465B
41
42 // Camera Chunks
43 #define CAM_SEECONE           0x4710
44 #define CAM_RANGES            0x4720
45
46 // Material Chunks
47 #define MAT_NAME              0xA000
48 #define MAT_AMBIENT           0xA010
49 #define MAT_DIFFUSE           0xA020
50 #define MAT_SPECULAR          0xA030
51 #define MAT_SHININESS         0xA040
52 #define MAT_SHINSTRENGTH      0xA041
53 #define MAT_TRANSPARENCY      0xA050
54 #define MAT_DOUBLESIDED       0xA081
55 #define MAT_SELFILLUM         0xA084
56 #define MAT_MAPTEXTURE1       0xA200
57 #define MAT_MAPOPACITY        0xA210
58
59 // Map Chunks
60 #define MAP_FILENAME          0xA300
61 #define MAP_OPTIONS           0xA351
62 #define MAP_1_U_SCALE   0xA354
63 #define MAP_1_V_SCALE   0xA356
64 #define MAP_U_OFFSET    0xA358
65 #define MAP_V_OFFSET    0xA35A
66 #define MAP_ROTATION    0xA35C
67
68 // Keyframer Chunks
69 #define KEYFRAME3DS           0xB000
70 #define FRM_AMBIENT           0xB001
71 #define FRM_MESHINFO          0xB002
72 #define FRM_CAMERA            0xB003
73 #define FRM_CAMERATARGET      0xB004
74 #define FRM_OMNILIGHT         0xB005
75 #define FRM_SPOTLIGHTTARGET   0xB006
76 #define FRM_SPOTLIGHT         0xB007
77 #define FRM_FRAMES            0xB008
78 #define FRM_PARAM             0xB010
79 #define FRM_DUMMYNAME         0xB011
80 #define FRM_PIVOT             0xB013
81 #define FRM_TRACKPOS          0xB020
82 #define FRM_TRACKROT          0xB021
83 #define FRM_TRACKSCALE        0xB022
84 #define FRM_TRACKFOV          0xB023
85 #define FRM_TRACKROLL         0xB024
86 #define FRM_TRACKCOLOR        0xB025
87 #define FRM_TRACKMORPH        0xB026   // What does this do?
88 #define FRM_TRACKHOTSPOT      0xB027
89 #define FRM_TRACKFALLOFF      0xB028
90 #define FRM_TRACKHIDE         0xB029
91 #define FRM_HIERARCHY         0xB030
92
93 typedef struct FileInfo FileInfo;
94
95 typedef struct
96 {
97    uint16 indices[3];
98    uint16 oldIndices[3];
99    uint smoothGroups;
100    bool done:1;
101 } Face;
102
103 struct FileInfo
104 {
105    File f;
106    DisplaySystem displaySystem;
107    Object rootObject;
108
109    uint16 chunkId;
110    uint pos, end;
111
112    FileInfo * parent;
113    int nFaces;
114    Face * faces;
115    char textureDirectory[MAX_DIRECTORY];
116 };
117
118 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
119                       | (((unsigned short)(word) & 0xff00) >> 8)
120
121 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
122                          | (((unsigned int)(dword) & 0x0000ff00) <<  8) \
123                          | (((unsigned int)(dword) & 0x00ff0000) >>  8) \
124                          | (((unsigned int)(dword) & 0xff000000) >> 24))
125
126 #ifndef __BIG_ENDIAN__
127 #define BIGENDSWAP_WORD(word)
128 #define BIGENDSWAP_DWORD(dword)
129 #else
130 #define BIGENDSWAP_WORD(word)      (*(uint16 *)(&(word)))  = SWAP_WORD((*(uint16 *)(&(word))));
131 #define BIGENDSWAP_DWORD(dword)    (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
132 #endif
133
134 // Zero Terminated String
135 static int ReadASCIIZ(File f, char ** string)
136 {
137    // *** Read String ***
138    int c;
139    char temp[1024] = "";
140    for(c=0; c<1024;)
141    {
142       f.Getc(&temp[c]);
143       if(!temp[c++]) break;
144    }
145    *string = new char[c];
146    if(*string)
147       strcpy(*string, temp);
148    return c;
149 }
150
151 static float ReadFloat(File f)
152 {
153    float floatValue;
154    f.Read(&floatValue, sizeof(float), 1);
155    BIGENDSWAP_DWORD(floatValue);
156    return floatValue;
157 }
158
159 static uint16 ReadWORD(File f)
160 {
161    uint16 wordValue;
162    f.Read(&wordValue, sizeof(uint16), 1);
163    BIGENDSWAP_WORD(wordValue);
164    return wordValue;
165 }
166
167 static uint ReadDWORD(File f)
168 {
169    uint dwordValue;
170    f.Read(&dwordValue, sizeof(uint), 1);
171    BIGENDSWAP_DWORD(dwordValue);
172    return dwordValue;
173 }
174
175 // Sub Chunks
176 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
177 {
178    for(;info->pos < info->end;)
179    {
180       FileInfo childInfo = *info;
181       uint length;
182
183       childInfo.parent = info;
184
185       info->f.Seek(info->pos, start);
186       childInfo.chunkId = ReadWORD(info->f);
187       length = ReadDWORD(info->f);
188
189       childInfo.pos += sizeof(uint16) + sizeof(uint);
190       childInfo.end = info->pos + length;
191
192       if(!chunkParser(&childInfo, data))
193          return false;
194
195       info->pos = childInfo.end;
196    }
197    return true;
198 }
199
200 // General Chunks
201 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
202 {
203    if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
204    {
205       byte value;
206       info->f.Getc(&value); rgb->r = value / 255.0f;
207       info->f.Getc(&value); rgb->g = value / 255.0f;
208       info->f.Getc(&value); rgb->b = value / 255.0f;
209    }
210    else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
211    {
212       rgb->r = ReadFloat(info->f);
213       rgb->g = ReadFloat(info->f);
214       rgb->b = ReadFloat(info->f);
215    }
216    return true;
217 }
218
219 static bool Read3DVertex(File f, Vector3Df vertex)
220 {
221    vertex.x = ReadFloat(f);
222    vertex.y = ReadFloat(f);
223    vertex.z = ReadFloat(f);
224    return true;
225 }
226
227 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
228 {
229    if(info->chunkId == AMOUNT_OF)
230       *amountOf = ReadWORD(info->f);
231    return true;
232 }
233
234 typedef struct
235 {
236    uint smoothGroups;
237    int index;
238 } VertexConfig;
239
240 typedef struct
241 {
242    int numConfig;
243    VertexConfig * config;
244 } VertexConfigList;
245
246 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
247 {
248    int c;
249    Face * faces = info->faces;
250    //int nFaces = info->nFaces;
251    int nVertices = mesh.nVertices;
252    int index;
253    int nNewVertices;
254    int * numShared;
255
256    VertexConfigList * configLists = new0 VertexConfigList[nVertices];
257
258    nNewVertices = nVertices;
259    for(c = 0; c<info->nFaces; c++)
260    {
261       Face * face = &faces[c];
262       int i;
263
264       for(i = 0; i<3; i++)
265       {
266          int index = face->indices[i];
267          int v;
268          VertexConfigList * configList = &configLists[index];
269          VertexConfig * config = null;
270          for(v = 0; v<configList->numConfig; v++)
271          {
272             uint smoothGroups = configList->config[v].smoothGroups;
273             if(smoothGroups == face->smoothGroups)
274             {
275                config = &configList->config[v];
276                break;
277             }
278             else if(smoothGroups && face->smoothGroups)
279             {
280                int g;
281                for(g = 0; g<32; g++)
282                   if(smoothGroups & (1<<g) && face->smoothGroups & (1<<g))
283                   {
284                      config = &configList->config[v];
285                      config->smoothGroups |= face->smoothGroups;
286                      break;
287                   }
288             }
289          }
290
291          if(!config || !face->smoothGroups)
292          {
293             // Duplicate the vertex and make the face use it
294             if(configList->numConfig)
295                index = nNewVertices++;
296             face->indices[i] = (uint16)index;
297             if(!config)
298             {
299                configList->config = renew configList->config VertexConfig[configList->numConfig + 1];
300                config = &configList->config[configList->numConfig++];
301                config->index = index;
302                config->smoothGroups = face->smoothGroups;
303             }
304          }
305          else
306          {
307             face->indices[i] = (uint16)config->index;
308          }
309       }
310    }
311
312    // Allocate some extra vertices
313    {
314       Vector3Df * oldVertices = mesh.vertices;
315       Pointf * oldTexCoords = mesh.texCoords;
316
317       *((void **)&mesh.vertices) = null;
318       *((void **)&mesh.texCoords) = null;
319       *((int *)&mesh.nVertices) = 0;
320
321       mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
322
323       // Fill in the new vertices
324       for(index = 0; index<nVertices; index++)
325       {
326          int v;
327          VertexConfigList * configList = &configLists[index];
328          for(v = 0; v<configList->numConfig; v++)
329          {
330             VertexConfig * config = &configList->config[v];
331             Vector3Df * normal;
332             if(config->smoothGroups)
333             {
334                //if(v > 0)
335                {
336                   // Duplicate vertex
337                   mesh.vertices[config->index] = oldVertices[index]; //mesh.vertices[index];
338                   if(mesh.texCoords)
339                      mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
340                }
341             }
342             else
343             {
344                mesh.vertices[config->index] = oldVertices[index];
345                if(mesh.texCoords)
346                   mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
347             }
348             normal = &mesh.normals[config->index];
349             *normal = { 0,0,0 };
350          }
351       }
352
353       delete oldVertices;
354       delete oldTexCoords;
355    }
356
357    numShared = new0 int[nNewVertices];
358
359    for(c = 0; c<info->nFaces; c++)
360    {
361       Face * face = &faces[c];
362       int i;
363       Plane plane;
364       Vector3Df planeNormal;
365       plane.FromPointsf(mesh.vertices[face->oldIndices[2]],
366                        mesh.vertices[face->oldIndices[1]],
367                        mesh.vertices[face->oldIndices[0]]);
368       planeNormal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
369       if(face->smoothGroups)
370       {
371          for(i = 0; i<3; i++)
372          {
373             int index = face->indices[i];
374             Vector3Df * normal = &mesh.normals[index];
375             normal->Add(normal, planeNormal);
376             numShared[index]++;
377          }
378       }
379       else
380       {
381          for(i = 0; i<3; i++)
382          {
383             int index = face->oldIndices[i];
384             int newIndex = face->indices[i];
385             mesh.normals[newIndex] = planeNormal;
386             // Duplicate vertex
387             if(index != newIndex)
388             {
389                mesh.vertices[newIndex] = mesh.vertices[index];
390                if(mesh.texCoords)
391                   mesh.texCoords[newIndex] = mesh.texCoords[index];
392             }
393             numShared[newIndex]++;
394          }
395       }
396    }
397    for(index = 0; index<nNewVertices; index++)
398    {
399       Vector3Df * normal = &mesh.normals[index];
400       normal->Scale(normal, 1.0f / numShared[index]);
401       normal->Normalize(normal);
402    }
403
404    mesh.Unlock({ normals = true });
405
406    // Free all the temporary stuff
407    if(configLists)
408    {
409       for(index = 0; index < nVertices; index++)
410       {
411          VertexConfigList * configList = &configLists[index];
412          if(configList->config) delete configList->config;
413       }
414       delete configLists;
415    }
416    delete numShared;
417 }
418
419 // Meshes
420 static bool ReadSmoothing(FileInfo * info, Object object)
421 {
422    Mesh mesh = object.mesh;
423    switch(info->chunkId)
424    {
425       case TRI_SMOOTHING:
426       {
427          int c;
428          for(c = 0; c<info->nFaces; c++)
429             info->faces[c].smoothGroups = ReadDWORD(info->f);
430          break;
431       }
432    }
433    return true;
434 }
435
436 static bool ReadFacesListChunks(FileInfo * info, Object object)
437 {
438    DisplaySystem displaySystem = info->displaySystem;
439    Mesh mesh = object.mesh;
440    switch(info->chunkId)
441    {
442       case TRI_MATERIAL:
443       {
444          char * name;
445          Material mat;
446
447          ReadASCIIZ(info->f, &name);
448          mat = displaySystem.GetMaterial(name);
449          if(mat)
450          {
451             if(mat.flags.translucent)
452             {
453                int c;
454                uint16 count = ReadWORD(info->f);
455                mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + count];
456                for(c = 0; c<count; c++)
457                {
458                   uint16 face = ReadWORD(info->f);
459                   PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
460
461                   if(mesh.AllocatePrimitive(triangle, triangles, 3))
462                   {
463                      triangle->indices[0] = info->faces[face].indices[0];
464                      triangle->indices[1] = info->faces[face].indices[1];
465                      triangle->indices[2] = info->faces[face].indices[2];
466                      triangle->middle.Add(mesh.vertices[triangle->indices[0]], mesh.vertices[triangle->indices[1]]);
467                      triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indices[2]]);
468                      triangle->plane.FromPointsf(
469                         mesh.vertices[triangle->indices[2]],
470                         mesh.vertices[triangle->indices[1]],
471                         mesh.vertices[triangle->indices[0]]);
472
473                      mesh.UnlockPrimitive(triangle);
474                   }
475                   triangle->middle.x /= 3;
476                   triangle->middle.y /= 3;
477                   triangle->middle.z /= 3;
478
479                   triangle->material = mat;
480
481                   info->faces[face].done = (byte)bool::true;
482                }
483                object.flags.translucent = true;
484             }
485             else
486             {
487                PrimitiveGroup group;
488                uint16 count = ReadWORD(info->f);
489                group = mesh.AddPrimitiveGroup(triangles, count * 3);
490                if(group)
491                {
492                   int c;
493                   group.material = mat;
494                   for(c = 0; c<count; c++)
495                   {
496                      uint16 face = ReadWORD(info->f);
497                      if(object.flags.flipWindings)
498                      {
499                         group.indices[c*3]   = info->faces[face].indices[2];
500                         group.indices[c*3+1] = info->faces[face].indices[1];
501                         group.indices[c*3+2] = info->faces[face].indices[0];
502                      }
503                      else
504                      {
505                         group.indices[c*3]   = info->faces[face].indices[0];
506                         group.indices[c*3+1] = info->faces[face].indices[1];
507                         group.indices[c*3+2] = info->faces[face].indices[2];
508                      }
509                      info->faces[face].done = (byte)bool::true;
510                   }
511                   mesh.UnlockPrimitiveGroup(group);
512                }
513             }
514          }
515          delete name;
516          break;
517       }
518    }
519    return true;
520 }
521
522 static bool ReadTriMesh(FileInfo * info, Object object)
523 {
524    Mesh mesh = object.mesh;
525    switch(info->chunkId)
526    {
527       case TRI_VERTEXL:
528       {
529          int c;
530          uint16 nVertices = ReadWORD(info->f);
531          //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
532          *((int *) &mesh.nVertices) = nVertices;
533          *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
534          if(mesh.vertices)
535          {
536             for(c = 0; c<mesh.nVertices; c++)
537             {
538                Vector3Df vertex;
539                Read3DVertex(info->f, vertex);
540                mesh.vertices[c].x = vertex.x;
541                mesh.vertices[c].y =-vertex.z;
542                mesh.vertices[c].z = vertex.y;
543             }
544          }
545          else
546             return false;
547          break;
548       }
549       case TRI_MAPPINGCOORS:
550       {
551          int c;
552          uint16 count = ReadWORD(info->f);
553          count = (uint16)Min(mesh.nVertices, count);
554
555          //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
556          *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
557          mesh.flags.texCoords1 = true;
558          if(mesh.texCoords)
559          {
560             for(c = 0; c<count; c++)
561             {
562                mesh.texCoords[c].x = ReadFloat(info->f);
563                mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
564             }
565          }
566          else
567             return false;
568          break;
569       }
570       case TRI_FACEL1:
571       {
572          int c;
573          uint16 nFaces = 0;
574          int count;
575          uint pos;
576
577          info->nFaces = nFaces = ReadWORD(info->f);
578          info->pos += sizeof(uint16);
579
580          info->faces = new0 Face[nFaces];
581          for(c = 0; c<nFaces; c++)
582          {
583             int i;
584             for(i = 0; i<3; i++)
585             {
586                info->faces[c].oldIndices[i] =
587                info->faces[c].indices[i] = ReadWORD(info->f);
588             }
589             ReadWORD(info->f);
590             info->pos += 4*sizeof(uint16);
591          }
592          pos = info->pos;
593          ReadChunks(ReadSmoothing, info, object);
594          info->pos = pos;
595
596          ComputeNormals(mesh, info, object);
597
598          ReadChunks(ReadFacesListChunks, info, object);
599
600          // Add faces without a material all together
601          count = 0;
602          for(c = 0; c<nFaces; c++)
603             if(!info->faces[c].done)
604                count++;
605          if(count)
606          {
607             PrimitiveGroup group = mesh.AddPrimitiveGroup(triangles, count * 3);
608             if(group)
609             {
610                for(c = 0; c<nFaces; c++)
611                   if(!info->faces[c].done)
612                   {
613                      group.indices[c*3]   = info->faces[c].indices[0];
614                      group.indices[c*3+1] = info->faces[c].indices[1];
615                      group.indices[c*3+2] = info->faces[c].indices[2];
616                   }
617                mesh.UnlockPrimitiveGroup(group);
618             }
619          }
620
621          delete info->faces;
622
623          /*
624          mesh.ComputeNormals();
625
626          if(object.flags.flipWindings)
627          {
628             if(mesh.Lock({ normals = true }))
629             {
630                for(c = 0; c<mesh.nVertices; c++)
631                {
632                   mesh.normals[c].x *= -1;
633                   mesh.normals[c].y *= -1;
634                   mesh.normals[c].z *= -1;
635                }
636                mesh.Unlock({ normals = true });
637             }
638          }*/
639
640          // could use this instead? : mesh.ApplyTranslucency(object);
641          break;
642       }
643       case TRI_LOCAL:
644       {
645          int c;
646          Vector3Df xAxis, yAxis, zAxis, center;
647          Vector3Df scaling;
648          Vector3Df orth;
649          Matrix inverse/*, source = { 0 }*/;
650
651          // Local Axes
652          Read3DVertex(info->f, xAxis);
653          Read3DVertex(info->f, yAxis);
654          Read3DVertex(info->f, zAxis);
655          Read3DVertex(info->f, center);
656
657          scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
658          scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
659          scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
660
661          // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
662 /*
663          source.m[3][3] = 1;
664          source.m[0][0] = xAxis.x; source.m[0][1]  = -xAxis.z;  source.m[0][2] = xAxis.y;
665          source.m[1][0] =-zAxis.x; source.m[1][1]  = zAxis.z;  source.m[1][2] = -zAxis.y;
666          source.m[2][0] = yAxis.x; source.m[2][1]  = -yAxis.z;  source.m[2][2] = yAxis.y;
667          source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
668
669          inverse.Inverse(source);
670 */
671
672          object.flags.flipWindings = false;
673
674          xAxis.Normalize(xAxis);
675          yAxis.Normalize(yAxis);
676          zAxis.Normalize(zAxis);
677
678          orth.CrossProduct(yAxis, zAxis);
679          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
680             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
681             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
682          {
683             object.flags.flipWindings ^= true;
684             xAxis = orth;
685          }
686
687          orth.CrossProduct(zAxis, xAxis);
688          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
689             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
690             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
691          {
692             object.flags.flipWindings ^= true;
693             yAxis = orth;
694          }
695
696          orth.CrossProduct(xAxis, yAxis);
697          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
698             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
699             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
700          {
701             object.flags.flipWindings ^= true;
702             zAxis = orth;
703          }
704
705          {
706             Matrix rotation;
707
708             rotation.m[0][0] = xAxis.x;
709             rotation.m[0][1] =-xAxis.z;
710             rotation.m[0][2] = xAxis.y;
711             rotation.m[0][3] = 0;
712             rotation.m[1][0] =-zAxis.x;
713             rotation.m[1][1] = zAxis.z;
714             rotation.m[1][2] =-zAxis.y;
715             rotation.m[1][3] = 0;
716             rotation.m[2][0] = yAxis.x;
717             rotation.m[2][1] =-yAxis.z;
718             rotation.m[2][2] = yAxis.y;
719             rotation.m[2][3] = 0;
720             rotation.m[3][0] = 0;
721             rotation.m[3][1] = 0;
722             rotation.m[3][2] = 0;
723             rotation.m[3][3] = 1;
724
725             {
726                Matrix temp, temp2;
727                inverse.Transpose(rotation);
728                temp.Identity();
729                temp.Translate(-center.x, center.z, -center.y);
730                temp2.Multiply(temp, inverse);
731                temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
732                inverse = temp2;
733             }
734
735             object.transform.scaling = scaling;
736             // TODO: Improve language to support deep properties on non constant functions
737             // object.transform.orientation.RotationMatrix(rotation);
738             {
739                Quaternion orientation;
740                orientation.RotationMatrix(rotation);
741                object.transform.orientation = orientation;
742             }
743             object.transform.position = { center.x, -center.z, center.y };
744          }
745
746          // Localize All Vertices
747          for(c = 0; c<mesh.nVertices; c++)
748          {
749             Vector3Df vertex = mesh.vertices[c];
750
751             mesh.vertices[c].MultMatrix(vertex, inverse);
752
753             mesh.vertices[c].x -= object.pivot.x;
754             mesh.vertices[c].y -= object.pivot.y;
755             mesh.vertices[c].z -= object.pivot.z;
756          }
757          break;
758       }
759    }
760    return true;
761 }
762
763 // Material Library
764 static bool ReadMap(FileInfo * info, Material mat)
765 {
766    DisplaySystem displaySystem = info->displaySystem;
767    switch(info->chunkId)
768    {
769       case MAP_FILENAME:
770       {
771          Bitmap opacityMap = null;
772          char * name;
773          char location[MAX_LOCATION];
774
775          ReadASCIIZ(info->f, &name);
776          strlwr(name);
777
778          strcpy(location, info->textureDirectory);
779          PathCat(location, name);
780
781          if(!mat.baseMap)
782          {
783             mat.baseMap = displaySystem.GetTexture(name);
784             if(!mat.baseMap)
785             {
786                mat.baseMap = Bitmap { };
787                if(!mat.baseMap.Load(location, null, null) ||
788                   !mat.baseMap.Convert(null, pixelFormat888, null) ||
789                   !displaySystem.AddTexture(name, mat.baseMap))
790                {
791                   delete mat.baseMap;
792                }
793                opacityMap = mat.baseMap;
794             }
795          }
796          else if(info->parent->chunkId == MAT_MAPOPACITY)
797          {
798             opacityMap = Bitmap { };
799             if(!opacityMap.Load(location, null, null) ||
800                !opacityMap.Convert(null, pixelFormat888, null))
801             {
802                delete opacityMap;
803             }
804          }
805
806          if(mat.baseMap)
807          {
808             if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap)
809             {
810                unsigned int c;
811                ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
812
813                for(c = 0; c < opacityMap.width * opacityMap.height; c++)
814                   picture[c] = ColorAlpha { ((ColorAlpha *)opacityMap.picture)[c].color.r, picture[c].color };
815             }
816             mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
817             mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
818          }
819          if(opacityMap != mat.baseMap)
820             delete opacityMap;
821          delete name;
822          break;
823       }
824       case MAP_OPTIONS:
825       {
826          uint16 options = ReadWORD(info->f);
827          if(!(options & 0x10)) mat.flags.tile = true;
828          break;
829       }
830       case MAP_1_U_SCALE:
831          mat.uScale = ReadFloat(info->f);
832          break;
833       case MAP_1_V_SCALE:
834          mat.vScale = ReadFloat(info->f);
835          break;
836    }
837    return true;
838 }
839
840 static bool ReadMaterial(FileInfo * info, Material mat)
841 {
842    switch(info->chunkId)
843    {
844       case MAT_NAME:
845       {
846          ReadASCIIZ(info->f, &mat.name);
847          break;
848       }
849       case MAT_TRANSPARENCY:
850       {
851          uint16 transparency;
852          ReadChunks(ReadAmountOf, info, &transparency);
853          mat.opacity = 1.0f - transparency / 100.0f;
854          if(mat.opacity < 1.0)
855             mat.flags.translucent = true;
856          break;
857       }
858       case MAT_DIFFUSE:
859       {
860          ReadChunks(ReadRGB, info, &mat.diffuse);
861          ReadChunks(ReadRGB, info, &mat.diffuse);
862          break;
863       }
864       case MAT_AMBIENT:
865       {
866          ReadChunks(ReadRGB, info, &mat.ambient);
867          ReadChunks(ReadRGB, info, &mat.ambient);
868          break;
869       }
870       case MAT_SPECULAR:
871       {
872          ReadChunks(ReadRGB, info, &mat.specular);
873          ReadChunks(ReadRGB, info, &mat.specular);
874          break;
875       }
876       case MAT_SELFILLUM:
877       {
878          uint16 emissive;
879          ReadChunks(ReadAmountOf, info, &emissive);
880          mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
881          mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
882          mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
883          break;
884       }
885       case MAT_SHINSTRENGTH:
886       {
887          uint16 shininess;
888          ReadChunks(ReadAmountOf, info, &shininess);
889          mat.specular.r *= shininess / 100.0f;
890          mat.specular.g *= shininess / 100.0f;
891          mat.specular.b *= shininess / 100.0f;
892          break;
893       }
894       case MAT_SHININESS:
895       {
896          uint16 power;
897          ReadChunks(ReadAmountOf, info, &power);
898          mat.power = power;
899          break;
900       }
901       case MAT_MAPTEXTURE1:
902          ReadChunks(ReadMap, info, mat);
903          break;
904       case MAT_MAPOPACITY:
905          ReadChunks(ReadMap, info, mat);
906          mat.flags.translucent = true;
907          break;
908       case MAT_DOUBLESIDED:
909          mat.flags.doubleSided = true;
910          break;
911    }
912    return true;
913 }
914
915 // Lights
916 static bool ReadLight(FileInfo * info, Object object)
917 {
918    Mesh mesh = object.mesh;
919    Light * light = &object.light;
920    switch(info->chunkId)
921    {
922       case RGB_BYTE:
923       case RGB_BYTE_GAMMA:
924       case RGB_FLOAT:
925       case RGB_FLOAT_GAMMA:
926          ReadRGB(info, &light->diffuse);
927          light->specular = light->diffuse;
928          break;
929       case LIT_SPOT:
930       {
931          Object target;
932          char targetName[MAXNAMELEN];
933
934          strcpy(targetName, object.name);
935          strcat(targetName, ".target");
936
937          light->flags.omni = false;
938          light->flags.spot = true;
939
940          target = Object { };
941          target.name = CopyString(targetName);
942          info->rootObject.children.AddName(target);
943          target.parent = info->rootObject;
944
945          light->target = target;
946
947          target.transform.position.x = ReadFloat(info->f);
948          target.transform.position.z = ReadFloat(info->f);
949          target.transform.position.y =-ReadFloat(info->f);
950
951          light->hotSpot = ReadFloat(info->f);
952          light->fallOff = ReadFloat(info->f);
953          break;
954       }
955       case LIT_ONOFF:
956       {
957          light->flags.off = true;
958          break;
959       }
960       case LIT_ATTENUATION:
961       {
962          /* solve (
963             {
964                d = 300, small = 0.001,
965                Kl = 0, Kc = 0,
966                d * (Kl + Kq * d) = (1 / small) - Kc
967             },
968             { Kc, Kl, Kq, small, d });
969          */
970
971          light->flags.attenuation = true;
972
973          /*
974          #define MINLIGHT     0.08
975          light->Kq = 1/(light->end*light->end*MINLIGHT);
976          */
977
978          #define MINLIGHT     0.15f
979          // #define MINLIGHT     0.1
980          light->Kl = (float)(1/(light->end*MINLIGHT));
981
982          break;
983       }
984       case LIT_START:
985       {
986          light->start = ReadFloat(info->f);
987          break;
988       }
989       case LIT_END:
990       {
991          light->end = ReadFloat(info->f);
992          break;
993       }
994       case LIT_MULTIPLIER:
995       {
996          light->multiplier = ReadFloat(info->f);
997          break;
998       }
999    }
1000    return true;
1001 }
1002
1003 // Cameras
1004 static bool ReadCamera(FileInfo * info, Object object)
1005 {
1006    Mesh mesh = object.mesh;
1007    switch(info->chunkId)
1008    {
1009       case CAM_SEECONE:
1010       {
1011          break;
1012       }
1013       case CAM_RANGES:
1014       {
1015          Camera camera = object.camera;
1016          float nearRange = ReadFloat(info->f);
1017          float farRange = ReadFloat(info->f);
1018          /*
1019          camera.zMin = Max(0.1, nearRange);
1020          camera.zMax = farRange;
1021          */
1022          break;
1023       }
1024    }
1025    return true;
1026 }
1027
1028 // Edit Chunks
1029 static bool ReadEditObject(FileInfo * info, char * name)
1030 {
1031    DisplaySystem displaySystem = info->displaySystem;
1032    switch(info->chunkId)
1033    {
1034       case OBJ_TRIMESH:
1035       {
1036          Object object = info->rootObject.Find(name);
1037          if(!object)
1038          {
1039             object = Object { };
1040             object.name = name;
1041             info->rootObject.children.AddName(object);
1042             object.parent = info->rootObject;
1043          }
1044          else
1045             delete name;
1046          object.InitializeMesh(displaySystem);
1047          ReadChunks(ReadTriMesh, info, object);
1048          object.flags.mesh = true;
1049          object.mesh.Unlock(0);
1050          break;
1051       }
1052       case OBJ_LIGHT:
1053       {
1054          Object object = info->rootObject.Find(name);
1055          Light * light;
1056          Vector3Df position;
1057
1058          if(!object)
1059          {
1060             object = Object { };
1061             object.name = name;
1062             info->rootObject.children.AddName(object);
1063             object.parent = info->rootObject;
1064          }
1065          else
1066             delete name;
1067          object.flags.light = true;
1068
1069          light = &object.light;
1070          light->lightObject = object;
1071          light->flags.omni = true;
1072          light->multiplier = 1.0f;
1073
1074          // This is not used?
1075          Read3DVertex(info->f, position);
1076          light->direction = { position.x, position.y, position.z };
1077          info->pos += sizeof(float) * 3;
1078
1079          ReadChunks(ReadLight, info, object);
1080          break;
1081       }
1082       case OBJ_CAMERA:
1083       {
1084          char targetName[MAXNAMELEN];
1085          Object object = info->rootObject.Find(name);
1086          Object target;
1087          Camera camera;
1088          float bankAngle, focus;
1089          double mm;
1090
1091          strcpy(targetName, name);
1092          strcat(targetName, ".target");
1093
1094          if(!object)
1095          {
1096             object = Object { };
1097             object.name = name;
1098             info->rootObject.children.AddName(object);
1099
1100             object.parent = info->rootObject;
1101             object.camera = Camera { };
1102             object.camera.type = lookAtObject;
1103
1104             target = Object { };
1105             target.name = CopyString(targetName);
1106             info->rootObject.children.AddName(target);
1107             target.parent = info->rootObject;
1108          }
1109          else
1110             delete name;
1111
1112          object.flags.camera = true;
1113          object.cameraTarget = target;
1114
1115          camera = object.camera;
1116          camera.cameraObject = object;
1117          camera.target = target;
1118
1119          //Read3DVertex(info->f, camera.position);
1120          object.transform.position.x = ReadFloat(info->f);
1121          object.transform.position.z = ReadFloat(info->f);
1122          object.transform.position.y =-ReadFloat(info->f);
1123
1124          info->pos += sizeof(float) * 3;
1125          //Read3DVertex(info->f, object.cameraTarget.position);
1126          target.transform.position.x = ReadFloat(info->f);
1127          target.transform.position.z = ReadFloat(info->f);
1128          target.transform.position.y =-ReadFloat(info->f);
1129
1130          info->pos += sizeof(float) * 3;
1131          bankAngle = ReadFloat(info->f);
1132          info->pos += sizeof(float);
1133          focus = ReadFloat(info->f);
1134          info->pos += sizeof(float);
1135
1136          mm = (focus - 5.05659508373109) / 1.13613250717301;
1137          camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1138
1139          ReadChunks(ReadCamera, info, object);
1140          break;
1141       }
1142       case OBJ_HIDDEN: break;
1143       default:
1144          delete name;
1145    }
1146    return true;
1147 }
1148
1149 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1150
1151 static bool ReadEditChunks(FileInfo * info, void * data)
1152 {
1153    switch(info->chunkId)
1154    {
1155       case EDIT_AMBIENT:
1156       {
1157          // Read the ambient color
1158          ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1159          break;
1160       }
1161       case EDIT_MATERIAL:
1162       {
1163          Material material { };
1164          Material mat;
1165          ReadChunks(ReadMaterial, info, material);
1166
1167          mat = info->displaySystem.AddNamedMaterial(material.name);
1168          if(mat)
1169          {
1170             if(material.baseMap)
1171                material.baseMap.MakeMipMaps(info->displaySystem);
1172             // COPY_NITEM(mat, material);
1173             CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1174          }
1175          else
1176          {
1177             delete material.baseMap;
1178          }
1179          delete material.name;
1180          delete material;
1181          break;
1182       }
1183       case EDIT_OBJECT:
1184       {
1185          char * name;
1186          info->pos += ReadASCIIZ(info->f, &name);
1187          ReadChunks(ReadEditObject, info, name);
1188          break;
1189       }
1190    }
1191    return true;
1192 }
1193
1194 struct ObjectInfoBlock
1195 {
1196    OldList tracks;
1197    short hierarchy;
1198    short parent;
1199    char * name;
1200    char * dummyName;
1201    Vector3Df pivot;
1202 };
1203
1204 // Key Framer Chunks
1205
1206 #define ACCFLAG_TENSION    0x00000001
1207 #define ACCFLAG_CONTINUITY 0x00000002
1208 #define ACCFLAG_BIAS       0x00000004
1209 #define ACCFLAG_EASETO     0x00000008
1210 #define ACCFLAG_EASEFROM   0x00000010
1211
1212 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1213 {
1214    switch(info->chunkId)
1215    {
1216       case FRM_PARAM:
1217       {
1218          uint16 flags1, flags2;
1219          ReadASCIIZ(info->f, &block->name);
1220          flags1 = ReadWORD(info->f);
1221          flags2 = ReadWORD(info->f);
1222          block->parent = ReadWORD(info->f);
1223          break;
1224       }
1225       case FRM_DUMMYNAME:
1226          ReadASCIIZ(info->f, &block->dummyName);
1227          break;
1228       case FRM_PIVOT:
1229          Read3DVertex(info->f, block->pivot);
1230          break;
1231       case FRM_HIERARCHY:
1232          block->hierarchy = ReadWORD(info->f);
1233          break;
1234       case FRM_TRACKPOS:
1235       case FRM_TRACKROT:
1236       case FRM_TRACKSCALE:
1237       case FRM_TRACKROLL:
1238       case FRM_TRACKFOV:
1239       case FRM_TRACKCOLOR:
1240       case FRM_TRACKHOTSPOT:
1241       case FRM_TRACKFALLOFF:
1242       {
1243          FrameTrack track { };
1244          if(track)
1245          {
1246             uint16 flags;
1247             byte unknown[8];
1248             uint c;
1249
1250             block->tracks.Add(track);
1251
1252             flags = ReadWORD(info->f);
1253
1254             info->f.Read(unknown, sizeof(unknown), 1);
1255
1256             track.numKeys = ReadDWORD(info->f);
1257
1258             switch(info->chunkId)
1259             {
1260                case FRM_TRACKPOS: track.type.type = position; break;
1261                case FRM_TRACKROT: track.type.type = rotation; break;
1262                case FRM_TRACKSCALE: track.type.type = scaling; break;
1263                case FRM_TRACKROLL: track.type.type = roll; break;
1264                case FRM_TRACKFOV: track.type.type = fov; break;
1265                case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1266                case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1267                case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1268             }
1269             if((flags & 0x0003) == 3)
1270                track.type.loop = true;
1271
1272             track.keys = new0 FrameKey[track.numKeys];
1273             for(c = 0; c<track.numKeys; c++)
1274             {
1275                uint16 accelerationFlags;
1276                FrameKey * key = track.keys + c;
1277
1278                key->frame = ReadDWORD(info->f);
1279                accelerationFlags = ReadWORD(info->f);
1280
1281                if(accelerationFlags & ACCFLAG_TENSION)
1282                   key->tension = ReadFloat(info->f);
1283                if(accelerationFlags & ACCFLAG_CONTINUITY)
1284                   key->continuity = ReadFloat(info->f);
1285                if(accelerationFlags & ACCFLAG_BIAS)
1286                   key->bias = ReadFloat(info->f);
1287                if(accelerationFlags & ACCFLAG_EASETO)
1288                   key->easeTo = ReadFloat(info->f);
1289                if(accelerationFlags & ACCFLAG_EASEFROM)
1290                   key->easeFrom = ReadFloat(info->f);
1291
1292                switch(info->chunkId)
1293                {
1294                   case FRM_TRACKPOS:
1295                   {
1296                      Vector3Df position;
1297                      Read3DVertex(info->f, position);
1298                      key->position = { position.x, -position.z, position.y };
1299                      break;
1300                   }
1301                   case FRM_TRACKROT:
1302                   {
1303                      Vector3Df axis;
1304                      Angle angle = ReadFloat(info->f);
1305                      Vector3Df fixedAxis;
1306
1307                      Read3DVertex(info->f, axis);
1308                      fixedAxis.x = axis.x;
1309                      fixedAxis.y = -axis.z;
1310                      fixedAxis.z = axis.y;
1311
1312                      if(c > 0)
1313                      {
1314                         Quaternion rotation;
1315                         rotation.RotationAxis(fixedAxis, angle);
1316                         key->orientation.Multiply((key - 1)->orientation, rotation);
1317                      }
1318                      else
1319                         key->orientation.RotationAxis(fixedAxis, angle);
1320                      break;
1321                   }
1322                   case FRM_TRACKSCALE:
1323                   {
1324                      Vector3Df scaling;
1325                      Read3DVertex(info->f, scaling);
1326                      key->scaling = { scaling.x, scaling.z, scaling.y };
1327                      break;
1328                   }
1329                   case FRM_TRACKFOV:
1330                   {
1331                      key->fov = ReadFloat(info->f);
1332                      break;
1333                   }
1334                   case FRM_TRACKROLL:
1335                   {
1336                      key->roll = -ReadFloat(info->f);
1337                      break;
1338                   }
1339                   case FRM_TRACKCOLOR:
1340                   {
1341                      FileInfo childInfo = *info;
1342                      childInfo.chunkId = RGB_FLOAT;
1343                      ReadRGB(&childInfo, &key->color);
1344                      break;
1345                   }
1346                   case FRM_TRACKHOTSPOT:
1347                   {
1348                      key->hotSpot = ReadFloat(info->f);
1349                      break;
1350                   }
1351                   case FRM_TRACKFALLOFF:
1352                   {
1353                      key->fallOff = ReadFloat(info->f);
1354                      break;
1355                   }
1356                }
1357             }
1358          }
1359          break;
1360       }
1361    }
1362    return true;
1363 }
1364
1365 static Object FindObjectID(Object object, int id)
1366 {
1367    Object result = null;
1368    Object child;
1369    for(child = object.children.first; child; child = child.next)
1370    {
1371       if(child.flags.hierarchy == (uint16) id)
1372       {
1373          result = child;
1374          break;
1375       }
1376       else
1377       {
1378          result = FindObjectID(child, id);
1379          if(result) break;
1380       }
1381    }
1382    return result;
1383 }
1384
1385 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1386 {
1387    switch(info->chunkId)
1388    {
1389       case FRM_MESHINFO:
1390       {
1391          ObjectInfoBlock block { };
1392          Object object;
1393
1394          ReadChunks(ReadFrameInfoBlock, info, &block);
1395
1396          if(block.dummyName && block.dummyName[0])
1397          {
1398             if(!strcmp(block.name, "$$$DUMMY"))
1399             {
1400                object = Object { };
1401                object.name = block.dummyName;
1402                info->rootObject.children.AddName(object);
1403                object.transform.scaling = { 1,1,1 };
1404             }
1405             else
1406             {
1407                Object model = info->rootObject.Find(block.name);
1408                if(model)
1409                {
1410                   object = Object { };
1411                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1412                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1413                   object.flags = model.flags;
1414                   object.flags.ownMesh = false;
1415                   object.mesh = model.mesh;
1416                   /*
1417                   object.min = model.min;
1418                   object.max = model.max;
1419                   object.radius = model.radius;
1420                   */
1421                   object.transform = model.transform;
1422                   info->rootObject.children.AddName(object);
1423                }
1424                delete block.dummyName;
1425             }
1426             object.parent = info->rootObject;
1427          }
1428          else
1429             object = info->rootObject.Find(block.name);
1430
1431          if(object)
1432          {
1433             Mesh mesh = object.mesh;
1434             object.flags.hierarchy = block.hierarchy + 1;
1435             if(block.parent != -1)
1436             {
1437                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1438                if(parent)
1439                {
1440                   object.parent.children.Remove(object);
1441                   parent.children.AddName(object);
1442                   object.parent = parent;
1443                }
1444             }
1445             object.pivot.x = block.pivot.x;
1446             object.pivot.y =-block.pivot.z;
1447             object.pivot.z = block.pivot.y;
1448
1449             if(mesh && object.flags.ownMesh)
1450             {
1451                if(mesh.Lock({ vertices = true }))
1452                {
1453                   int c;
1454                   // Take pivot into account
1455                   for(c = 0; c<mesh.nVertices; c++)
1456                   {
1457                      mesh.vertices[c].x -= object.pivot.x;
1458                      mesh.vertices[c].y -= object.pivot.y;
1459                      mesh.vertices[c].z -= object.pivot.z;
1460                   }
1461                   mesh.Unlock({ vertices = true });
1462                }
1463             }
1464
1465             object.tracks = block.tracks;
1466             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1467             object.endFrame = info->rootObject.endFrame;
1468          }
1469          /*else
1470             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1471          delete block.name;
1472          break;
1473       }
1474       case FRM_CAMERA:
1475       {
1476          ObjectInfoBlock block { };
1477          Object object;
1478
1479          ReadChunks(ReadFrameInfoBlock, info, &block);
1480
1481          if(block.dummyName && block.dummyName[0])
1482          {
1483             if(!strcmp(block.name, "$$$DUMMY"))
1484             {
1485                object = Object { };
1486                object.name = block.dummyName;
1487                info->rootObject.children.AddName(object);
1488                object.transform.scaling = { 1, 1, 1 };
1489                object.flags.camera = true;
1490             }
1491             else
1492             {
1493                Object model = info->rootObject.Find(block.name);
1494                if(model)
1495                {
1496                   object = Object { };
1497                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1498                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1499                   object.flags = model.flags;
1500                   object.flags.ownMesh = false;
1501                   object.camera = model.camera;
1502                   object.flags.camera = true;
1503                   info->rootObject.children.AddName(object);
1504                }
1505                delete block.dummyName;
1506             }
1507             object.parent = info->rootObject;
1508          }
1509          else
1510             object = info->rootObject.Find(block.name);
1511
1512          if(object)
1513          {
1514             object.flags.hierarchy = block.hierarchy + 1;
1515             if(block.parent != -1)
1516             {
1517                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1518                if(parent)
1519                {
1520                   object.parent.children.Remove(object);
1521                   parent.children.AddName(object);
1522                   object.parent = parent;
1523                }
1524             }
1525             object.pivot.x = block.pivot.x;
1526             object.pivot.y =-block.pivot.z;
1527             object.pivot.z = block.pivot.y;
1528
1529             object.tracks = block.tracks;
1530             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1531             object.endFrame = info->rootObject.endFrame;
1532          }
1533          /*else
1534             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1535          delete block.name;
1536          break;
1537       }
1538       case FRM_CAMERATARGET:
1539       {
1540          ObjectInfoBlock block { };
1541          Object object;
1542          char targetName[MAXNAMELEN];
1543
1544          ReadChunks(ReadFrameInfoBlock, info, &block);
1545
1546          strcpy(targetName, block.name);
1547          strcat(targetName, ".target");
1548
1549          if(block.dummyName && block.dummyName[0])
1550          {
1551             if(!strcmp(block.name, "$$$DUMMY"))
1552             {
1553                object = Object { };
1554                object.name = block.dummyName;
1555                info->rootObject.children.AddName(object);
1556                object.transform.scaling = { 1,1,1 };
1557             }
1558             else
1559             {
1560                Object model = info->rootObject.Find(targetName);
1561                if(model)
1562                {
1563                   object = Object { };
1564                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1565                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1566                   object.flags = model.flags;
1567                   object.flags.ownMesh = false;
1568                   object.camera = model.camera;
1569                   info->rootObject.children.AddName(object);
1570                }
1571                delete block.dummyName;
1572             }
1573             object.parent = info->rootObject;
1574          }
1575          else
1576             object = info->rootObject.Find(targetName);
1577
1578          if(object)
1579          {
1580             object.flags.hierarchy = block.hierarchy + 1;
1581             if(block.parent != -1)
1582             {
1583                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1584                if(parent)
1585                {
1586                   object.parent.children.Remove(object);
1587                   parent.children.AddName(object);
1588                   object.parent = parent;
1589                }
1590             }
1591             object.pivot.x = block.pivot.x;
1592             object.pivot.y =-block.pivot.z;
1593             object.pivot.z = block.pivot.y;
1594
1595             object.tracks = block.tracks;
1596             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1597             object.endFrame = info->rootObject.endFrame;
1598          }
1599          /*else
1600             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1601          delete block.name;
1602          break;
1603       }
1604       case FRM_AMBIENT:
1605       {
1606          ObjectInfoBlock block { };
1607
1608          ReadChunks(ReadFrameInfoBlock, info, &block);
1609
1610          info->rootObject.tracks = block.tracks;
1611          break;
1612       }
1613       case FRM_OMNILIGHT:
1614       case FRM_SPOTLIGHT:
1615       {
1616          ObjectInfoBlock block { };
1617          Object object;
1618
1619          ReadChunks(ReadFrameInfoBlock, info, &block);
1620
1621          if(block.dummyName && block.dummyName[0])
1622          {
1623             if(!strcmp(block.name, "$$$DUMMY"))
1624             {
1625                object = Object { };
1626                object.name = block.dummyName;
1627                info->rootObject.children.AddName(object);
1628                object.transform.scaling = { 1, 1, 1 };
1629                object.flags.light = true;
1630             }
1631             else
1632             {
1633                Object model = info->rootObject.Find(block.name);
1634                if(model)
1635                {
1636                   object = Object { };
1637                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1638                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1639                   object.flags = model.flags;
1640                   object.flags.ownMesh = false;
1641                   object.light = model.light;
1642                   object.flags.light = true;
1643                   info->rootObject.children.AddName(object);
1644                }
1645                delete block.dummyName;
1646             }
1647             object.parent = info->rootObject;
1648          }
1649          else
1650             object = info->rootObject.Find(block.name);
1651
1652          if(object)
1653          {
1654             object.flags.hierarchy = block.hierarchy + 1;
1655             if(block.parent != -1)
1656             {
1657                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1658                if(parent)
1659                {
1660                   object.parent.children.Remove(object);
1661                   parent.children.AddName(object);
1662                   object.parent = parent;
1663                }
1664             }
1665             object.pivot.x = block.pivot.x;
1666             object.pivot.y =-block.pivot.z;
1667             object.pivot.z = block.pivot.y;
1668
1669             object.tracks = block.tracks;
1670             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1671             object.endFrame = info->rootObject.endFrame;
1672          }
1673          /*else
1674             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1675          delete block.name;
1676          break;
1677       }
1678       case FRM_SPOTLIGHTTARGET:
1679       {
1680          ObjectInfoBlock block { };
1681          Object object;
1682          char targetName[MAXNAMELEN];
1683
1684          ReadChunks(ReadFrameInfoBlock, info, &block);
1685
1686          strcpy(targetName, block.name);
1687          strcat(targetName, ".target");
1688
1689          if(block.dummyName && block.dummyName[0])
1690          {
1691             if(!strcmp(block.name, "$$$DUMMY"))
1692             {
1693                object = Object { };
1694                object.name = block.dummyName;
1695                info->rootObject.children.AddName(object);
1696                object.transform.scaling = { 1,1,1 };
1697             }
1698             else
1699             {
1700                Object model = info->rootObject.Find(targetName);
1701                if(model)
1702                {
1703                   object = Object { };
1704                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1705                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1706                   object.flags = model.flags;
1707                   object.flags.ownMesh = false;
1708                   object.light = model.light;
1709                   info->rootObject.children.AddName(object);
1710                }
1711                delete block.dummyName;
1712             }
1713             object.parent = info->rootObject;
1714          }
1715          else
1716             object = info->rootObject.Find(targetName);
1717
1718          if(object)
1719          {
1720             object.flags.hierarchy = block.hierarchy + 1;
1721             if(block.parent != -1)
1722             {
1723                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1724                if(parent)
1725                {
1726                   object.parent.children.Remove(object);
1727                   parent.children.AddName(object);
1728                   object.parent = parent;
1729                }
1730             }
1731             object.pivot.x = block.pivot.x;
1732             object.pivot.y =-block.pivot.z;
1733             object.pivot.z = block.pivot.y;
1734
1735             object.tracks = block.tracks;
1736             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1737             object.endFrame = info->rootObject.endFrame;
1738          }
1739          /*else
1740             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1741          delete block.name;
1742          break;
1743       }
1744       case FRM_FRAMES:
1745       {
1746          info->rootObject.startFrame = ReadDWORD(info->f);
1747          info->rootObject.endFrame = ReadDWORD(info->f);
1748          *&(info->rootObject.frame) = info->rootObject.startFrame;
1749          break;
1750       }
1751    }
1752    return true;
1753 }
1754
1755 // Main Chunks
1756 static bool ReadMainChunks(FileInfo * info, void * data)
1757 {
1758    switch(info->chunkId)
1759    {
1760       case EDIT3DS:
1761          ReadChunks(ReadEditChunks, info, null);
1762          break;
1763       case KEYFRAME3DS:
1764       {
1765          Object object = data;
1766          if(!(object.flags.keysLoaded)) // Don't read key frames on reload
1767          {
1768             ReadChunks(ReadKeyFrameChunks, info, null);
1769             object.flags.keysLoaded = true;
1770          }
1771          break;
1772       }
1773    }
1774    return true;
1775 }
1776
1777 static bool ReadMain(FileInfo * info, void * data)
1778 {
1779    switch(info->chunkId)
1780    {
1781       case MAIN3DS:
1782          ReadChunks(ReadMainChunks, info, data);
1783          break;
1784    }
1785    return true;
1786 }
1787
1788 class Object3DSFormat : ObjectFormat
1789 {
1790    class_property(extension) = "3ds";
1791
1792    bool Load(Object object, char * fileName, DisplaySystem displaySystem)
1793    {
1794       bool result = false;
1795       if(fileName)
1796       {
1797          FileInfo info = {0};
1798          info.rootObject = object;
1799          info.displaySystem = displaySystem;
1800          info.pos = 0;
1801          info.end = 1;
1802          StripLastDirectory(fileName, info.textureDirectory);
1803          info.f = FileOpen(fileName, read);
1804          if(info.f)
1805          {
1806             if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
1807             {
1808                object.Animate(object.frame);
1809                object.flags.root = true;
1810                object.SetMinMaxRadius(true);
1811                result = true;
1812             }
1813             delete info.f;
1814          }
1815       }
1816       if(!result)
1817          object.Free(displaySystem);
1818       return result;
1819    }
1820 }