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"  |
25 | using namespace tMath;  |
26 | using namespace tStd;  |
27 | namespace tScene  |
28 | {  |
29 |   |
30 |   |
31 | void 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 |   |
62 | void 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 |   |
84 | void 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 |   |
95 | void 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 |   |
144 | bool 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 |   |
240 | bool 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 |   |
483 | void 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 |   |
629 | void 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 |   |
678 | void 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 |   |
727 | void 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 |   |
774 | void 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 |   |
831 | void 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 |   |
891 | void 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 |   |
966 | void 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 |   |
977 | void 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 |   |
1028 | void 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 |   |
1061 | void 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 |   |
1072 | void 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 |   |
1095 | void 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 |   |
1106 | void 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 |   |
1129 | void 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 |   |
1140 | void 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 |   |
1151 | void 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 |   |
1162 | void 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 |   |
1179 | void 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 |   |
1225 | void 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 |   |
1242 | void 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 |   |
1259 | void 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 |   |
1276 | int 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 |   |
1299 | int 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 |   |
1313 | void tWorld::InsertMaterial(tMaterial* material)  |
1314 | {  |
1315 | Materials.Append(material);  |
1316 | }  |
1317 |   |
1318 |   |
1319 | tMaterial* 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 |   |
1329 | tMaterial* 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 |   |
1339 | int 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 |   |
1353 | void tWorld::InsertSkeleton(tSkeleton* skeleton)  |
1354 | {  |
1355 | Skeletons.Append(skeleton);  |
1356 | }  |
1357 |   |
1358 |   |
1359 | tSkeleton* 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 |   |
1369 | tSkeleton* 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 |   |
1379 | int 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 |   |
1393 | void tWorld::InsertPolyModel(tPolyModel* polyModel)  |
1394 | {  |
1395 | PolyModels.Append(polyModel);  |
1396 | }  |
1397 |   |
1398 |   |
1399 | tPolyModel* 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 |   |
1409 | tPolyModel* 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 |   |
1419 | int 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 |   |
1433 | void tWorld::InsertCamera(tCamera* camera)  |
1434 | {  |
1435 | Cameras.Append(camera);  |
1436 | }  |
1437 |   |
1438 |   |
1439 | tCamera* 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 |   |
1449 | tCamera* 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 |   |
1459 | int 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 |   |
1473 | void tWorld::InsertLight(tLight* light)  |
1474 | {  |
1475 | Lights.Append(light);  |
1476 | }  |
1477 |   |
1478 |   |
1479 | tLight* 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 |   |
1489 | tLight* 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 |   |
1499 | int 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 |   |
1513 | void tWorld::InsertPath(tPath* path)  |
1514 | {  |
1515 | Paths.Append(path);  |
1516 | }  |
1517 |   |
1518 |   |
1519 | tPath* 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 |   |
1529 | tPath* 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 |   |
1539 | int 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 |   |
1553 | void tWorld::InsertLodGroup(tLodGroup* lodGroup)  |
1554 | {  |
1555 | LodGroups.Append(lodGroup);  |
1556 | }  |
1557 |   |
1558 |   |
1559 | tLodGroup* 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 |   |
1569 | tLodGroup* 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 |   |
1579 | int 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 |   |
1640 | int 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 |   |
1654 | void tWorld::InsertInstance(tInstance* instance)  |
1655 | {  |
1656 | Instances.Append(instance);  |
1657 | }  |
1658 |   |
1659 |   |
1660 | tInstance* 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 |   |
1670 | tInstance* 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 |   |
1680 | bool 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 |   |
1689 | int 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 |   |
1703 | void tWorld::InsertSelection(tSelection* sel)  |
1704 | {  |
1705 | Selections.Append(sel);  |
1706 | }  |
1707 |   |
1708 |   |
1709 | tSelection* 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 |   |
1719 | void 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 |   |
1733 | tSelection* 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 | } |