| 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 | } |