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