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