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