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