void TSShapeLoader::generateSkins() { Vector<AppMesh*> skins; for (int iObject = 0; iObject < shape->objects.size(); iObject++) { for (int iMesh = 0; iMesh < shape->objects[iObject].numMeshes; iMesh++) { AppMesh* mesh = appMeshes[shape->objects[iObject].startMeshIndex + iMesh]; if (mesh->isSkin()) skins.push_back(mesh); } } for (int iSkin = 0; iSkin < skins.size(); iSkin++) { updateProgress(Load_GenerateSkins, "Generating skins...", skins.size(), iSkin); // Get skin data (bones, vertex weights etc) AppMesh* skin = skins[iSkin]; skin->lookupSkinData(); // Just copy initial verts and norms for now skin->initialVerts.set(skin->points.address(), skin->vertsPerFrame); skin->initialNorms.set(skin->normals.address(), skin->vertsPerFrame); // Map bones to nodes skin->nodeIndex.setSize(skin->bones.size()); for (int iBone = 0; iBone < skin->bones.size(); iBone++) { // Find the node that matches this bone skin->nodeIndex[iBone] = -1; for (int iNode = 0; iNode < appNodes.size(); iNode++) { if (appNodes[iNode]->isEqual(skin->bones[iBone])) { delete skin->bones[iBone]; skin->bones[iBone] = appNodes[iNode]; skin->nodeIndex[iBone] = iNode; break; } } if (skin->nodeIndex[iBone] == -1) { Con::warnf("Could not find bone %d. Defaulting to first node", iBone); skin->nodeIndex[iBone] = 0; } } } }
void TSShapeLoader::generateDefaultStates() { // Generate default object states (includes initial geometry) for (int iObject = 0; iObject < shape->objects.size(); iObject++) { updateProgress(Load_GenerateDefaultStates, "Generating initial mesh and node states...", shape->objects.size(), iObject); TSShape::Object& obj = shape->objects[iObject]; // Calculate the objectOffset for each mesh at T=0 for (int iMesh = 0; iMesh < obj.numMeshes; iMesh++) { AppMesh* appMesh = appMeshes[obj.startMeshIndex + iMesh]; AppNode* appNode = obj.nodeIndex >= 0 ? appNodes[obj.nodeIndex] : boundsNode; MatrixF meshMat(appMesh->getMeshTransform(DefaultTime)); MatrixF nodeMat(appMesh->isSkin() ? meshMat : appNode->getNodeTransform(DefaultTime)); zapScale(nodeMat); appMesh->objectOffset = nodeMat.inverse() * meshMat; } generateObjectState(shape->objects[iObject], DefaultTime, true, true); } // Generate default node transforms for (int iNode = 0; iNode < appNodes.size(); iNode++) { // Determine the default translation and rotation for the node QuatF rot, srot; Point3F trans, scale; generateNodeTransform(appNodes[iNode], DefaultTime, false, 0, rot, trans, srot, scale); // Add default node translation and rotation addNodeRotation(rot, true); addNodeTranslation(trans, true); } }
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); } }
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; }