void TSShapeLoader::recurseSubshape(AppNode* appNode, S32 parentIndex, bool recurseChildren) { // Ignore local bounds nodes if (appNode->isBounds()) return; S32 subShapeNum = shape->subShapeFirstNode.size()-1; Subshape* subshape = subshapes[subShapeNum]; // Check if we should collapse this node S32 myIndex; if (ignoreNode(appNode->getName())) { myIndex = parentIndex; } else { // Check that adding this node will not exceed the maximum node count if (shape->nodes.size() >= MAX_TS_SET_SIZE) return; myIndex = shape->nodes.size(); String nodeName = getUniqueName(appNode->getName(), cmpShapeName, shape->names); // Create the 3space node shape->nodes.increment(); shape->nodes.last().nameIndex = shape->addName(nodeName); shape->nodes.last().parentIndex = parentIndex; shape->nodes.last().firstObject = -1; shape->nodes.last().firstChild = -1; shape->nodes.last().nextSibling = -1; // Add the AppNode to a matching list (so AppNodes can be accessed using 3space // node indices) appNodes.push_back(appNode); appNodes.last()->mParentIndex = parentIndex; // Check for NULL detail or AutoBillboard nodes (no children or geometry) if ((appNode->getNumChildNodes() == 0) && (appNode->getNumMesh() == 0)) { S32 size = 0x7FFFFFFF; String dname(String::GetTrailingNumber(appNode->getName(), size)); if (dStrEqual(dname, "nulldetail") && (size != 0x7FFFFFFF)) { shape->addDetail("detail", size, subShapeNum); } else if (appNode->isBillboard() && (size != 0x7FFFFFFF)) { // AutoBillboard detail S32 numEquatorSteps = 4; S32 numPolarSteps = 0; F32 polarAngle = 0.0f; S32 dl = 0; S32 dim = 64; bool includePoles = true; appNode->getInt("BB::EQUATOR_STEPS", numEquatorSteps); appNode->getInt("BB::POLAR_STEPS", numPolarSteps); appNode->getFloat("BB::POLAR_ANGLE", polarAngle); appNode->getInt("BB::DL", dl); appNode->getInt("BB::DIM", dim); appNode->getBool("BB::INCLUDE_POLES", includePoles); S32 detIndex = shape->addDetail( "bbDetail", size, -1 ); shape->details[detIndex].bbEquatorSteps = numEquatorSteps; shape->details[detIndex].bbPolarSteps = numPolarSteps; shape->details[detIndex].bbDetailLevel = dl; shape->details[detIndex].bbDimension = dim; shape->details[detIndex].bbIncludePoles = includePoles; shape->details[detIndex].bbPolarAngle = polarAngle; } } } // Collect geometry for (U32 iMesh = 0; iMesh < appNode->getNumMesh(); iMesh++) { AppMesh* mesh = appNode->getMesh(iMesh); if (!ignoreMesh(mesh->getName())) { subshape->objMeshes.push_back(mesh); subshape->objNodes.push_back(mesh->isSkin() ? -1 : myIndex); } } // Create children if (recurseChildren) { for (int iChild = 0; iChild < appNode->getNumChildNodes(); iChild++) recurseSubshape(appNode->getChildNode(iChild), myIndex, true); } }
void TSShapeLoader::generateObjects() { for (S32 iSub = 0; iSub < subshapes.size(); iSub++) { Subshape* subshape = subshapes[iSub]; shape->subShapeFirstObject.push_back(shape->objects.size()); // Get the names and sizes of the meshes for this subshape Vector<String> meshNames; for (S32 iMesh = 0; iMesh < subshape->objMeshes.size(); iMesh++) { AppMesh* mesh = subshape->objMeshes[iMesh]; mesh->detailSize = 2; String name = String::GetTrailingNumber( mesh->getName(), mesh->detailSize ); name = getUniqueName( name, cmpMeshNameAndSize, meshNames, &(subshape->objMeshes), (void*)mesh->detailSize ); meshNames.push_back( name ); // Fix up any collision details that don't have a negative detail level. if ( dStrStartsWith(meshNames[iMesh], "Collision") || dStrStartsWith(meshNames[iMesh], "LOSCol") ) { if (mesh->detailSize > 0) mesh->detailSize = -mesh->detailSize; } } // An 'object' is a collection of meshes with the same base name and // different detail sizes. The object is attached to the node of the // highest detail mesh. // Sort the 3 arrays (objMeshes, objNodes, meshNames) by name and size for (S32 i = 0; i < subshape->objMeshes.size()-1; i++) { for (S32 j = i+1; j < subshape->objMeshes.size(); j++) { if ((meshNames[i].compare(meshNames[j]) < 0) || ((meshNames[i].compare(meshNames[j]) == 0) && (subshape->objMeshes[i]->detailSize < subshape->objMeshes[j]->detailSize))) { { AppMesh* tmp = subshape->objMeshes[i]; subshape->objMeshes[i] = subshape->objMeshes[j]; subshape->objMeshes[j] = tmp; } { S32 tmp = subshape->objNodes[i]; subshape->objNodes[i] = subshape->objNodes[j]; subshape->objNodes[j] = tmp; } { String tmp = meshNames[i]; meshNames[i] = meshNames[j]; meshNames[j] = tmp; } } } } // Now create objects const String* lastName = 0; for (S32 iMesh = 0; iMesh < subshape->objMeshes.size(); iMesh++) { AppMesh* mesh = subshape->objMeshes[iMesh]; if (!lastName || (meshNames[iMesh] != *lastName)) { shape->objects.increment(); shape->objects.last().nameIndex = shape->addName(meshNames[iMesh]); shape->objects.last().nodeIndex = subshape->objNodes[iMesh]; shape->objects.last().startMeshIndex = appMeshes.size(); shape->objects.last().numMeshes = 0; lastName = &meshNames[iMesh]; } // Add this mesh to the object appMeshes.push_back(mesh); shape->objects.last().numMeshes++; // Set mesh flags mesh->flags = 0; if (mesh->isBillboard()) { mesh->flags |= TSMesh::Billboard; if (mesh->isBillboardZAxis()) mesh->flags |= TSMesh::BillboardZAxis; } // Set the detail name... do fixups for collision details. const char* detailName = "detail"; if ( mesh->detailSize < 0 ) { if ( dStrStartsWith(meshNames[iMesh], "Collision") || dStrStartsWith(meshNames[iMesh], "Col") ) detailName = "Collision"; else if (dStrStartsWith(meshNames[iMesh], "LOSCol")) detailName = "LOS"; } // Attempt to add the detail (will fail if it already exists) S32 oldNumDetails = shape->details.size(); shape->addDetail(detailName, mesh->detailSize, iSub); if (shape->details.size() > oldNumDetails) { Con::warnf("Object mesh \"%s\" has no matching detail (\"%s%d\" has" " been added automatically)", mesh->getName(false), detailName, mesh->detailSize); } } // Get object count for this subshape shape->subShapeNumObjects.push_back(shape->objects.size() - shape->subShapeFirstObject.last()); } }
bool AppSceneEnum::processNode(AppNode * node) { // Helper method to help rot nodes that we find in the scene. // At this stage we do not need to collect all the nodes // because the tree structure will still be there when we // build the shape. What we need to do right now is grab // the top of all the subtrees, any meshes hanging on the // root level (these will be lower detail levels, we don't // need to grab meshes on the sub-trees because they will // be found when we recurse into the sub-tree), the bounds // node, and any sequences. const char * name = node->getName(); const char * pname = node->getParentName(); AppConfig::PrintDump(PDPass1,avar("Processing Node %s with parent %s\r\n", name, pname)); AppSequence * seq = getSequence(node); if (seq) { sequences.push_back(seq); return true; } if (node->isDummy()) return false; if (isSubtree(node)) { // Add this node to the subtree list... AppConfig::PrintDump(PDPass1,avar("Found subtree starting at Node \"%s\"\r\n",name)); subtrees.push_back(node); return true; } // See if it is a bounding box. If so, save it as THE bounding // box for the scene if (node->isBounds()) { if (boundsNode) { setExportError("More than one bounds node found."); AppConfig::PrintDump(PDPass1,"More than one bounds node found.\r\n"); } else AppConfig::PrintDump(PDPass1,"Bounding box found\r\n"); boundsNode = node; return true; } // if we use this node, then be sure to return true so the caller doesn't delete it bool used = false; if (node->getNumMesh()!=0) { for (S32 i=0; i<node->getNumMesh(); i++) { AppMesh * mesh = node->getMesh(i); if (mesh->isSkin()) { AppConfig::PrintDump(PDPass1,avar("Skin \"%s\" with parent \"%s\" added to entry list\r\n",mesh->getName(),pname)); skins.push_back(mesh); used = true; } else { if (node->isParentRoot()) { AppConfig::PrintDump(PDPass1,avar("Mesh \"%s\" with parent \"%s\" added to entry list\r\n",mesh->getName(),pname)); meshNodes.push_back(node); meshes.push_back(mesh); used = true; } } } if (used) usedNodes.push_back(node); } return used; }