1// tWorld.cpp 
2// 
3// This class represents entire worlds of tObjects. Instances, materials, models, paths, lights, skeletons, and cameras 
4// may all be present in a tWorld. It is able to load multiple tChunk-based binary tac files. It can also save them. 
5// 
6// Every saved tac file contains a version chunk with both major and minor version numbers. Increment the minor number 
7// if there is a change to a tObject that does not break binary compatibility. For example, you can add a new chunk ID 
8// as long as loading it is optional. The major version number must be incremented if file compatibility is broken. 
9// This can happen if chunks are rearranged or chunk contents are altered. This allows an exporters to change, perhaps 
10// a new chunk was added, without the tac file parsing tool needing an upgrade immediately (forward compatibility). The 
11// minor version should be reset to 0 when the major number is incremented. Any tools, by looking at the major number, 
12// can maintain backwards compatibility. 
13// 
14// Copyright (c) 2006, 2017 Tristan Grimmer. 
15// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
16// granted, provided that the above copyright notice and this permission notice appear in all copies. 
17// 
18// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
20// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
21// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
22// PERFORMANCE OF THIS SOFTWARE. 
23 
24#include "Scene/tWorld.h" 
25using namespace tMath; 
26using namespace tStd; 
27namespace tScene 
28{ 
29 
30 
31void tWorld::Clear() 
32{ 
33 Name.Clear(); 
34 LastLoadedFilename.Clear(); 
35 
36 Cameras.Empty(); 
37 Lights.Empty(); 
38 Paths.Empty(); 
39 Materials.Empty(); 
40 Skeletons.Empty(); 
41 PolyModels.Empty(); 
42 LodGroups.Empty(); 
43 Instances.Empty(); 
44 Selections.Empty(); 
45 
46 // Empty scenes can be assumed to be at the current version. 
47 MajorVersion = SceneMajorVersion; 
48 MinorVersion = SceneMinorVersion; 
49 
50 NextCameraID = 0; 
51 NextLightID = 0; 
52 NextPathID = 0; 
53 NextMaterialID = 0; 
54 NextPolyModelID = 0; 
55 NextSkeletonID = 0; 
56 NextLodGroupID = 0; 
57 NextInstanceID = 0; 
58 NextSelectionID = 0; 
59} 
60 
61 
62void tWorld::Scale(float scale) 
63{ 
64 for (tItList<tCamera>::Iter camera = Cameras.First(); camera; ++camera) 
65 camera->Scale(scale); 
66 
67 for (tItList<tLight>::Iter light = Lights.First(); light; ++light) 
68 light->Scale(scale); 
69 
70 for (tItList<tPath>::Iter path = Paths.First(); path; ++path) 
71 path->Scale(scale); 
72 
73 for (tItList<tPolyModel>::Iter model = PolyModels.First(); model; ++model) 
74 model->Scale(scale); 
75 
76 for (tItList<tSkeleton>::Iter skeleton = Skeletons.First(); skeleton; ++skeleton) 
77 skeleton->Scale(scale); 
78 
79 for (tItList<tInstance>::Iter instance = Instances.First(); instance; ++instance) 
80 instance->Scale(scale); 
81} 
82 
83 
84void tWorld::MergeScene(tWorld& sceneDestroyed) 
85{ 
86 MergeItems 
87 ( 
88 &sceneDestroyed.Cameras, &sceneDestroyed.Lights, &sceneDestroyed.Paths, 
89 &sceneDestroyed.Materials, &sceneDestroyed.Skeletons, &sceneDestroyed.PolyModels, 
90 &sceneDestroyed.LodGroups, &sceneDestroyed.Instances, &sceneDestroyed.Selections 
91 ); 
92} 
93 
94 
95void tWorld::AddOffsetToAllIDs(uint32 offset) 
96{ 
97 for (tItList<tCamera>::Iter it = Cameras.First(); it; ++it) 
98 it->ID += offset; 
99 
100 for (tItList<tLight>::Iter it = Lights.First(); it; ++it) 
101 it->ID += offset; 
102 
103 for (tItList<tPath>::Iter it = Paths.First(); it; ++it) 
104 it->ID += offset; 
105 
106 for (tItList<tMaterial>::Iter it = Materials.First(); it; ++it) 
107 it->ID += offset; 
108 
109 for (tItList<tSkeleton>::Iter it = Skeletons.First(); it; ++it) 
110 it->ID += offset; 
111 
112 for (tItList<tPolyModel>::Iter it = PolyModels.First(); it; ++it) 
113 { 
114 tPolyModel* p = it; 
115 p->ID += offset; 
116 if (p->Mesh.FaceTableMaterialIDs) 
117 { 
118 for (int f = 0; f < p->Mesh.NumFaces; ++f) 
119 p->Mesh.FaceTableMaterialIDs[f] += offset; 
120 } 
121 } 
122 
123 for (tItList<tLodGroup>::Iter it = LodGroups.First(); it; ++it) 
124 it->ID += offset; 
125 
126 for (tItList<tInstance>::Iter it = Instances.First(); it; ++it) 
127 { 
128 it->ID += offset; 
129 it->ObjectID += offset; 
130 } 
131 
132 for (tItList<tSelection>::Iter ss = Selections.First(); ss; ++ss) 
133 { 
134 ss->ID += offset; 
135 for (tItList<uint32>::Iter instID = ss->InstanceIDs.First(); instID; ++instID) 
136 { 
137 uint32* id = instID.GetObject(); 
138 *id += offset; 
139 } 
140 } 
141} 
142 
143 
144bool tWorld::MergeItems 
145( 
146 tItList<tCamera>* cameras, tItList<tLight>* lights, tItList<tPath>* paths, 
147 tItList<tMaterial>* materials, tItList<tSkeleton>* skeletons, tItList<tPolyModel>* polyModels, 
148 tItList<tLodGroup>* lodGroups, tItList<tInstance>* instances, tItList<tSelection>* selections 
149) 
150{ 
151 //@todo The tree hierarchy sanity-checking here and return false if we're screwed. 
152 tItList<tCamera> emptyCameras; 
153 tItList<tLight> emptyLights; 
154 tItList<tPath> emptyPaths; 
155 tItList<tMaterial> emptyMaterials; 
156 tItList<tSkeleton> emptySkeletons; 
157 tItList<tPolyModel> emptyPolyModels; 
158 tItList<tLodGroup> emptyLodGroups; 
159 tItList<tInstance> emptyInstances; 
160 tItList<tSelection> emptySelections; 
161 
162 tItList<tCamera>& newCameras = cameras ? *cameras : emptyCameras; 
163 tItList<tLight>& newLights = lights ? *lights : emptyLights; 
164 tItList<tPath>& newPaths = paths ? *paths : emptyPaths; 
165 tItList<tMaterial>& newMaterials = materials ? *materials : emptyMaterials; 
166 tItList<tSkeleton>& newSkeletons = skeletons ? *skeletons : emptySkeletons; 
167 tItList<tPolyModel>& newPolyModels = polyModels ? *polyModels : emptyPolyModels; 
168 tItList<tLodGroup>& newLodGroups = lodGroups ? *lodGroups : emptyLodGroups; 
169 tItList<tInstance>& newInstances = instances ? *instances : emptyInstances; 
170 tItList<tSelection>& newSelections = selections ? *selections : emptySelections; 
171 
172 // Make sure every type of object has a unique ID. Object lists that refer to the corrected lists are also fixed 
173 // up. The first arg is a list of items that need their IDs adjusted to make them unique -- the remaining arg(s) 
174 // are items that refer to those adjusted IDs and need to be updated. 
175 if (cameras) 
176 CorrectCameraIDs(newCameras, newInstances); 
177 
178 if (lights) 
179 CorrectLightIDs(newLights, newInstances); 
180 
181 if (paths) 
182 CorrectPathIDs(newPaths, newInstances); 
183 
184 if (materials) 
185 CorrectMaterialIDs(newMaterials, newPolyModels); 
186 
187 if (skeletons) 
188 CorrectSkeletonIDs(newSkeletons, newPolyModels); 
189 
190 if (polyModels) 
191 CorrectPolyModelIDs(newPolyModels, newLodGroups, newInstances); 
192 
193 // For now nothing refers to LOD groups. just keep IDs unique. Eventually instances may refer to these. 
194 if (lodGroups) 
195 CorrectLodGroupIDs(newLodGroups); 
196 
197 if (instances) 
198 CorrectInstanceIDs(newInstances, newSelections); 
199 
200 // Currently nothing refers to Selections. New selections that have the same name as existing selection get merged 
201 // into the existing. For selections it is based on the selection name. 
202 if (selections) 
203 { 
204 MergeToExistingSelections(newSelections); 
205 CorrectSelectionIDs(newSelections); 
206 } 
207 
208 // We can now add the groups, cameras, lights, materials, skeletons, models, and instances to the scene. 
209 while (tCamera* c = newCameras.Remove()) 
210 Cameras.Append(c); 
211 
212 while (tLight* l = newLights.Remove()) 
213 Lights.Append(l); 
214 
215 while (tPath* s = newPaths.Remove()) 
216 Paths.Append(s); 
217 
218 while (tMaterial* m = newMaterials.Remove()) 
219 Materials.Append(m); 
220 
221 while (tSkeleton* s = newSkeletons.Remove()) 
222 Skeletons.Append(s); 
223 
224 while (tPolyModel* m = newPolyModels.Remove()) 
225 PolyModels.Append(m); 
226 
227 while (tLodGroup* l = newLodGroups.Remove()) 
228 LodGroups.Append(l); 
229 
230 while (tInstance* i = newInstances.Remove()) 
231 Instances.Append(i); 
232 
233 while (tSelection* s = newSelections.Remove()) 
234 Selections.Append(s); 
235 
236 return true; 
237} 
238 
239 
240bool tWorld::CombinePolyModelInstances(tItList<tInstance>& polymodelInstances, tString newInstName) 
241{ 
242 tPolyModel* newModel = new tPolyModel(); 
243 newModel->ID = NextPolyModelID++; 
244 newModel->Name = tString("ModelFor_") + newInstName; 
245 newModel->IsLodGroupMember = false; 
246 
247 tInstance* newInstance = new tInstance(); 
248 newInstance->ID = NextInstanceID++; 
249 newInstance->Name = newInstName; 
250 newInstance->ObjectType = tInstance::tType::PolyModel; 
251 newInstance->ObjectID = newModel->ID; 
252 newInstance->Transform = tMatrix4::identity;  
253 
254 tMesh* newMesh = &newModel->Mesh; 
255 
256 // Vert tables. 
257 int numVertsTotal = 0; 
258 int numFacesTotal = 0; 
259 int numEdgeTotal =0; 
260 int numWeightSetsTotal = 0; 
261 int numNormalsTotal =0; 
262 int numUVTotal =0; 
263 int numNMUVTotal = 0; 
264 int numColourTotal = 0; 
265 int numTangentTotal =0; 
266  
267 tItList<tPolyModel> combinedModels; 
268 for (tItList<tInstance>::Iter it = polymodelInstances.First(); it; ++it) 
269 { 
270 tInstance* inst = it; 
271 tAssert(inst->ObjectType == tInstance::tType::PolyModel); 
272 tPolyModel* model = FindPolyModel(inst->ObjectID); 
273 
274 combinedModels.Append(model); 
275 tMesh* mesh = &model->Mesh; 
276 
277 numVertsTotal += mesh->NumVertPositions; 
278 numFacesTotal += mesh->NumFaces; 
279 numEdgeTotal += mesh->NumEdges; 
280 numWeightSetsTotal += mesh->NumVertWeightSets; 
281 numNormalsTotal += mesh->NumVertNormals; 
282 numUVTotal += mesh->NumVertUVs; 
283 numNMUVTotal += mesh->NumVertNormalMapUVs; 
284 numColourTotal += mesh->NumVertColours; 
285 numTangentTotal += mesh->NumVertTangents; 
286 } 
287 
288 newMesh->NumVertPositions = numVertsTotal; 
289 newMesh->NumFaces = numFacesTotal; 
290 if (numVertsTotal == 0 || numFacesTotal == 0) 
291 return false; 
292 
293 newMesh->NumEdges = numEdgeTotal; 
294 newMesh->NumVertWeightSets = numWeightSetsTotal; 
295 newMesh->NumVertNormals = numNormalsTotal; 
296 newMesh->NumVertUVs = numUVTotal; 
297 newMesh->NumVertNormalMapUVs = numNMUVTotal; 
298 newMesh->NumVertColours = numColourTotal; 
299 newMesh->NumVertTangents = numTangentTotal; 
300 
301 newMesh->FaceTableVertPositionIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
302 newMesh->FaceTableVertWeightSetIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
303 newMesh->FaceTableVertNormalIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
304 newMesh->FaceTableUVIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
305 newMesh->FaceTableNormalMapUVIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
306 newMesh->FaceTableColourIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
307 newMesh->FaceTableFaceNormals = newMesh->NumFaces > 0 ? new tVector3[newMesh->NumFaces] : nullptr; 
308 newMesh->FaceTableMaterialIDs = newMesh->NumFaces > 0 ? new uint32[newMesh->NumFaces] : nullptr; 
309 newMesh->FaceTableTangentIndices = newMesh->NumFaces > 0 ? new tMath::tTriFace[newMesh->NumFaces] : nullptr; 
310 newMesh->EdgeTableVertPositionIndices = newMesh->NumEdges > 0 ? new tMath::tEdge[newMesh->NumEdges] : nullptr; 
311 newMesh->VertTablePositions = newMesh->NumVertPositions > 0 ? new tVector3[newMesh->NumVertPositions] : nullptr; 
312 newMesh->VertTableWeightSets = newMesh->NumVertWeightSets > 0 ? new tWeightSet[newMesh->NumVertWeightSets] : nullptr; 
313 newMesh->VertTableNormals = newMesh->NumVertNormals > 0 ? new tVector3[newMesh->NumVertNormals] : nullptr; 
314 newMesh->VertTableUVs = newMesh->NumVertUVs > 0 ? new tMath::tVector2[newMesh->NumVertUVs] : nullptr; 
315 newMesh->VertTableNormalMapUVs = newMesh->NumVertNormalMapUVs > 0 ? new tMath::tVector2[newMesh->NumVertNormalMapUVs] : nullptr; 
316 newMesh->VertTableColours = newMesh->NumVertColours > 0 ? new tColouri[newMesh->NumVertColours] : nullptr; 
317 newMesh->VertTableTangents = newMesh->NumVertTangents > 0 ? new tMath::tVector4[newMesh->NumVertTangents] : nullptr; 
318 
319 int curFace = 0; 
320 int curEdge = 0; 
321 int curVert = 0; 
322 int curWeightSet = 0; 
323 int curNormal = 0; 
324 int curUV = 0; 
325 int curNMUV = 0; 
326 int curColour = 0; 
327 int curTangent = 0; 
328 
329 for (tItList<tInstance>::Iter it = polymodelInstances.First(); it;) 
330 { 
331 tItList<tInstance>::Iter next = it + 1; 
332 tInstance* inst = it; 
333 tAssert(inst->ObjectType == tInstance::tType::PolyModel); 
334 
335 tPolyModel* model = FindPolyModel(inst->ObjectID); 
336 tMesh* mesh = &model->Mesh; 
337 
338 for (int f = 0; f < mesh->NumFaces; ++f) 
339 { 
340 // Offset face indices. 
341 if (newMesh->FaceTableVertPositionIndices && mesh->FaceTableVertPositionIndices) 
342 { 
343 newMesh->FaceTableVertPositionIndices[f+curFace].Index[0] = mesh->FaceTableVertPositionIndices[f].Index[0] + curVert; 
344 newMesh->FaceTableVertPositionIndices[f+curFace].Index[1] = mesh->FaceTableVertPositionIndices[f].Index[1] + curVert; 
345 newMesh->FaceTableVertPositionIndices[f+curFace].Index[2] = mesh->FaceTableVertPositionIndices[f].Index[2] + curVert; 
346 }  
347 
348 if (newMesh->FaceTableVertWeightSetIndices && mesh->FaceTableVertWeightSetIndices) 
349 { 
350 newMesh->FaceTableVertWeightSetIndices[f+curFace].Index[0] = mesh->FaceTableVertWeightSetIndices[f].Index[0] + curWeightSet; 
351 newMesh->FaceTableVertWeightSetIndices[f+curFace].Index[1] = mesh->FaceTableVertWeightSetIndices[f].Index[1] + curWeightSet; 
352 newMesh->FaceTableVertWeightSetIndices[f+curFace].Index[2] = mesh->FaceTableVertWeightSetIndices[f].Index[2] + curWeightSet; 
353 } 
354 
355 if (newMesh->FaceTableVertNormalIndices && mesh->FaceTableVertNormalIndices) 
356 { 
357 newMesh->FaceTableVertNormalIndices[f+curFace].Index[0] = mesh->FaceTableVertNormalIndices[f].Index[0] + curNormal; 
358 newMesh->FaceTableVertNormalIndices[f+curFace].Index[1] = mesh->FaceTableVertNormalIndices[f].Index[1] + curNormal; 
359 newMesh->FaceTableVertNormalIndices[f+curFace].Index[2] = mesh->FaceTableVertNormalIndices[f].Index[2] + curNormal; 
360 } 
361 
362 if (newMesh->FaceTableUVIndices && mesh->FaceTableUVIndices) 
363 { 
364 newMesh->FaceTableUVIndices[f+curFace].Index[0] = mesh->FaceTableUVIndices[f].Index[0] + curUV; 
365 newMesh->FaceTableUVIndices[f+curFace].Index[1] = mesh->FaceTableUVIndices[f].Index[1] + curUV; 
366 newMesh->FaceTableUVIndices[f+curFace].Index[2] = mesh->FaceTableUVIndices[f].Index[2] + curUV; 
367 }  
368 
369 if (newMesh->FaceTableNormalMapUVIndices) 
370 { 
371 newMesh->FaceTableNormalMapUVIndices[f+curFace].Index[0] = mesh->FaceTableNormalMapUVIndices == 0 ? -1 : mesh->FaceTableNormalMapUVIndices[f].Index[0] + curNMUV; 
372 newMesh->FaceTableNormalMapUVIndices[f+curFace].Index[1] = mesh->FaceTableNormalMapUVIndices == 0 ? -1 : mesh->FaceTableNormalMapUVIndices[f].Index[1] + curNMUV; 
373 newMesh->FaceTableNormalMapUVIndices[f+curFace].Index[2] = mesh->FaceTableNormalMapUVIndices == 0 ? -1 : mesh->FaceTableNormalMapUVIndices[f].Index[2] + curNMUV; 
374 }  
375 
376 if (newMesh->FaceTableColourIndices) 
377 { 
378 newMesh->FaceTableColourIndices[f+curFace].Index[0] = mesh->FaceTableColourIndices == 0 ? -1 : mesh->FaceTableColourIndices[f].Index[0] + curColour; 
379 newMesh->FaceTableColourIndices[f+curFace].Index[1] = mesh->FaceTableColourIndices == 0 ? -1 : mesh->FaceTableColourIndices[f].Index[1] + curColour; 
380 newMesh->FaceTableColourIndices[f+curFace].Index[2] = mesh->FaceTableColourIndices == 0 ? -1 : mesh->FaceTableColourIndices[f].Index[2] + curColour; 
381 } 
382 
383 if (newMesh->FaceTableTangentIndices && mesh->FaceTableTangentIndices) 
384 { 
385 newMesh->FaceTableTangentIndices[f+curFace].Index[0] = mesh->FaceTableTangentIndices[f].Index[0] + curTangent; 
386 newMesh->FaceTableTangentIndices[f+curFace].Index[1] = mesh->FaceTableTangentIndices[f].Index[1] + curTangent; 
387 newMesh->FaceTableTangentIndices[f+curFace].Index[2] = mesh->FaceTableTangentIndices[f].Index[2] + curTangent; 
388 }  
389 } 
390 
391 tMemcpy( &newMesh->FaceTableFaceNormals[curFace], mesh->FaceTableFaceNormals, sizeof(tVector3) * mesh->NumFaces); 
392 tMemcpy( &newMesh->FaceTableMaterialIDs[curFace], mesh->FaceTableMaterialIDs, sizeof(uint32) * mesh->NumFaces); 
393 tMemcpy( &newMesh->EdgeTableVertPositionIndices[curEdge], mesh->EdgeTableVertPositionIndices, mesh->NumEdges * sizeof(tEdge) ); 
394 for (int e = 0; e < mesh->NumEdges; ++e) 
395 { 
396 newMesh->EdgeTableVertPositionIndices[curEdge + e ].Index[0] += curVert; 
397 newMesh->EdgeTableVertPositionIndices[curEdge + e ].Index[1] += curVert; 
398 } 
399 
400 // Convert to world space. 
401 for (int v = 0; v < mesh->NumVertPositions; ++v) 
402 tMath::tMul(newMesh->VertTablePositions[curVert+v] , inst->Transform, mesh->VertTablePositions[v]); 
403 
404 tMemcpy(&newMesh->VertTableWeightSets[curWeightSet],mesh->VertTableWeightSets, mesh->NumVertWeightSets * sizeof(tWeightSet)); 
405 
406 tMatrix4 rotOnly = inst->Transform; 
407 tSet(rotOnly.C4, 0.0f, 0.0f, 0.0f, 1.0f); 
408 for (int n = 0; n < mesh->NumVertNormals; ++n) 
409 { 
410 // Convert to world space (rotation only). 
411 tMath::tMul(newMesh->VertTableNormals[curNormal+n] , rotOnly, mesh->VertTableNormals[n]); 
412 newMesh->VertTableNormals[curNormal+n].Normalize(); 
413 } 
414 
415 tMemcpy(&newMesh->VertTableUVs[curUV], mesh->VertTableUVs, mesh->NumVertUVs * sizeof(tVector2)); 
416 tMemcpy(&newMesh->VertTableNormalMapUVs[curNMUV], mesh->VertTableNormalMapUVs, mesh->NumVertNormalMapUVs * sizeof(tVector2)); 
417 tMemcpy(&newMesh->VertTableColours[curColour], mesh->VertTableColours, mesh->NumVertColours* sizeof(tColouri)); 
418 tMemcpy(&newMesh->VertTableTangents[curTangent], mesh->VertTableTangents, mesh->NumVertTangents * sizeof(tVector4)); 
419  
420 curFace += mesh->NumFaces; 
421 curEdge += mesh->NumEdges; 
422 curVert += mesh->NumVertPositions; 
423 curWeightSet += mesh->NumVertWeightSets; 
424 curNormal += mesh->NumVertNormals; 
425 curUV += mesh->NumVertUVs; 
426 curNMUV += mesh->NumVertNormalMapUVs; 
427 curColour += mesh->NumVertColours; 
428 curTangent += mesh->NumVertTangents; 
429 
430 Instances.Remove( it ); 
431 delete inst; 
432 
433 it = next; 
434 } 
435 
436 // Now remove any models left stranded. 
437 tItList<tPolyModel> strandedModels; 
438 for (tItList<tPolyModel>::Iter it = combinedModels.First(); it;) 
439 { 
440 tItList<tPolyModel>::Iter next = it + 1; 
441 tPolyModel* model = it; 
442 
443 bool stranded = true; 
444 for (tItList<tInstance>::Iter it2 = Instances.First(); stranded && it2; ++it2) 
445 { 
446 tInstance* inst = it2; 
447 if (inst->ObjectType != tInstance::tType::PolyModel) 
448 continue; 
449 
450 if (inst->ObjectID == model->ID) 
451 stranded = false; 
452 } 
453 
454 if (stranded) 
455 strandedModels.Append(model); 
456 
457 it = next; 
458 } 
459 
460 for (tItList< tPolyModel >::Iter it = PolyModels.First(); it;) 
461 { 
462 tItList<tPolyModel>::Iter next = it+1; 
463 tPolyModel* m1 = it; 
464 for (tItList<tPolyModel>::Iter it2 = strandedModels.First(); it2; ++it2) 
465 { 
466 tPolyModel* m2 = it2; 
467 if (m1 == m2) 
468 { 
469 PolyModels.Remove(it); 
470 delete m1; 
471 break; 
472 } 
473 } 
474 it = next; 
475 } 
476 
477 Instances.Append(newInstance); 
478 PolyModels.Append(newModel); 
479 return true; 
480} 
481 
482 
483void tWorld::Load(const tString& tacFile, uint32 loadFilter) 
484{ 
485 Name = tacFile; 
486 LastLoadedFilename = tacFile; 
487 tChunkReader tac(tacFile); 
488 
489 tItList<tCamera> newCameras; 
490 tItList<tLight> newLights; 
491 tItList<tPath> newPaths; 
492 tItList<tMaterial> newMaterials; 
493 tItList<tSkeleton> newSkeletons; 
494 tItList<tPolyModel> newPolyModels; 
495 tItList<tLodGroup> newLodGroups; 
496 tItList<tInstance> newInstances; 
497 tItList<tSelection> newSelections; 
498 
499 for (tChunk tacChunk = tac.First(); tacChunk.Valid(); tacChunk = tacChunk.Next()) 
500 { 
501 switch (tacChunk.ID()) 
502 { 
503 case tChunkID::Core_Version: 
504 tacChunk.GetItem(MajorVersion); 
505 tacChunk.GetItem(MinorVersion); 
506 break; 
507 
508 case tChunkID::Scene_Scene: 
509 { 
510 for (tChunk chunk = tacChunk.First(); chunk.Valid(); chunk = chunk.Next()) 
511 { 
512 switch (chunk.ID()) 
513 { 
514 case tChunkID::Scene_MaterialList: 
515 if (loadFilter & tLoadFilter_Materials) 
516 LoadMaterials(chunk, newMaterials); 
517 break; 
518 
519 case tChunkID::Scene_ObjectList: 
520 LoadObjects(chunk, newPolyModels, newSkeletons, newCameras, newLights, newPaths, loadFilter); 
521 break; 
522 
523 case tChunkID::Scene_GroupList: 
524 if (loadFilter & tLoadFilter_LodGroups) 
525 LoadGroups(chunk, newLodGroups); 
526 break; 
527 
528 case tChunkID::Scene_InstanceList: 
529 if (loadFilter & tLoadFilter_Instances) 
530 LoadInstances(chunk, newInstances); 
531 break; 
532 
533 case tChunkID::Scene_SelectionList: 
534 if (loadFilter & tLoadFilter_Selections) 
535 LoadSelections(chunk, newSelections); 
536 break; 
537 } 
538 } 
539 break; 
540 } 
541 } 
542 } 
543 
544 // Fixup external references. The main external references are diffuse texture files found in the materials and the 
545 // shader file (shd). 
546 tString tacDir = tSystem::tGetDir(tacFile); 
547 
548 for (tItList<tMaterial>::Iter m = newMaterials.First(); m; ++m) 
549 { 
550 // We need to simplify paths by removing any up-directory markers ".." and same-directory markers ".". This 
551 // step is essential since two different tac files may refer to the same external file so we want the path to 
552 // the file to look the same no matter the original tac file location. This allows correct optimization when 
553 // checking for duplicate materials. 
554 if (!m->TextureDiffuse.IsEmpty()) 
555 m->TextureDiffuse = tSystem::tGetSimplifiedPath(tacDir + m->TextureDiffuse); 
556 
557 if (!m->TextureNormalMap.IsEmpty()) 
558 m->TextureNormalMap = tSystem::tGetSimplifiedPath(tacDir + m->TextureNormalMap); 
559 
560 if (!m->TextureA.IsEmpty()) 
561 m->TextureA = tSystem::tGetSimplifiedPath(tacDir + m->TextureA); 
562 
563 if (!m->TextureB.IsEmpty()) 
564 m->TextureB = tSystem::tGetSimplifiedPath(tacDir + m->TextureB); 
565 
566 if (!m->TextureC.IsEmpty()) 
567 m->TextureC = tSystem::tGetSimplifiedPath(tacDir + m->TextureC); 
568 
569 if (!m->TextureD.IsEmpty()) 
570 m->TextureD = tSystem::tGetSimplifiedPath(tacDir + m->TextureD); 
571 
572 if (!m->TextureE.IsEmpty()) 
573 m->TextureE = tSystem::tGetSimplifiedPath(tacDir + m->TextureE); 
574 
575 if (!m->ShaderFile.IsEmpty()) 
576 m->ShaderFile = tSystem::tGetSimplifiedPath(tacDir + m->ShaderFile); 
577 } 
578 
579 // Make sure every type of object has a unique ID. Object lists that refer to the corrected lists are also fixed 
580 // up. i.e. The first arg is a list of items that need their IDs adjusted to make them unique -- the remaining 
581 // arg(s) are items that refer to those adjusted IDs and need to be updated. 
582 CorrectCameraIDs(newCameras, newInstances); 
583 CorrectLightIDs(newLights, newInstances); 
584 CorrectPathIDs(newPaths, newInstances); 
585 CorrectMaterialIDs(newMaterials, newPolyModels); 
586 CorrectSkeletonIDs(newSkeletons, newPolyModels); 
587 
588 CorrectPolyModelIDs(newPolyModels, newLodGroups, newInstances); 
589 
590 // For now nothing refers to LOD groups -- just keep IDs unique. Eventually instances may refer to these. 
591 CorrectLodGroupIDs(newLodGroups); 
592 CorrectInstanceIDs(newInstances, newSelections); 
593 
594 // Nothing refers to selections. New selections that have the same name as existing selections get merged into the 
595 // existing. For selections it is based on the selection name. 
596 MergeToExistingSelections(newSelections); 
597 CorrectSelectionIDs(newSelections); 
598 
599 // We can now add the groups, cameras, lights, materials, skeletons, models, and instances to the scene. 
600 while (tCamera* c = newCameras.Remove()) 
601 Cameras.Append(c); 
602 
603 while (tLight* l = newLights.Remove()) 
604 Lights.Append(l); 
605 
606 while (tPath* s = newPaths.Remove()) 
607 Paths.Append(s); 
608 
609 while (tMaterial* m = newMaterials.Remove()) 
610 Materials.Append(m); 
611 
612 while (tSkeleton* s = newSkeletons.Remove()) 
613 Skeletons.Append(s); 
614 
615 while (tPolyModel* m = newPolyModels.Remove()) 
616 PolyModels.Append(m); 
617 
618 while (tLodGroup* l = newLodGroups.Remove()) 
619 LodGroups.Append(l); 
620 
621 while (tInstance* i = newInstances.Remove()) 
622 Instances.Append(i); 
623 
624 while (tSelection* s = newSelections.Remove()) 
625 Selections.Append(s); 
626} 
627 
628 
629void tWorld::CorrectCameraIDs(tItList<tCamera>& newCameras, tItList<tInstance>& newInstances) 
630{ 
631 int numNewCameras = newCameras.GetNumItems(); 
632 if (!numNewCameras) 
633 return; 
634 
635 uint32* newIDTable = new uint32[numNewCameras]; 
636 
637 // Decide on some new camera ID values. 
638 for (int i = 0; i < numNewCameras; i++) 
639 newIDTable[i] = NextCameraID++; 
640 
641 // Correct all references to these IDs by the instances. 
642 for (tItList<tInstance>::Iter inst = newInstances.First(); inst; ++inst) 
643 { 
644 if (inst->ObjectType != tInstance::tType::Camera) 
645 continue; 
646 
647 uint32 origID = inst->ObjectID; 
648 
649 // Find the correct cameras. 
650 int newCameraIndex = 0; 
651 bool foundCamera = false; 
652 for (tItList<tCamera>::Iter camera = newCameras.First(); camera; ++camera) 
653 { 
654 if (origID == camera->ID) 
655 { 
656 foundCamera = true; 
657 break; 
658 } 
659 
660 newCameraIndex++; 
661 } 
662 
663 if (!foundCamera) 
664 throw tError("Could not find camera with ID %d. Could be that the list has 2 models with the same ID.", origID); 
665 
666 inst->ObjectID = newIDTable[newCameraIndex]; 
667 } 
668 
669 // Assign the cameras their new ID. 
670 int index = 0; 
671 for (tItList<tCamera>::Iter l = newCameras.First(); l; ++l) 
672 l->ID = newIDTable[index++]; 
673 
674 delete[] newIDTable; 
675} 
676 
677 
678void tWorld::CorrectLightIDs(tItList<tLight>& newLights, tItList<tInstance>& newInstances) 
679{ 
680 int numNewLights = newLights.GetNumItems(); 
681 if (!numNewLights) 
682 return; 
683 
684 uint32* newIDTable = new uint32[numNewLights]; 
685 
686 // Decide on some new light ID values. 
687 for (int i = 0; i < numNewLights; i++) 
688 newIDTable[i] = NextLightID++; 
689 
690 // Correct all references to these IDs by the instances. 
691 for (tItList<tInstance>::Iter inst = newInstances.First(); inst; ++inst) 
692 { 
693 if (inst->ObjectType != tInstance::tType::Light) 
694 continue; 
695 
696 uint32 origID = inst->ObjectID; 
697 
698 // Find the correct lights. 
699 int newLightIndex = 0; 
700 bool foundLight = false; 
701 for (tItList<tLight>::Iter light = newLights.First(); light; ++light) 
702 { 
703 if (origID == light->ID) 
704 { 
705 foundLight = true; 
706 break; 
707 } 
708 
709 newLightIndex++; 
710 } 
711 
712 if (!foundLight) 
713 throw tError("Could not find light with ID %d. Could be that the list has 2 models with the same ID.", origID); 
714 
715 inst->ObjectID = newIDTable[newLightIndex]; 
716 } 
717 
718 // Assign the lights their new ID. 
719 int index = 0; 
720 for (tItList<tLight>::Iter l = newLights.First(); l; ++l) 
721 l->ID = newIDTable[index++]; 
722 
723 delete[] newIDTable; 
724} 
725 
726 
727void tWorld::CorrectPathIDs(tItList<tPath>& newPaths, tItList<tInstance>& newInstances ) 
728{ 
729 int numNewPaths = newPaths.GetNumItems(); 
730 if (!numNewPaths) 
731 return; 
732 
733 uint32* newIDTable = new uint32[numNewPaths]; 
734 
735 // Decide on some new path ID values. 
736 for (int i = 0; i < numNewPaths; i++) 
737 newIDTable[i] = NextPathID++; 
738 
739 // Correct all references to these IDs by the instances. 
740 for (tItList<tInstance>::Iter inst = newInstances.First(); inst; ++inst) 
741 { 
742 if (inst->ObjectType != tInstance::tType::Path) 
743 continue; 
744 
745 uint32 origID = inst->ObjectID; 
746 
747 // Find the correct path. 
748 int newPathIndex = 0; 
749 bool foundPath = false; 
750 for (tItList<tPath>::Iter path = newPaths.First(); path; ++path) 
751 { 
752 if (origID == path->ID) 
753 { 
754 foundPath = true; 
755 break; 
756 } 
757 
758 newPathIndex++; 
759 } 
760 
761 tAssert(foundPath); 
762 inst->ObjectID = newIDTable[newPathIndex]; 
763 } 
764 
765 // Assign the paths their new ID. 
766 int index = 0; 
767 for (tItList<tPath>::Iter s = newPaths.First(); s; ++s) 
768 s->ID = newIDTable[index++]; 
769 
770 delete[] newIDTable; 
771} 
772 
773 
774void tWorld::CorrectMaterialIDs(tItList<tMaterial>& newMaterials, tItList<tPolyModel>& newPolyModels) 
775{ 
776 // We need to make sure that there are no duplicated material IDs before adding the new models and materials to the 
777 // scene. We do this by assigning material IDs sequentially starting at 0 for any new material. We then traverse 
778 // all the models and adjust their material face IDs. This cannot be done in one pass -- if the models are added 
779 // before the adjustment then previously present models will be adjusted incorrectly. This is because the exporter 
780 // makes no guarantees about what material IDs it uses. 
781 int numNewMaterials = newMaterials.GetNumItems(); 
782 if (numNewMaterials) 
783 { 
784 uint32* newIDTable = new uint32[numNewMaterials]; 
785 
786 // Decide on some new material ID values. 
787 for (int i = 0; i < numNewMaterials; i++) 
788 newIDTable[i] = NextMaterialID++; 
789 
790 // Correct all references to these IDs in the models. Loop through all the faces on all the new models. 
791 for (tItList<tPolyModel>::Iter model = newPolyModels.First(); model; ++model) 
792 { 
793 tMesh& mesh = model->Mesh; 
794 
795 if (!mesh.FaceTableMaterialIDs) 
796 continue; 
797 
798 for (int f = 0; f < mesh.NumFaces; f++) 
799 { 
800 uint32 origID = mesh.FaceTableMaterialIDs[f]; 
801 
802 // Find the correct material. 
803 int newMatIndex = 0; 
804 bool foundMat = false; 
805 for (tItList<tMaterial>::Iter mat = newMaterials.First(); mat; ++mat) 
806 { 
807 if (origID == mat->ID) 
808 { 
809 foundMat = true; 
810 break; 
811 } 
812 
813 newMatIndex++; 
814 } 
815 
816 tAssert(foundMat); 
817 mesh.FaceTableMaterialIDs[f] = newIDTable[newMatIndex]; 
818 } 
819 } 
820 
821 // Assign the materials their new ID. 
822 int index = 0; 
823 for (tItList<tMaterial>::Iter m = newMaterials.First(); m; ++m) 
824 m->ID = newIDTable[index++]; 
825 
826 delete[] newIDTable; 
827 } 
828} 
829 
830 
831void tWorld::CorrectSkeletonIDs(tItList<tSkeleton>& newSkeletons, tItList<tPolyModel>& newPolyModels) 
832{ 
833 int numNewSkeletons = newSkeletons.GetNumItems(); 
834 if (numNewSkeletons) 
835 { 
836 uint32* newIDTable = new uint32[numNewSkeletons]; 
837 
838 // Decide on some new skeleton ID values. 
839 for (int i = 0; i < numNewSkeletons; i++) 
840 newIDTable[i] = NextSkeletonID++; 
841 
842 // Correct all references to these IDs in the models. 
843 for (tItList<tPolyModel>::Iter model = newPolyModels.First(); model; ++model) 
844 { 
845 tMesh& mesh = model->Mesh; 
846 
847 if (!mesh.VertTableWeightSets) 
848 continue; 
849 
850 // Loop through all the weight sets on all the new models. 
851 for (int set = 0; set < mesh.NumVertWeightSets; set++) 
852 { 
853 tWeightSet* weightSet = &mesh.VertTableWeightSets[set]; 
854 
855 for (int w = 0; w < weightSet->NumWeights; w++) 
856 { 
857 uint32 origID = weightSet->Weights[w].SkeletonID; 
858 
859 // Find the correct skeleton. 
860 int newSkelIndex = 0; 
861 bool foundSkel = false; 
862 for (tItList<tSkeleton>::Iter skel = newSkeletons.First(); skel; ++skel) 
863 { 
864 if (origID == skel->ID) 
865 { 
866 foundSkel = true; 
867 break; 
868 } 
869 
870 newSkelIndex++; 
871 } 
872 
873 tAssert(foundSkel); 
874 weightSet->Weights[w].SkeletonID = newIDTable[newSkelIndex]; 
875 } 
876 } 
877 } 
878 
879 // Assign the skeletons their new ID. 
880 int index = 0; 
881 for (tItList<tSkeleton>::Iter s = newSkeletons.First(); s; ++s) 
882 { 
883 s->ID = newIDTable[index++]; 
884 } 
885 
886 delete[] newIDTable; 
887 } 
888} 
889 
890 
891void tWorld::CorrectPolyModelIDs(tItList<tPolyModel>& newPolyModels, tItList<tLodGroup>& newLodGroups, tItList<tInstance>& newInstances) 
892{ 
893 int numNewModels = newPolyModels.GetNumItems(); 
894 if (!numNewModels) 
895 return; 
896 
897 uint32* newIDTable = new uint32[numNewModels]; 
898 
899 // Decide on some new poly model ID values. 
900 for (int i = 0; i < numNewModels; i++) 
901 newIDTable[i] = NextPolyModelID++; 
902 
903 // Correct all references to these IDs by the LOD groups. 
904 for (tItList<tLodGroup>::Iter group = newLodGroups.First(); group; ++group) 
905 { 
906 for (tItList<tLodParam>::Iter lod = group->LodParams.First(); lod; ++lod) 
907 { 
908 uint32 origID = lod->ModelID; 
909 
910 // Find the correct poly model. 
911 int newModelIndex = 0; 
912 bool foundModel = false; 
913 for (tItList<tPolyModel>::Iter model = newPolyModels.First(); model; ++model) 
914 { 
915 if (origID == model->ID) 
916 { 
917 foundModel = true; 
918 break; 
919 } 
920 
921 newModelIndex++; 
922 } 
923 
924 tAssert(foundModel); 
925 lod->ModelID = newIDTable[newModelIndex]; 
926 } 
927 } 
928 
929 // Correct all references to these IDs by the instances. 
930 for (tItList<tInstance>::Iter inst = newInstances.First(); inst; ++inst) 
931 { 
932 if (inst->ObjectType != tInstance::tType::PolyModel) 
933 continue; 
934 
935 uint32 origID = inst->ObjectID; 
936 
937 // Find the correct poly model. 
938 int newModelIndex = 0; 
939 bool foundModel = false; 
940 for (tItList<tPolyModel>::Iter model = newPolyModels.First(); model; ++model) 
941 { 
942 if (origID == model->ID) 
943 { 
944 foundModel = true; 
945 break; 
946 } 
947 
948 newModelIndex++; 
949 } 
950 
951 if (!foundModel) 
952 throw tError("Could not find model with ID %d. Could be that the list has 2 models with the same ID.", origID); 
953 
954 inst->ObjectID = newIDTable[newModelIndex]; 
955 } 
956 
957 // Assign the poly models their new ID. 
958 int index = 0; 
959 for (tItList<tPolyModel>::Iter m = newPolyModels.First(); m; ++m) 
960 m->ID = newIDTable[index++]; 
961 
962 delete[] newIDTable; 
963} 
964 
965 
966void tWorld::CorrectLodGroupIDs(tItList<tLodGroup>& newLodGroups) 
967{ 
968 // Just keep IDs unique. Groups are top level so no-one needs to be told about the changes. 
969 for (tItList<tLodGroup>::Iter group = newLodGroups.First(); group; ++group) 
970 { 
971 group->ID = NextLodGroupID; 
972 NextLodGroupID++; 
973 } 
974} 
975 
976 
977void tWorld::CorrectInstanceIDs(tItList<tInstance>& newInstances, tItList<tSelection>& newSelections) 
978{ 
979 int numNewInstances = newInstances.GetNumItems(); 
980 if (!numNewInstances) 
981 return; 
982 
983 uint32* newIDTable = new uint32[numNewInstances]; 
984 
985 // Decide on some new instance ID values. 
986 for (int i = 0; i < numNewInstances; i++) 
987 newIDTable[i] = NextInstanceID++; 
988 
989 // Correct all references to these IDs by the selections. 
990 for (tItList<tSelection>::Iter sel = newSelections.First(); sel; ++sel) 
991 { 
992 // Loop through all selection's instance IDs. 
993 for (tItList<uint32>::Iter instIDIter = sel->InstanceIDs.First(); instIDIter; ++instIDIter) 
994 { 
995 uint32* instID = instIDIter.GetObject(); 
996 uint32 origID = *instID; 
997 
998 // Find the correct instances. 
999 int newInstanceIndex = 0; 
1000 bool foundInstance = false; 
1001 for (tItList<tInstance>::Iter inst = newInstances.First(); inst; ++inst) 
1002 { 
1003 if (origID == inst->ID) 
1004 { 
1005 foundInstance = true; 
1006 break; 
1007 } 
1008 
1009 newInstanceIndex++; 
1010 } 
1011 
1012 if (!foundInstance) 
1013 throw tError("Could not find instance with ID %d while resolving selections.", origID); 
1014 
1015 *instID = newIDTable[newInstanceIndex]; 
1016 } 
1017 } 
1018 
1019 // Assign the instances their new ID. 
1020 int index = 0; 
1021 for (tItList<tInstance>::Iter i = newInstances.First(); i; ++i) 
1022 i->ID = newIDTable[index++]; 
1023 
1024 delete[] newIDTable; 
1025} 
1026 
1027 
1028void tWorld::MergeToExistingSelections(tItList<tSelection>& newSelections) 
1029{ 
1030 tItList<tSelection>::Iter newSel = newSelections.First(); 
1031 while (newSel) 
1032 { 
1033 tItList<tSelection>::Iter nextSel = newSel; 
1034 ++nextSel; 
1035 
1036 // Search for an existing selection with the same name as the new one. 
1037 tSelection* existingSel = FindSelection(newSel->Name); 
1038 if (!existingSel) 
1039 { 
1040 newSel = nextSel; 
1041 continue; 
1042 } 
1043 
1044 // Transfer all the instance IDs to the existing selection if they aren't there already. 
1045 for (tItList<uint32>::Iter instID = newSel->InstanceIDs.First(); instID; ++instID) 
1046 { 
1047 uint32 newID = *(instID.GetObject()); 
1048 if (existingSel->ContainsInstance(newID)) 
1049 continue; 
1050 
1051 existingSel->InstanceIDs.Append(new uint32(newID)); 
1052 } 
1053 
1054 // newSel has been merged so we're done with it. 
1055 delete newSelections.Remove(newSel); 
1056 newSel = nextSel; 
1057 } 
1058} 
1059 
1060 
1061void tWorld::CorrectSelectionIDs(tItList<tSelection>& newSelections) 
1062{ 
1063 // Just keep IDs unique. Selections are top level so no-one needs to be told about the changes. 
1064 for (tItList<tSelection>::Iter sel = newSelections.First(); sel; ++sel) 
1065 { 
1066 sel->ID = NextSelectionID; 
1067 NextSelectionID++; 
1068 } 
1069} 
1070 
1071 
1072void tWorld::Save(const tString& tacFile) const 
1073{ 
1074 tChunkWriter chunk(tacFile); 
1075 
1076 chunk.Begin(tChunkID::Core_Version); 
1077 { 
1078 chunk.Write(SceneMajorVersion); 
1079 chunk.Write(SceneMinorVersion); 
1080 } 
1081 chunk.End(); 
1082 
1083 chunk.Begin(tChunkID::Scene_Scene); 
1084 { 
1085 SaveMaterials(chunk); 
1086 SaveObjects(chunk); 
1087 SaveGroups(chunk); 
1088 SaveInstances(chunk); 
1089 SaveSelections(chunk); 
1090 } 
1091 chunk.End(); 
1092} 
1093 
1094 
1095void tWorld::SaveMaterials(tChunkWriter& chunk) const 
1096{ 
1097 chunk.Begin(tChunkID::Scene_MaterialList); 
1098 { 
1099 for (tItList<tMaterial>::Iter material = Materials.First(); material; ++material) 
1100 material->Save(chunk); 
1101 } 
1102 chunk.End(); 
1103} 
1104 
1105 
1106void tWorld::SaveObjects(tChunkWriter& chunk) const 
1107{ 
1108 chunk.Begin(tChunkID::Scene_ObjectList); 
1109 { 
1110 for (tItList<tSkeleton>::Iter skeleton = Skeletons.First(); skeleton; ++skeleton) 
1111 skeleton->Save(chunk); 
1112 
1113 for (tItList<tPolyModel>::Iter polyModel = PolyModels.First(); polyModel; ++polyModel) 
1114 polyModel->Save(chunk); 
1115 
1116 for (tItList<tCamera>::Iter camera = Cameras.First(); camera; ++camera) 
1117 camera->Save(chunk); 
1118  
1119 for (tItList<tLight>::Iter light = Lights.First(); light; ++light) 
1120 light->Save(chunk); 
1121 
1122 for (tItList<tPath>::Iter path = Paths.First(); path; ++path) 
1123 path->Save(chunk); 
1124 } 
1125 chunk.End(); 
1126} 
1127 
1128 
1129void tWorld::SaveGroups(tChunkWriter& chunk) const 
1130{ 
1131 chunk.Begin(tChunkID::Scene_GroupList); 
1132 { 
1133 for (tItList<tLodGroup>::Iter lodGroup = LodGroups.First(); lodGroup; ++lodGroup) 
1134 lodGroup->Save(chunk); 
1135 } 
1136 chunk.End(); 
1137} 
1138 
1139 
1140void tWorld::SaveInstances(tChunkWriter& chunk) const 
1141{ 
1142 chunk.Begin(tChunkID::Scene_InstanceList); 
1143 { 
1144 for (tItList<tInstance>::Iter instance = Instances.First(); instance; ++instance) 
1145 instance->Save(chunk); 
1146 } 
1147 chunk.End(); 
1148} 
1149 
1150 
1151void tWorld::SaveSelections(tChunkWriter& chunk) const 
1152{ 
1153 chunk.Begin(tChunkID::Scene_SelectionList); 
1154 { 
1155 for (tItList<tSelection>::Iter sel = Selections.First(); sel; ++sel) 
1156 sel->Save(chunk); 
1157 } 
1158 chunk.End(); 
1159} 
1160 
1161 
1162void tWorld::LoadMaterials(const tChunk& matListChunk, tItList<tMaterial>& materials) 
1163{ 
1164 tAssert(matListChunk.ID() == tChunkID::Scene_MaterialList); 
1165 tAssert(materials.IsEmpty()); 
1166 
1167 for (tChunk chunk = matListChunk.First(); chunk.Valid(); chunk = chunk.Next()) 
1168 { 
1169 switch (chunk.ID()) 
1170 { 
1171 case tChunkID::Scene_Material: 
1172 materials.Append(new tMaterial(chunk)); 
1173 break; 
1174 } 
1175 } 
1176} 
1177 
1178 
1179void tWorld::LoadObjects(const tChunk& objListChunk, tItList<tPolyModel>& polyModels, tItList<tSkeleton>& skeletons, tItList<tCamera>& cameras, tItList<tLight>& lights, tItList<tPath>& paths, uint32 loadFlags) 
1180{ 
1181 tAssert(objListChunk.ID() == tChunkID::Scene_ObjectList); 
1182 tAssert(polyModels.IsEmpty()); 
1183 tAssert(skeletons.IsEmpty()); 
1184 tAssert(cameras.IsEmpty()); 
1185 tAssert(lights.IsEmpty()); 
1186 tAssert(paths.IsEmpty()); 
1187 
1188 for (tChunk chunk = objListChunk.First(); chunk.Valid(); chunk = chunk.Next()) 
1189 { 
1190 switch (chunk.ID()) 
1191 { 
1192 case tChunkID::Scene_PolyModel: 
1193 if (loadFlags & tLoadFilter_Models) 
1194 polyModels.Append(new tPolyModel(chunk)); 
1195 break; 
1196 
1197 case tChunkID::Scene_Skeleton: 
1198 if (loadFlags & tLoadFilter_Skeletons) 
1199 skeletons.Append(new tSkeleton(chunk)); 
1200 break; 
1201 
1202 case tChunkID::Scene_Camera: 
1203 if (loadFlags & tLoadFilter_Cameras) 
1204 cameras.Append(new tCamera(chunk)); 
1205 break; 
1206 
1207 case tChunkID::Scene_Light: 
1208 if (loadFlags & tLoadFilter_Lights) 
1209 lights.Append(new tLight(chunk)); 
1210 break; 
1211 
1212 case tChunkID::Scene_Path: 
1213 if (loadFlags & tLoadFilter_Paths) 
1214 paths.Append(new tPath(chunk)); 
1215 break; 
1216 
1217 case tChunkID::Scene_PatchModel: 
1218 // Not implemented. 
1219 break; 
1220 } 
1221 } 
1222} 
1223 
1224 
1225void tWorld::LoadGroups(const tChunk& groupListChunk, tItList<tLodGroup>& lodGroups) 
1226{ 
1227 tAssert(groupListChunk.GetID() == tChunkID::Scene_GroupList); 
1228 tAssert(lodGroups.IsEmpty()); 
1229 
1230 for (tChunk chunk = groupListChunk.First(); chunk.Valid(); chunk = chunk.Next()) 
1231 { 
1232 switch (chunk.GetID()) 
1233 { 
1234 case tChunkID::Scene_LodGroup: 
1235 lodGroups.Append(new tLodGroup(chunk)); 
1236 break; 
1237 } 
1238 } 
1239} 
1240 
1241 
1242void tWorld::LoadInstances(const tChunk& instListChunk, tItList<tInstance>& instances) 
1243{ 
1244 tAssert(instListChunk.ID() == tChunkID::Scene_InstanceList); 
1245 tAssert(instances.IsEmpty()); 
1246 
1247 for (tChunk chunk = instListChunk.First(); chunk.Valid(); chunk = chunk.Next()) 
1248 { 
1249 switch (chunk.ID()) 
1250 { 
1251 case tChunkID::Scene_Instance: 
1252 instances.Append(new tInstance(chunk)); 
1253 break; 
1254 } 
1255 } 
1256} 
1257 
1258 
1259void tWorld::LoadSelections(const tChunk& selListChunk, tItList<tSelection>& selections) 
1260{ 
1261 tAssert(selListChunk.ID() == tChunkID::Scene_SelectionList); 
1262 tAssert(selections.IsEmpty()); 
1263 
1264 for (tChunk chunk = selListChunk.First(); chunk.Valid(); chunk = chunk.Next()) 
1265 { 
1266 switch (chunk.ID()) 
1267 { 
1268 case tChunkID::Scene_Selection: 
1269 selections.Append(new tSelection(chunk)); 
1270 break; 
1271 } 
1272 } 
1273} 
1274 
1275 
1276int tWorld::GetMaterials(const tMesh& mesh, tItList<tMaterial>& materials) 
1277{ 
1278 if (!mesh.FaceTableMaterialIDs) 
1279 return 0; 
1280 
1281 int count = 0; 
1282 for (int f = 0; f < mesh.NumFaces; ++f) 
1283 { 
1284 tMaterial* mat = FindMaterial(mesh.FaceTableMaterialIDs[f]); 
1285 if (mat) 
1286 { 
1287 if (!materials.Find(mat)) 
1288 { 
1289 materials.Append(mat); 
1290 count++; 
1291 } 
1292 } 
1293 } 
1294 
1295 return count; 
1296} 
1297 
1298 
1299int tWorld::GetNumMaterials(const tString& name) const 
1300{ 
1301 if (name.IsEmpty()) 
1302 return Materials.GetNumItems(); 
1303 
1304 int count = 0; 
1305 for (tItList<tMaterial>::Iter m = Materials.First(); m; ++m) 
1306 if (m->Name == name) 
1307 count++; 
1308 
1309 return count; 
1310} 
1311 
1312 
1313void tWorld::InsertMaterial(tMaterial* material) 
1314{ 
1315 Materials.Append(material); 
1316} 
1317 
1318 
1319tMaterial* tWorld::FindMaterial(const tString& name) const 
1320{ 
1321 for (tItList<tMaterial>::Iter m = Materials.First(); m; ++m) 
1322 if (m->Name == name) 
1323 return m; 
1324 
1325 return nullptr; 
1326} 
1327 
1328 
1329tMaterial* tWorld::FindMaterial(uint32 id) const 
1330{ 
1331 for (tItList<tMaterial>::Iter m = Materials.First(); m; ++m) 
1332 if (m->ID == id) 
1333 return m; 
1334 
1335 return nullptr; 
1336} 
1337 
1338 
1339int tWorld::GetNumSkeletons(const tString& name) const 
1340{ 
1341 if (name.IsEmpty()) 
1342 return Skeletons.GetNumItems(); 
1343 
1344 int count = 0; 
1345 for (tItList<tSkeleton>::Iter s = Skeletons.First(); s; ++s) 
1346 if (s->Name == name) 
1347 count++; 
1348 
1349 return count; 
1350} 
1351 
1352 
1353void tWorld::InsertSkeleton(tSkeleton* skeleton) 
1354{ 
1355 Skeletons.Append(skeleton); 
1356} 
1357 
1358 
1359tSkeleton* tWorld::FindSkeleton(const tString& name) const 
1360{ 
1361 for (tItList<tSkeleton>::Iter s = Skeletons.First(); s; ++s) 
1362 if (s->Name == name) 
1363 return s; 
1364 
1365 return nullptr; 
1366} 
1367 
1368 
1369tSkeleton* tWorld::FindSkeleton(uint32 id) const 
1370{ 
1371 for (tItList<tSkeleton>::Iter s = Skeletons.First(); s; ++s) 
1372 if (s->ID == id) 
1373 return s; 
1374 
1375 return nullptr; 
1376} 
1377 
1378 
1379int tWorld::GetNumModels(const tString& name) const 
1380{ 
1381 if (name.IsEmpty()) 
1382 return PolyModels.GetNumItems(); 
1383 
1384 int count = 0; 
1385 for (tItList<tPolyModel>::Iter m = PolyModels.First(); m; ++m) 
1386 if (m->Name == name) 
1387 count++; 
1388 
1389 return count; 
1390} 
1391 
1392 
1393void tWorld::InsertPolyModel(tPolyModel* polyModel) 
1394{ 
1395 PolyModels.Append(polyModel); 
1396} 
1397 
1398 
1399tPolyModel* tWorld::FindPolyModel(const tString& name) const 
1400{ 
1401 for (tItList<tPolyModel>::Iter m = PolyModels.First(); m; ++m) 
1402 if (m->Name == name) 
1403 return m; 
1404 
1405 return nullptr; 
1406} 
1407 
1408 
1409tPolyModel* tWorld::FindPolyModel(uint32 id) const 
1410{ 
1411 for (tItList<tPolyModel>::Iter m = PolyModels.First(); m; ++m) 
1412 if (m->ID == id) 
1413 return m; 
1414 
1415 return nullptr; 
1416} 
1417 
1418 
1419int tWorld::GetNumCameras(const tString& name) const 
1420{ 
1421 if (name.IsEmpty()) 
1422 return Cameras.GetNumItems(); 
1423 
1424 int count = 0; 
1425 for (tItList<tCamera>::Iter c = Cameras.First(); c; ++c) 
1426 if (c->Name == name) 
1427 count++; 
1428 
1429 return count; 
1430} 
1431 
1432 
1433void tWorld::InsertCamera(tCamera* camera) 
1434{ 
1435 Cameras.Append(camera); 
1436} 
1437 
1438 
1439tCamera* tWorld::FindCamera(const tString& name) const 
1440{ 
1441 for (tItList<tCamera>::Iter c = Cameras.First(); c; ++c) 
1442 if (c->Name == name) 
1443 return c; 
1444 
1445 return nullptr; 
1446} 
1447 
1448 
1449tCamera* tWorld::FindCamera(uint32 id) const 
1450{ 
1451 for (tItList<tCamera>::Iter c = Cameras.First(); c; ++c) 
1452 if (c->ID == id) 
1453 return c; 
1454 
1455 return nullptr; 
1456} 
1457 
1458 
1459int tWorld::GetNumLights(const tString& name) const 
1460{ 
1461 if (name.IsEmpty()) 
1462 return Lights.GetNumItems(); 
1463 
1464 int count = 0; 
1465 for (tItList<tLight>::Iter l = Lights.First(); l; ++l) 
1466 if (l->Name == name) 
1467 count++; 
1468 
1469 return count; 
1470} 
1471 
1472 
1473void tWorld::InsertLight(tLight* light) 
1474{ 
1475 Lights.Append(light); 
1476} 
1477 
1478 
1479tLight* tWorld::FindLight(const tString& name) const 
1480{ 
1481 for (tItList<tLight>::Iter l = Lights.First(); l; ++l) 
1482 if (l->Name == name) 
1483 return l; 
1484 
1485 return nullptr; 
1486} 
1487 
1488 
1489tLight* tWorld::FindLight(uint32 id) const 
1490{ 
1491 for (tItList<tLight>::Iter l = Lights.First(); l; ++l) 
1492 if (l->ID == id) 
1493 return l; 
1494 
1495 return nullptr; 
1496} 
1497 
1498 
1499int tWorld::GetNumPaths(const tString& name) const 
1500{ 
1501 if (name.IsEmpty()) 
1502 return Paths.GetNumItems(); 
1503 
1504 int count = 0; 
1505 for (tItList<tPath>::Iter s = Paths.First(); s; ++s) 
1506 if (s->Name == name) 
1507 count++; 
1508 
1509 return count; 
1510} 
1511 
1512 
1513void tWorld::InsertPath(tPath* path) 
1514{ 
1515 Paths.Append(path); 
1516} 
1517 
1518 
1519tPath* tWorld::FindPath(const tString& name) const 
1520{ 
1521 for (tItList<tPath>::Iter s = Paths.First(); s; ++s) 
1522 if (s->Name == name) 
1523 return s; 
1524 
1525 return nullptr; 
1526} 
1527 
1528 
1529tPath* tWorld::FindPath(uint32 id) const 
1530{ 
1531 for (tItList<tPath>::Iter s = Paths.First(); s; ++s) 
1532 if (s->ID == id) 
1533 return s; 
1534 
1535 return nullptr; 
1536} 
1537 
1538 
1539int tWorld::GetNumLodGroups(const tString& name) const 
1540{ 
1541 if (name.IsEmpty()) 
1542 return LodGroups.GetNumItems(); 
1543 
1544 int count = 0; 
1545 for (tItList<tLodGroup>::Iter g = LodGroups.First(); g; ++g) 
1546 if (g->Name == name) 
1547 count++; 
1548 
1549 return count; 
1550} 
1551 
1552 
1553void tWorld::InsertLodGroup(tLodGroup* lodGroup) 
1554{ 
1555 LodGroups.Append(lodGroup); 
1556} 
1557 
1558 
1559tLodGroup* tWorld::FindLodGroup(const tString& name) const 
1560{ 
1561 for (tItList<tLodGroup>::Iter g = LodGroups.First(); g; ++g) 
1562 if (g->Name == name) 
1563 return g; 
1564 
1565 return nullptr; 
1566} 
1567 
1568 
1569tLodGroup* tWorld::FindLodGroup(uint32 id) const 
1570{ 
1571 for (tItList<tLodGroup>::Iter g = LodGroups.First(); g; ++g) 
1572 if (g->ID == id) 
1573 return g; 
1574 
1575 return nullptr; 
1576} 
1577 
1578 
1579int tWorld::GenerateLodGroupsFromModelNamingConvention() 
1580{ 
1581 int numGroupsCreated = 0; 
1582 
1583 // Figure out the starting ID for any new groups. 
1584 uint32 nextLodGroupID = 0; 
1585 for (tItList<tLodGroup>::Iter grp = LodGroups.First(); grp; ++grp) 
1586 if (grp->ID > nextLodGroupID) 
1587 nextLodGroupID = grp->ID; 
1588 
1589 nextLodGroupID++; 
1590 
1591 // Go through all the models and check the names, creating new groups as necessary. 
1592 for (tItList<tPolyModel>::Iter model = PolyModels.First(); model; ++model) 
1593 { 
1594 // If the model is already a member of an LOD group then we just skip it. 
1595 if (model->IsLodGroupMember) 
1596 continue; 
1597 
1598 // Look at the model's name and decide if it needs to be added to an LOD group. If it does, it sets the model's 
1599 // bIsLodGroupMember to true and adds it to the correctly named group. The following name manipulation works 
1600 // fine for non-lod names. We check if the end of the name contains "LOD_n", where n is a percent. If it isn't 
1601 // then it's a normal model and we have nothing to do here. 
1602 tString baseName = model->Name; 
1603 tString lodThreshold = baseName.ExtractRight('_'); 
1604 tString lodTag = baseName.ExtractRight('_'); 
1605 
1606 lodTag.LowCase(); 
1607 if (lodTag != "lod") 
1608 continue; 
1609 
1610 model->IsLodGroupMember = true; 
1611 
1612 // Find the correct LOD group to add this to, or create a new one if it doesn't exist. 
1613 tLodGroup* group = FindLodGroup(baseName); 
1614 if (!group) 
1615 { 
1616 group = new tLodGroup(); 
1617 group->ID = nextLodGroupID++; 
1618 group->Name = baseName; 
1619 LodGroups.Append(group); 
1620 numGroupsCreated++; 
1621 } 
1622 
1623 tLodParam* newInfo = new tLodParam(); 
1624 newInfo->ModelID = model->ID; 
1625 
1626 // Deal with the threshold. If the lodThreshold string is empty a value of 0.0f will be used. The value is 
1627 // entered in the content creation tool as a percent so we divide by 100. 
1628 newInfo->Threshold = lodThreshold.GetAsFloat() / 100.0f; 
1629 group->LodParams.Append(newInfo); 
1630 } 
1631 
1632 // All LOD groups must be sorted. 
1633 for (tItList<tLodGroup>::Iter group = LodGroups.First(); group; ++group) 
1634 group->Sort(); 
1635 
1636 return numGroupsCreated; 
1637} 
1638 
1639 
1640int tWorld::GetNumInstances(const tString& name) const 
1641{ 
1642 if (name.IsEmpty()) 
1643 return Instances.GetNumItems(); 
1644 
1645 int count = 0; 
1646 for (tItList<tInstance>::Iter inst = Instances.First(); inst; ++inst) 
1647 if (inst->Name == name) 
1648 count++; 
1649 
1650 return count; 
1651} 
1652 
1653 
1654void tWorld::InsertInstance(tInstance* instance) 
1655{ 
1656 Instances.Append(instance); 
1657} 
1658 
1659 
1660tInstance* tWorld::FindInstance(const tString& name) const 
1661{ 
1662 for (tItList<tInstance>::Iter inst = Instances.First(); inst; ++inst) 
1663 if (inst->Name == name) 
1664 return inst; 
1665 
1666 return nullptr; 
1667} 
1668 
1669 
1670tInstance* tWorld::FindInstance(uint32 id) const 
1671{ 
1672 for (tItList<tInstance>::Iter inst = Instances.First(); inst; ++inst) 
1673 if (inst->ID == id) 
1674 return inst; 
1675 
1676 return nullptr; 
1677} 
1678 
1679 
1680bool tWorld::IsInstanceInSelection(const tInstance* inst, const tSelection* sel) const 
1681{ 
1682 if (!sel || !inst) 
1683 return false; 
1684 
1685 return sel->ContainsInstance(inst->ID) ? true : false; 
1686} 
1687 
1688 
1689int tWorld::GetNumSelections(const tString& name) const 
1690{ 
1691 if (name.IsEmpty()) 
1692 return Selections.GetNumItems(); 
1693 
1694 int count = 0; 
1695 for (tItList<tSelection>::Iter inst = Selections.First(); inst; ++inst) 
1696 if (inst->Name == name) 
1697 count++; 
1698 
1699 return count; 
1700} 
1701 
1702 
1703void tWorld::InsertSelection(tSelection* sel) 
1704{ 
1705 Selections.Append(sel); 
1706} 
1707 
1708 
1709tSelection* tWorld::FindSelection(const tString& name) const 
1710{ 
1711 for (tItList<tSelection>::Iter sel = Selections.First(); sel; ++sel) 
1712 if (sel->Name == name) 
1713 return sel; 
1714 
1715 return nullptr; 
1716} 
1717 
1718 
1719void tWorld::FindSelectionsContaining(tItList<tSelection*>& sels, const tString& partialName) const 
1720{ 
1721 for (tItList<tSelection>::Iter sel = Selections.First(); sel; ++sel) 
1722 { 
1723 if (sel->Name.FindString(partialName) != -1) 
1724 { 
1725 tSelection** newSelPointer = new (tSelection*); 
1726 *newSelPointer = sel.GetObject(); 
1727 sels.Append(newSelPointer); 
1728 } 
1729 } 
1730} 
1731 
1732 
1733tSelection* tWorld::FindSelection(uint32 id) const 
1734{ 
1735 for (tItList<tSelection>::Iter sel = Selections.First(); sel; ++sel) 
1736 if (sel->ID == id) 
1737 return sel; 
1738 
1739 return nullptr; 
1740} 
1741 
1742 
1743}