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}