1 namespace gfx3D::models;
8 #define RGB_FLOAT 0x0010
9 #define RGB_BYTE 0x0011
10 #define RGB_BYTE_GAMMA 0x0012
11 #define RGB_FLOAT_GAMMA 0x0013
14 #define AMOUNT_OF 0x0030
16 #define MAIN3DS 0x4D4D
17 #define EDIT3DS 0x3D3D
18 #define EDIT_AMBIENT 0x2100
19 #define EDIT_MATERIAL 0xAFFF
20 #define EDIT_OBJECT 0x4000
21 #define OBJ_HIDDEN 0x4010
22 #define OBJ_TRIMESH 0x4100
23 #define OBJ_LIGHT 0x4600
24 #define OBJ_CAMERA 0x4700
26 // Triangular Mesh Chunks
27 #define TRI_VERTEXL 0x4110
28 #define TRI_FACEL1 0x4120
29 #define TRI_MATERIAL 0x4130
30 #define TRI_MAPPINGCOORS 0x4140
31 #define TRI_SMOOTHING 0x4150
32 #define TRI_LOCAL 0x4160
35 #define LIT_SPOT 0x4610
36 #define LIT_ONOFF 0x4620
37 #define LIT_ATTENUATION 0x4625
38 #define LIT_START 0x4659
39 #define LIT_END 0x465A
40 #define LIT_MULTIPLIER 0x465B
43 #define CAM_SEECONE 0x4710
44 #define CAM_RANGES 0x4720
47 #define MAT_NAME 0xA000
48 #define MAT_AMBIENT 0xA010
49 #define MAT_DIFFUSE 0xA020
50 #define MAT_SPECULAR 0xA030
51 #define MAT_SHININESS 0xA040
52 #define MAT_SHINSTRENGTH 0xA041
53 #define MAT_SHIN3PC 0xA042
54 #define MAT_TRANSPARENCY 0xA050
55 #define MAT_XPFALL 0xA052
56 #define MAT_REFBLUR 0xA053
57 #define MAT_SELFILLUM 0xA080
58 #define MAT_DOUBLESIDED 0xA081
59 #define MAT_ADDITIVE 0xA083
60 #define MAT_SELFILPCT 0xA084
61 #define MAT_WIRE 0xA085
62 #define MAT_SUPERSMP 0xA086
63 #define MAT_WIRETHICKNESS 0xA087
64 #define MAT_FACEMAP 0xA088
65 #define MAT_XPFALLIN 0xA08A
66 #define MAT_PHONG 0xA08C
67 #define MAT_WIREABS 0xA08E
68 #define MAT_SHADING 0xA100
69 #define MAT_MAPTEXTURE1 0xA200
70 #define MAT_SPECULARMAP 0xA204
71 #define MAT_MAPOPACITY 0xA210
72 #define MAT_REFLECTIONMAP 0xA220
73 #define MAT_BUMPMAP 0xA230
76 #define MAP_FILENAME 0xA300
78 #define MAT_SHININESSMAP 0xA33C
79 #define MAT_EMISSIVEMAP 0xA33D
80 #define MAP_OPTIONS 0xA351
81 #define MAP_1_U_SCALE 0xA354
82 #define MAP_1_V_SCALE 0xA356
83 #define MAP_U_OFFSET 0xA358
84 #define MAP_V_OFFSET 0xA35A
85 #define MAP_ROTATION 0xA35C
87 #define MAP_OPTIONS_DECAL 0x0001 // (related to MAP_OPTIONS_DONTTILE)
88 #define MAP_OPTIONS_MIRROR 0x0002
89 #define MAP_OPTIONS_NEGATIVE 0x0008
90 #define MAP_OPTIONS_DONTTILE 0x0010
91 #define MAP_OPTIONS_SUMMEDFILTERING 0x0020
92 #define MAP_OPTIONS_USEALPHA 0x0040
93 #define MAP_OPTIONS_LUMORALPHATINT 0x0080
94 #define MAP_OPTIONS_IGNOREALPHA 0x0100
95 #define MAP_OPTIONS_RGBTINT 0x0200
97 #define MAP_FILTERBLUR 0xA353
98 #define MAP_1_U_SCALE 0xA354
99 #define MAP_1_V_SCALE 0xA356
100 #define MAP_U_OFFSET 0xA358
101 #define MAP_V_OFFSET 0xA35A
102 #define MAP_ROTATION 0xA35C
104 #define MAP_LUMTINT1 0xA360
105 #define MAP_LUMTINT2 0xA362
107 #define MAP_TINTR 0xA364
108 #define MAP_TINTG 0xA366
109 #define MAP_TINTB 0xA368
112 #define KEYFRAME3DS 0xB000
113 #define FRM_AMBIENT 0xB001
114 #define FRM_MESHINFO 0xB002
115 #define FRM_CAMERA 0xB003
116 #define FRM_CAMERATARGET 0xB004
117 #define FRM_OMNILIGHT 0xB005
118 #define FRM_SPOTLIGHTTARGET 0xB006
119 #define FRM_SPOTLIGHT 0xB007
120 #define FRM_FRAMES 0xB008
121 #define FRM_PARAM 0xB010
122 #define FRM_DUMMYNAME 0xB011
123 #define FRM_PIVOT 0xB013
124 #define FRM_TRACKPOS 0xB020
125 #define FRM_TRACKROT 0xB021
126 #define FRM_TRACKSCALE 0xB022
127 #define FRM_TRACKFOV 0xB023
128 #define FRM_TRACKROLL 0xB024
129 #define FRM_TRACKCOLOR 0xB025
130 #define FRM_TRACKMORPH 0xB026 // What does this do?
131 #define FRM_TRACKHOTSPOT 0xB027
132 #define FRM_TRACKFALLOFF 0xB028
133 #define FRM_TRACKHIDE 0xB029
134 #define FRM_HIERARCHY 0xB030
136 typedef struct FileInfo FileInfo;
141 uint16 oldIndices[3];
149 DisplaySystem displaySystem;
158 char textureDirectory[MAX_DIRECTORY];
161 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
162 | (((unsigned short)(word) & 0xff00) >> 8)
164 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
165 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
166 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
167 | (((unsigned int)(dword) & 0xff000000) >> 24))
169 #ifndef __BIG_ENDIAN__
170 #define BIGENDSWAP_WORD(word)
171 #define BIGENDSWAP_DWORD(dword)
173 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
174 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
177 // Zero Terminated String
178 static int ReadASCIIZ(File f, char ** string)
180 // *** Read String ***
182 char temp[1024] = "";
186 if(!temp[c++]) break;
188 *string = new char[c];
190 strcpy(*string, temp);
194 static float ReadFloat(File f)
197 f.Read(&floatValue, sizeof(float), 1);
198 BIGENDSWAP_DWORD(floatValue);
202 static uint16 ReadWORD(File f)
205 f.Read(&wordValue, sizeof(uint16), 1);
206 BIGENDSWAP_WORD(wordValue);
210 static uint ReadDWORD(File f)
213 f.Read(&dwordValue, sizeof(uint), 1);
214 BIGENDSWAP_DWORD(dwordValue);
219 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
221 for(;info->pos < info->end;)
223 FileInfo childInfo = *info;
226 childInfo.parent = info;
228 info->f.Seek(info->pos, start);
229 childInfo.chunkId = ReadWORD(info->f);
230 length = ReadDWORD(info->f);
232 childInfo.pos += sizeof(uint16) + sizeof(uint);
233 childInfo.end = info->pos + length;
235 if(!chunkParser(&childInfo, data))
238 info->pos = childInfo.end;
244 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
246 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
249 info->f.Getc(&value); rgb->r = value / 255.0f;
250 info->f.Getc(&value); rgb->g = value / 255.0f;
251 info->f.Getc(&value); rgb->b = value / 255.0f;
253 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
255 rgb->r = ReadFloat(info->f);
256 rgb->g = ReadFloat(info->f);
257 rgb->b = ReadFloat(info->f);
262 static bool Read3DVertex(File f, Vector3Df vertex)
264 vertex.x = ReadFloat(f);
265 vertex.y = ReadFloat(f);
266 vertex.z = ReadFloat(f);
270 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
272 if(info->chunkId == AMOUNT_OF)
273 *amountOf = ReadWORD(info->f);
286 VertexConfig * config;
289 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
292 Face * faces = info->faces;
293 //int nFaces = info->nFaces;
294 int nVertices = mesh.nVertices;
299 VertexConfigList * configLists = new0 VertexConfigList[nVertices];
301 nNewVertices = nVertices;
302 for(c = 0; c<info->nFaces; c++)
304 Face * face = &faces[c];
309 int index = face->indices[i];
311 VertexConfigList * configList = &configLists[index];
312 VertexConfig * config = null;
313 for(v = 0; v<configList->numConfig; v++)
315 uint smoothGroups = configList->config[v].smoothGroups;
316 if(smoothGroups == face->smoothGroups)
318 config = &configList->config[v];
321 else if(smoothGroups && face->smoothGroups)
324 for(g = 0; g<32; g++)
325 if(smoothGroups & (1<<g) && face->smoothGroups & (1<<g))
327 config = &configList->config[v];
328 config->smoothGroups |= face->smoothGroups;
334 if(!config || !face->smoothGroups)
336 // Duplicate the vertex and make the face use it
337 if(configList->numConfig)
338 index = nNewVertices++;
339 face->indices[i] = (uint16)index;
342 configList->config = renew configList->config VertexConfig[configList->numConfig + 1];
343 config = &configList->config[configList->numConfig++];
344 config->index = index;
345 config->smoothGroups = face->smoothGroups;
350 face->indices[i] = (uint16)config->index;
355 // Allocate some extra vertices
357 Vector3Df * oldVertices = mesh.vertices;
358 Pointf * oldTexCoords = mesh.texCoords;
360 *((void **)&mesh.vertices) = null;
361 *((void **)&mesh.texCoords) = null;
362 *((int *)&mesh.nVertices) = 0;
364 mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
366 // Fill in the new vertices
367 for(index = 0; index<nVertices; index++)
370 VertexConfigList * configList = &configLists[index];
371 for(v = 0; v<configList->numConfig; v++)
373 VertexConfig * config = &configList->config[v];
375 if(config->smoothGroups)
380 mesh.vertices[config->index] = oldVertices[index]; //mesh.vertices[index];
382 mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
387 mesh.vertices[config->index] = oldVertices[index];
389 mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
391 normal = &mesh.normals[config->index];
400 numShared = new0 int[nNewVertices];
402 for(c = 0; c<info->nFaces; c++)
404 Face * face = &faces[c];
407 Vector3Df planeNormal;
408 plane.FromPointsf(mesh.vertices[face->oldIndices[2]],
409 mesh.vertices[face->oldIndices[1]],
410 mesh.vertices[face->oldIndices[0]]);
411 planeNormal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
412 if(face->smoothGroups)
416 int index = face->indices[i];
417 Vector3Df * normal = &mesh.normals[index];
418 normal->Add(normal, planeNormal);
426 int index = face->oldIndices[i];
427 int newIndex = face->indices[i];
428 mesh.normals[newIndex] = planeNormal;
430 if(index != newIndex)
432 mesh.vertices[newIndex] = mesh.vertices[index];
434 mesh.texCoords[newIndex] = mesh.texCoords[index];
436 numShared[newIndex]++;
440 for(index = 0; index<nNewVertices; index++)
442 Vector3Df * normal = &mesh.normals[index];
443 normal->Scale(normal, 1.0f / numShared[index]);
444 normal->Normalize(normal);
447 mesh.Unlock({ normals = true });
449 // Free all the temporary stuff
452 for(index = 0; index < nVertices; index++)
454 VertexConfigList * configList = &configLists[index];
455 if(configList->config) delete configList->config;
463 static bool ReadSmoothing(FileInfo * info, Object object)
465 Mesh mesh = object.mesh;
466 switch(info->chunkId)
471 for(c = 0; c<info->nFaces; c++)
472 info->faces[c].smoothGroups = ReadDWORD(info->f);
479 static bool ReadFacesListChunks(FileInfo * info, Object object)
481 DisplaySystem displaySystem = info->displaySystem;
482 Mesh mesh = object.mesh;
483 switch(info->chunkId)
490 ReadASCIIZ(info->f, &name);
491 mat = displaySystem.GetMaterial(name);
494 if(mat.flags.translucent)
497 uint16 count = ReadWORD(info->f);
498 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + count];
499 for(c = 0; c<count; c++)
501 uint16 face = ReadWORD(info->f);
502 PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
504 if(mesh.AllocatePrimitive(triangle, triangles, 3))
506 triangle->indices[0] = info->faces[face].indices[0];
507 triangle->indices[1] = info->faces[face].indices[1];
508 triangle->indices[2] = info->faces[face].indices[2];
509 triangle->middle.Add(mesh.vertices[triangle->indices[0]], mesh.vertices[triangle->indices[1]]);
510 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indices[2]]);
511 triangle->plane.FromPointsf(
512 mesh.vertices[triangle->indices[2]],
513 mesh.vertices[triangle->indices[1]],
514 mesh.vertices[triangle->indices[0]]);
516 mesh.UnlockPrimitive(triangle);
518 triangle->middle.x /= 3;
519 triangle->middle.y /= 3;
520 triangle->middle.z /= 3;
522 triangle->material = mat;
524 info->faces[face].done = (byte)bool::true;
526 object.flags.translucent = true;
530 PrimitiveGroup group;
531 uint16 count = ReadWORD(info->f);
532 group = mesh.AddPrimitiveGroup(triangles, count * 3);
536 group.material = mat;
537 for(c = 0; c<count; c++)
539 uint16 face = ReadWORD(info->f);
540 if(object.flags.flipWindings)
542 group.indices[c*3] = info->faces[face].indices[2];
543 group.indices[c*3+1] = info->faces[face].indices[1];
544 group.indices[c*3+2] = info->faces[face].indices[0];
548 group.indices[c*3] = info->faces[face].indices[0];
549 group.indices[c*3+1] = info->faces[face].indices[1];
550 group.indices[c*3+2] = info->faces[face].indices[2];
552 info->faces[face].done = (byte)bool::true;
554 mesh.UnlockPrimitiveGroup(group);
565 static bool ReadTriMesh(FileInfo * info, Object object)
567 Mesh mesh = object.mesh;
568 switch(info->chunkId)
573 uint16 nVertices = ReadWORD(info->f);
574 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
575 *((int *) &mesh.nVertices) = nVertices;
576 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
579 for(c = 0; c<mesh.nVertices; c++)
582 Read3DVertex(info->f, vertex);
583 mesh.vertices[c].x = vertex.x;
584 mesh.vertices[c].y =-vertex.z;
585 mesh.vertices[c].z = vertex.y;
592 case TRI_MAPPINGCOORS:
595 uint16 count = ReadWORD(info->f);
596 count = (uint16)Min(mesh.nVertices, count);
598 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
599 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
600 mesh.flags.texCoords1 = true;
603 for(c = 0; c<count; c++)
605 mesh.texCoords[c].x = ReadFloat(info->f);
606 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
620 info->nFaces = nFaces = ReadWORD(info->f);
621 info->pos += sizeof(uint16);
623 info->faces = new0 Face[nFaces];
624 for(c = 0; c<nFaces; c++)
629 info->faces[c].oldIndices[i] =
630 info->faces[c].indices[i] = ReadWORD(info->f);
633 info->pos += 4*sizeof(uint16);
636 ReadChunks(ReadSmoothing, info, object);
639 ComputeNormals(mesh, info, object);
641 ReadChunks(ReadFacesListChunks, info, object);
643 // Add faces without a material all together
645 for(c = 0; c<nFaces; c++)
646 if(!info->faces[c].done)
650 PrimitiveGroup group = mesh.AddPrimitiveGroup(triangles, count * 3);
653 for(c = 0; c<nFaces; c++)
654 if(!info->faces[c].done)
656 group.indices[c*3] = info->faces[c].indices[0];
657 group.indices[c*3+1] = info->faces[c].indices[1];
658 group.indices[c*3+2] = info->faces[c].indices[2];
660 mesh.UnlockPrimitiveGroup(group);
667 mesh.ComputeNormals();
669 if(object.flags.flipWindings)
671 if(mesh.Lock({ normals = true }))
673 for(c = 0; c<mesh.nVertices; c++)
675 mesh.normals[c].x *= -1;
676 mesh.normals[c].y *= -1;
677 mesh.normals[c].z *= -1;
679 mesh.Unlock({ normals = true });
683 // could use this instead? : mesh.ApplyTranslucency(object);
689 Vector3Df xAxis, yAxis, zAxis, center;
692 Matrix inverse/*, source = { 0 }*/;
695 Read3DVertex(info->f, xAxis);
696 Read3DVertex(info->f, yAxis);
697 Read3DVertex(info->f, zAxis);
698 Read3DVertex(info->f, center);
700 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
701 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
702 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
704 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
707 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
708 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
709 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
710 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
712 inverse.Inverse(source);
715 object.flags.flipWindings = false;
717 xAxis.Normalize(xAxis);
718 yAxis.Normalize(yAxis);
719 zAxis.Normalize(zAxis);
721 orth.CrossProduct(yAxis, zAxis);
722 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
723 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
724 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
726 object.flags.flipWindings ^= true;
730 orth.CrossProduct(zAxis, xAxis);
731 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
732 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
733 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
735 object.flags.flipWindings ^= true;
739 orth.CrossProduct(xAxis, yAxis);
740 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
741 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
742 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
744 object.flags.flipWindings ^= true;
751 rotation.m[0][0] = xAxis.x;
752 rotation.m[0][1] =-xAxis.z;
753 rotation.m[0][2] = xAxis.y;
754 rotation.m[0][3] = 0;
755 rotation.m[1][0] =-zAxis.x;
756 rotation.m[1][1] = zAxis.z;
757 rotation.m[1][2] =-zAxis.y;
758 rotation.m[1][3] = 0;
759 rotation.m[2][0] = yAxis.x;
760 rotation.m[2][1] =-yAxis.z;
761 rotation.m[2][2] = yAxis.y;
762 rotation.m[2][3] = 0;
763 rotation.m[3][0] = 0;
764 rotation.m[3][1] = 0;
765 rotation.m[3][2] = 0;
766 rotation.m[3][3] = 1;
770 inverse.Transpose(rotation);
772 temp.Translate(-center.x, center.z, -center.y);
773 temp2.Multiply(temp, inverse);
774 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
778 object.transform.scaling = scaling;
779 // TODO: Improve language to support deep properties on non constant functions
780 // object.transform.orientation.RotationMatrix(rotation);
782 Quaternion orientation;
783 orientation.RotationMatrix(rotation);
784 object.transform.orientation = orientation;
786 object.transform.position = { center.x, -center.z, center.y };
789 // Localize All Vertices
790 for(c = 0; c<mesh.nVertices; c++)
792 Vector3Df vertex = mesh.vertices[c];
794 mesh.vertices[c].MultMatrix(vertex, inverse);
796 mesh.vertices[c].x -= object.pivot.x;
797 mesh.vertices[c].y -= object.pivot.y;
798 mesh.vertices[c].z -= object.pivot.z;
807 static bool ReadMap(FileInfo * info, Material mat)
809 DisplaySystem displaySystem = info->displaySystem;
810 switch(info->chunkId)
815 char location[MAX_LOCATION];
817 ReadASCIIZ(info->f, &name);
820 strcpy(location, info->textureDirectory);
821 PathCat(location, name);
823 if(info->parent->chunkId == MAT_BUMPMAP)
825 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
826 char bumpName[MAX_FILENAME];
827 strcpy(bumpName, "BUMP:");
828 strcat(bumpName, name);
831 mat.bumpMap = displaySystem.GetTexture(bumpName);
834 mat.bumpMap = Bitmap { };
835 if(!mat.bumpMap.Load(location, null, null) ||
836 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
837 !displaySystem.AddTexture(bumpName, mat.bumpMap))
841 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
842 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
845 for(y = 0; y < bh; y++)
846 for(x = 0; x < bw; x++)
848 uint bc = y * bw + x;
849 Color color = picture[bc].color;
850 picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
858 Bitmap opacityMap = null;
859 bool alphaOnly = true;
860 bool translucent = false;
863 mat.baseMap = displaySystem.GetTexture(name);
866 mat.baseMap = Bitmap { };
867 if(!mat.baseMap.Load(location, null, null) ||
868 !mat.baseMap.Convert(null, pixelFormat888, null) ||
869 !displaySystem.AddTexture(name, mat.baseMap))
873 opacityMap = mat.baseMap;
876 else if(info->parent->chunkId == MAT_MAPOPACITY)
878 opacityMap = Bitmap { };
879 if(opacityMap.Load(location, null, null))
881 if(opacityMap.pixelFormat == pixelFormatRGBA)
883 if(!opacityMap.Convert(null, pixelFormat888, null))
890 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
892 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
893 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
895 int ow = opacityMap.width, oh = opacityMap.height;
896 int bw = mat.baseMap.width, bh = mat.baseMap.height;
898 for(y = 0; y < bh; y++)
899 for(x = 0; x < bw; x++)
901 int bc = ((y % bh) * bw + (x % bw));
902 int oc = ((y % oh) * bw + (x % ow));
903 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
904 if(alpha && alpha < 255)
906 picture[bc] = ColorAlpha { alpha, picture[bc].color };
910 mat.flags.translucent = true;
911 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
912 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
914 if(opacityMap != mat.baseMap)
922 uint16 options = ReadWORD(info->f);
923 if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
927 mat.uScale = ReadFloat(info->f);
930 mat.vScale = ReadFloat(info->f);
936 PrintLn("Unhandled Map block");
943 static bool ReadMaterial(FileInfo * info, Material mat)
945 switch(info->chunkId)
949 ReadASCIIZ(info->f, &mat.name);
952 case MAT_TRANSPARENCY:
955 ReadChunks(ReadAmountOf, info, &transparency);
956 mat.opacity = 1.0f - transparency / 100.0f;
957 if(mat.opacity < 1.0)
958 mat.flags.translucent = true;
963 ReadChunks(ReadRGB, info, &mat.diffuse);
964 ReadChunks(ReadRGB, info, &mat.diffuse);
969 ReadChunks(ReadRGB, info, &mat.ambient);
970 ReadChunks(ReadRGB, info, &mat.ambient);
975 ReadChunks(ReadRGB, info, &mat.specular);
976 ReadChunks(ReadRGB, info, &mat.specular);
982 ReadChunks(ReadAmountOf, info, &emissive);
983 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
984 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
985 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
988 case MAT_SHINSTRENGTH:
991 ReadChunks(ReadAmountOf, info, &shininess);
992 mat.specular.r *= shininess / 100.0f;
993 mat.specular.g *= shininess / 100.0f;
994 mat.specular.b *= shininess / 100.0f;
1000 ReadChunks(ReadAmountOf, info, &power);
1004 case MAT_MAPTEXTURE1:
1005 ReadChunks(ReadMap, info, mat);
1007 case MAT_MAPOPACITY:
1008 ReadChunks(ReadMap, info, mat);
1010 case MAT_DOUBLESIDED:
1011 mat.flags.doubleSided = true;
1014 ReadChunks(ReadMap, info, mat);
1018 PrintLn("Unhandled MAT type ID", info->chunkId);
1025 static bool ReadLight(FileInfo * info, Object object)
1027 Mesh mesh = object.mesh;
1028 Light * light = &object.light;
1029 switch(info->chunkId)
1032 case RGB_BYTE_GAMMA:
1034 case RGB_FLOAT_GAMMA:
1035 ReadRGB(info, &light->diffuse);
1036 light->specular = light->diffuse;
1041 char targetName[MAXNAMELEN];
1043 strcpy(targetName, object.name);
1044 strcat(targetName, ".target");
1046 light->flags.omni = false;
1047 light->flags.spot = true;
1049 target = Object { };
1050 target.name = CopyString(targetName);
1051 info->rootObject.children.AddName(target);
1052 target.parent = info->rootObject;
1054 light->target = target;
1056 target.transform.position.x = ReadFloat(info->f);
1057 target.transform.position.z = ReadFloat(info->f);
1058 target.transform.position.y =-ReadFloat(info->f);
1060 light->hotSpot = ReadFloat(info->f);
1061 light->fallOff = ReadFloat(info->f);
1066 light->flags.off = true;
1069 case LIT_ATTENUATION:
1073 d = 300, small = 0.001,
1075 d * (Kl + Kq * d) = (1 / small) - Kc
1077 { Kc, Kl, Kq, small, d });
1080 light->flags.attenuation = true;
1083 #define MINLIGHT 0.08
1084 light->Kq = 1/(light->end*light->end*MINLIGHT);
1087 #define MINLIGHT 0.15f
1088 // #define MINLIGHT 0.1
1089 light->Kl = (float)(1/(light->end*MINLIGHT));
1095 light->start = ReadFloat(info->f);
1100 light->end = ReadFloat(info->f);
1103 case LIT_MULTIPLIER:
1105 light->multiplier = ReadFloat(info->f);
1113 static bool ReadCamera(FileInfo * info, Object object)
1115 Mesh mesh = object.mesh;
1116 switch(info->chunkId)
1124 Camera camera = object.camera;
1125 float nearRange = ReadFloat(info->f);
1126 float farRange = ReadFloat(info->f);
1128 camera.zMin = Max(0.1, nearRange);
1129 camera.zMax = farRange;
1138 static bool ReadEditObject(FileInfo * info, char * name)
1140 DisplaySystem displaySystem = info->displaySystem;
1141 switch(info->chunkId)
1145 Object object = info->rootObject.Find(name);
1148 object = Object { };
1149 object.name = CopyString(name);
1150 info->rootObject.children.AddName(object);
1151 object.parent = info->rootObject;
1153 object.InitializeMesh(displaySystem);
1154 ReadChunks(ReadTriMesh, info, object);
1155 object.flags.mesh = true;
1156 object.mesh.Unlock(0);
1161 Object object = info->rootObject.Find(name);
1167 object = Object { };
1168 object.name = CopyString(name);
1169 info->rootObject.children.AddName(object);
1170 object.parent = info->rootObject;
1172 object.flags.light = true;
1174 light = &object.light;
1175 light->lightObject = object;
1176 light->flags.omni = true;
1177 light->multiplier = 1.0f;
1179 // This is not used?
1180 Read3DVertex(info->f, position);
1181 light->direction = { position.x, position.y, position.z };
1182 info->pos += sizeof(float) * 3;
1184 ReadChunks(ReadLight, info, object);
1189 char targetName[MAXNAMELEN];
1190 Object object = info->rootObject.Find(name);
1193 float bankAngle, focus;
1196 strcpy(targetName, name);
1197 strcat(targetName, ".target");
1198 target = info->rootObject.Find(targetName);
1202 object = Object { };
1203 object.name = CopyString(name);
1204 info->rootObject.children.AddName(object);
1206 object.parent = info->rootObject;
1207 object.camera = Camera { };
1208 object.camera.type = lookAtObject;
1213 target = Object { };
1214 target.name = CopyString(targetName);
1215 info->rootObject.children.AddName(target);
1216 target.parent = info->rootObject;
1219 object.flags.camera = true;
1220 object.cameraTarget = target;
1222 camera = object.camera;
1223 camera.cameraObject = object;
1224 camera.target = target;
1226 //Read3DVertex(info->f, camera.position);
1227 object.transform.position.x = ReadFloat(info->f);
1228 object.transform.position.z = ReadFloat(info->f);
1229 object.transform.position.y =-ReadFloat(info->f);
1231 info->pos += sizeof(float) * 3;
1232 //Read3DVertex(info->f, object.cameraTarget.position);
1233 target.transform.position.x = ReadFloat(info->f);
1234 target.transform.position.z = ReadFloat(info->f);
1235 target.transform.position.y =-ReadFloat(info->f);
1237 info->pos += sizeof(float) * 3;
1238 bankAngle = ReadFloat(info->f);
1239 info->pos += sizeof(float);
1240 focus = ReadFloat(info->f);
1241 info->pos += sizeof(float);
1243 mm = (focus - 5.05659508373109) / 1.13613250717301;
1244 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1246 ReadChunks(ReadCamera, info, object);
1249 case OBJ_HIDDEN: break;
1254 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1256 static bool ReadEditChunks(FileInfo * info, void * data)
1258 switch(info->chunkId)
1262 // Read the ambient color
1263 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1268 Material material { };
1270 ReadChunks(ReadMaterial, info, material);
1272 mat = info->displaySystem.AddNamedMaterial(material.name);
1275 if(material.baseMap)
1276 material.baseMap.MakeMipMaps(info->displaySystem);
1277 if(material.bumpMap)
1278 material.bumpMap.MakeMipMaps(info->displaySystem);
1279 // COPY_NITEM(mat, material);
1280 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1284 delete material.baseMap;
1286 delete material.name;
1293 info->pos += ReadASCIIZ(info->f, &name);
1294 ReadChunks(ReadEditObject, info, name);
1302 struct ObjectInfoBlock
1312 // Key Framer Chunks
1314 #define ACCFLAG_TENSION 0x00000001
1315 #define ACCFLAG_CONTINUITY 0x00000002
1316 #define ACCFLAG_BIAS 0x00000004
1317 #define ACCFLAG_EASETO 0x00000008
1318 #define ACCFLAG_EASEFROM 0x00000010
1320 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1322 switch(info->chunkId)
1326 uint16 flags1, flags2;
1327 ReadASCIIZ(info->f, &block->name);
1328 flags1 = ReadWORD(info->f);
1329 flags2 = ReadWORD(info->f);
1330 block->parent = ReadWORD(info->f);
1334 ReadASCIIZ(info->f, &block->dummyName);
1337 Read3DVertex(info->f, block->pivot);
1340 block->hierarchy = ReadWORD(info->f);
1344 case FRM_TRACKSCALE:
1347 case FRM_TRACKCOLOR:
1348 case FRM_TRACKHOTSPOT:
1349 case FRM_TRACKFALLOFF:
1351 FrameTrack track { };
1358 block->tracks.Add(track);
1360 flags = ReadWORD(info->f);
1362 info->f.Read(unknown, sizeof(unknown), 1);
1364 track.numKeys = ReadDWORD(info->f);
1366 switch(info->chunkId)
1368 case FRM_TRACKPOS: track.type.type = position; break;
1369 case FRM_TRACKROT: track.type.type = rotation; break;
1370 case FRM_TRACKSCALE: track.type.type = scaling; break;
1371 case FRM_TRACKROLL: track.type.type = roll; break;
1372 case FRM_TRACKFOV: track.type.type = fov; break;
1373 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1374 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1375 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1377 if((flags & 0x0003) == 3)
1378 track.type.loop = true;
1380 track.keys = new0 FrameKey[track.numKeys];
1381 for(c = 0; c<track.numKeys; c++)
1383 uint16 accelerationFlags;
1384 FrameKey * key = track.keys + c;
1386 key->frame = ReadDWORD(info->f);
1387 accelerationFlags = ReadWORD(info->f);
1389 if(accelerationFlags & ACCFLAG_TENSION)
1390 key->tension = ReadFloat(info->f);
1391 if(accelerationFlags & ACCFLAG_CONTINUITY)
1392 key->continuity = ReadFloat(info->f);
1393 if(accelerationFlags & ACCFLAG_BIAS)
1394 key->bias = ReadFloat(info->f);
1395 if(accelerationFlags & ACCFLAG_EASETO)
1396 key->easeTo = ReadFloat(info->f);
1397 if(accelerationFlags & ACCFLAG_EASEFROM)
1398 key->easeFrom = ReadFloat(info->f);
1400 switch(info->chunkId)
1405 Read3DVertex(info->f, position);
1406 key->position = { position.x, -position.z, position.y };
1412 Angle angle = ReadFloat(info->f);
1413 Vector3Df fixedAxis;
1415 Read3DVertex(info->f, axis);
1416 fixedAxis.x = axis.x;
1417 fixedAxis.y = -axis.z;
1418 fixedAxis.z = axis.y;
1422 Quaternion rotation;
1423 rotation.RotationAxis(fixedAxis, angle);
1424 key->orientation.Multiply((key - 1)->orientation, rotation);
1427 key->orientation.RotationAxis(fixedAxis, angle);
1430 case FRM_TRACKSCALE:
1433 Read3DVertex(info->f, scaling);
1434 key->scaling = { scaling.x, scaling.z, scaling.y };
1439 key->fov = ReadFloat(info->f);
1444 key->roll = -ReadFloat(info->f);
1447 case FRM_TRACKCOLOR:
1449 FileInfo childInfo = *info;
1450 childInfo.chunkId = RGB_FLOAT;
1451 ReadRGB(&childInfo, &key->color);
1454 case FRM_TRACKHOTSPOT:
1456 key->hotSpot = ReadFloat(info->f);
1459 case FRM_TRACKFALLOFF:
1461 key->fallOff = ReadFloat(info->f);
1473 static Object FindObjectID(Object object, int id)
1475 Object result = null;
1477 for(child = object.children.first; child; child = child.next)
1479 if(child.flags.hierarchy == (uint16) id)
1486 result = FindObjectID(child, id);
1493 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1495 switch(info->chunkId)
1499 ObjectInfoBlock block { };
1502 ReadChunks(ReadFrameInfoBlock, info, &block);
1504 if(block.dummyName && block.dummyName[0])
1506 if(!strcmp(block.name, "$$$DUMMY"))
1508 object = Object { };
1509 object.name = block.dummyName;
1510 info->rootObject.children.AddName(object);
1511 object.transform.scaling = { 1,1,1 };
1515 Object model = info->rootObject.Find(block.name);
1518 object = Object { };
1519 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1520 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1521 object.flags = model.flags;
1522 object.flags.ownMesh = false;
1523 object.mesh = model.mesh;
1525 object.min = model.min;
1526 object.max = model.max;
1527 object.radius = model.radius;
1529 object.transform = model.transform;
1530 info->rootObject.children.AddName(object);
1532 delete block.dummyName;
1534 object.parent = info->rootObject;
1537 object = info->rootObject.Find(block.name);
1541 Mesh mesh = object.mesh;
1542 object.flags.hierarchy = block.hierarchy + 1;
1543 if(block.parent != -1)
1545 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1548 object.parent.children.Remove(object);
1549 parent.children.AddName(object);
1550 object.parent = parent;
1553 object.pivot.x = block.pivot.x;
1554 object.pivot.y =-block.pivot.z;
1555 object.pivot.z = block.pivot.y;
1557 if(mesh && object.flags.ownMesh)
1559 if(mesh.Lock({ vertices = true }))
1562 // Take pivot into account
1563 for(c = 0; c<mesh.nVertices; c++)
1565 mesh.vertices[c].x -= object.pivot.x;
1566 mesh.vertices[c].y -= object.pivot.y;
1567 mesh.vertices[c].z -= object.pivot.z;
1569 mesh.Unlock({ vertices = true });
1573 object.tracks = block.tracks;
1574 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1575 object.endFrame = info->rootObject.endFrame;
1578 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1584 ObjectInfoBlock block { };
1587 ReadChunks(ReadFrameInfoBlock, info, &block);
1589 if(block.dummyName && block.dummyName[0])
1591 if(!strcmp(block.name, "$$$DUMMY"))
1593 object = Object { };
1594 object.name = block.dummyName;
1595 info->rootObject.children.AddName(object);
1596 object.transform.scaling = { 1, 1, 1 };
1597 object.flags.camera = true;
1601 Object model = info->rootObject.Find(block.name);
1604 object = Object { };
1605 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1606 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1607 object.flags = model.flags;
1608 object.flags.ownMesh = false;
1609 object.camera = model.camera;
1610 object.flags.camera = true;
1611 info->rootObject.children.AddName(object);
1613 delete block.dummyName;
1615 object.parent = info->rootObject;
1618 object = info->rootObject.Find(block.name);
1622 object.flags.hierarchy = block.hierarchy + 1;
1623 if(block.parent != -1)
1625 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1628 object.parent.children.Remove(object);
1629 parent.children.AddName(object);
1630 object.parent = parent;
1633 object.pivot.x = block.pivot.x;
1634 object.pivot.y =-block.pivot.z;
1635 object.pivot.z = block.pivot.y;
1637 object.tracks = block.tracks;
1638 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1639 object.endFrame = info->rootObject.endFrame;
1642 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1646 case FRM_CAMERATARGET:
1648 ObjectInfoBlock block { };
1650 char targetName[MAXNAMELEN];
1652 ReadChunks(ReadFrameInfoBlock, info, &block);
1654 strcpy(targetName, block.name);
1655 strcat(targetName, ".target");
1657 if(block.dummyName && block.dummyName[0])
1659 if(!strcmp(block.name, "$$$DUMMY"))
1661 object = Object { };
1662 object.name = block.dummyName;
1663 info->rootObject.children.AddName(object);
1664 object.transform.scaling = { 1,1,1 };
1668 Object model = info->rootObject.Find(targetName);
1671 object = Object { };
1672 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1673 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1674 object.flags = model.flags;
1675 object.flags.ownMesh = false;
1676 object.camera = model.camera;
1677 info->rootObject.children.AddName(object);
1679 delete block.dummyName;
1681 object.parent = info->rootObject;
1684 object = info->rootObject.Find(targetName);
1688 object.flags.hierarchy = block.hierarchy + 1;
1689 if(block.parent != -1)
1691 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1694 object.parent.children.Remove(object);
1695 parent.children.AddName(object);
1696 object.parent = parent;
1699 object.pivot.x = block.pivot.x;
1700 object.pivot.y =-block.pivot.z;
1701 object.pivot.z = block.pivot.y;
1703 object.tracks = block.tracks;
1704 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1705 object.endFrame = info->rootObject.endFrame;
1708 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1714 ObjectInfoBlock block { };
1716 ReadChunks(ReadFrameInfoBlock, info, &block);
1718 info->rootObject.tracks = block.tracks;
1724 ObjectInfoBlock block { };
1727 ReadChunks(ReadFrameInfoBlock, info, &block);
1729 if(block.dummyName && block.dummyName[0])
1731 if(!strcmp(block.name, "$$$DUMMY"))
1733 object = Object { };
1734 object.name = block.dummyName;
1735 info->rootObject.children.AddName(object);
1736 object.transform.scaling = { 1, 1, 1 };
1737 object.flags.light = true;
1741 Object model = info->rootObject.Find(block.name);
1744 object = Object { };
1745 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1746 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1747 object.flags = model.flags;
1748 object.flags.ownMesh = false;
1749 object.light = model.light;
1750 object.flags.light = true;
1751 info->rootObject.children.AddName(object);
1753 delete block.dummyName;
1755 object.parent = info->rootObject;
1758 object = info->rootObject.Find(block.name);
1762 object.flags.hierarchy = block.hierarchy + 1;
1763 if(block.parent != -1)
1765 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1768 object.parent.children.Remove(object);
1769 parent.children.AddName(object);
1770 object.parent = parent;
1773 object.pivot.x = block.pivot.x;
1774 object.pivot.y =-block.pivot.z;
1775 object.pivot.z = block.pivot.y;
1777 object.tracks = block.tracks;
1778 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1779 object.endFrame = info->rootObject.endFrame;
1782 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1786 case FRM_SPOTLIGHTTARGET:
1788 ObjectInfoBlock block { };
1790 char targetName[MAXNAMELEN];
1792 ReadChunks(ReadFrameInfoBlock, info, &block);
1794 strcpy(targetName, block.name);
1795 strcat(targetName, ".target");
1797 if(block.dummyName && block.dummyName[0])
1799 if(!strcmp(block.name, "$$$DUMMY"))
1801 object = Object { };
1802 object.name = block.dummyName;
1803 info->rootObject.children.AddName(object);
1804 object.transform.scaling = { 1,1,1 };
1808 Object model = info->rootObject.Find(targetName);
1811 object = Object { };
1812 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1813 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1814 object.flags = model.flags;
1815 object.flags.ownMesh = false;
1816 object.light = model.light;
1817 info->rootObject.children.AddName(object);
1819 delete block.dummyName;
1821 object.parent = info->rootObject;
1824 object = info->rootObject.Find(targetName);
1828 object.flags.hierarchy = block.hierarchy + 1;
1829 if(block.parent != -1)
1831 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1834 object.parent.children.Remove(object);
1835 parent.children.AddName(object);
1836 object.parent = parent;
1839 object.pivot.x = block.pivot.x;
1840 object.pivot.y =-block.pivot.z;
1841 object.pivot.z = block.pivot.y;
1843 object.tracks = block.tracks;
1844 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1845 object.endFrame = info->rootObject.endFrame;
1848 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1854 info->rootObject.startFrame = ReadDWORD(info->f);
1855 info->rootObject.endFrame = ReadDWORD(info->f);
1856 *&(info->rootObject.frame) = info->rootObject.startFrame;
1864 static bool ReadMainChunks(FileInfo * info, void * data)
1866 switch(info->chunkId)
1869 ReadChunks(ReadEditChunks, info, null);
1873 Object object = data;
1874 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
1876 ReadChunks(ReadKeyFrameChunks, info, null);
1877 object.flags.keysLoaded = true;
1885 static bool ReadMain(FileInfo * info, void * data)
1887 switch(info->chunkId)
1890 ReadChunks(ReadMainChunks, info, data);
1896 class Object3DSFormat : ObjectFormat
1898 class_property(extension) = "3ds";
1900 bool Load(Object object, char * fileName, DisplaySystem displaySystem)
1902 bool result = false;
1905 FileInfo info = {0};
1906 info.rootObject = object;
1907 info.displaySystem = displaySystem;
1910 StripLastDirectory(fileName, info.textureDirectory);
1911 info.f = FileOpen(fileName, read);
1914 if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
1916 object.flags.root = true;
1917 object.SetMinMaxRadius(true);
1918 object._Animate(object.frame);
1919 object.UpdateTransform();
1926 object.Free(displaySystem);