static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds) { assert(shape != NULL); // Interpret flags bool hidden = (flags & 0x01) != 0; // Not displayed bool collide = (flags & 0x02) != 0; // Use mesh for collision bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision // Bounding box collision isn't implemented, always use mesh for now. if(bbcollide) { collide = true; bbcollide = false; } // If the object was marked "NCO" earlier, it shouldn't collide with // anything. if(flags & 0x800) { collide = false; bbcollide = false; } if(!collide && !bbcollide && hidden) // This mesh apparently isn't being used for anything, so don't // bother setting it up. return; // Material name for this submesh, if any String material; // Skip the entire material phase for hidden nodes if(!hidden) { // These are set below if present NiTexturingProperty *t = NULL; NiMaterialProperty *m = NULL; NiAlphaProperty *a = NULL; // Scan the property list for material information PropertyList &list = shape->props; int n = list.length(); for(int i=0; i<n; i++) { // Entries may be empty if(!list.has(i)) continue; Property *pr = &list[i]; if(pr->recType == RC_NiTexturingProperty) t = (NiTexturingProperty*)pr; else if(pr->recType == RC_NiMaterialProperty) m = (NiMaterialProperty*)pr; else if(pr->recType == RC_NiAlphaProperty) a = (NiAlphaProperty*)pr; } // Texture String texName; if(t && t->textures[0].inUse) { NiSourceTexture *st = t->textures[0].texture.getPtr(); if(st->external) { SString tname = st->filename; /* findRealTexture checks if the file actually exists. If it doesn't, and the name ends in .tga, it will try replacing the extension with .dds instead and search for that. Bethesda at some at some point converted all their BSA textures from tga to dds for increased load speed, but all texture file name references were kept as .tga. The function replaces the name in place (that's why we cast away the const modifier), but this is no problem since all the nif data is stored in a local throwaway buffer. */ texName = "textures\\" + tname.toString(); findRealTexture(texName); } else warn("Found internal texture, ignoring."); } // Alpha modifiers int alphaFlags = -1; ubyte alphaTest = 0; if(a) { alphaFlags = a->flags; alphaTest = a->data->threshold; } // Material if(m || !texName.empty()) { // If we're here, then this mesh has a material. Thus we // need to calculate a snappy material name. It should // contain the mesh name (mesh->getName()) but also has to // be unique. One mesh may use many materials. material = getUniqueName(mesh->getName()); if(m) { // Use NiMaterialProperty data to create the data const S_MaterialProperty *d = m->data; createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, d->glossiness, d->alpha, alphaFlags, alphaTest, texName); } else { // We only have a texture name. Create a default // material for it. Vector zero, one; for(int i=0; i<3;i++) { zero.array[i] = 0.0; one.array[i] = 1.0; } createMaterial(material, one, one, zero, zero, 0.0, 1.0, alphaFlags, alphaTest, texName); } } } // End of material block, if(!hidden) ... /* Do in-place transformation of all the vertices and normals. This is pretty messy stuff, but we need it to make the sub-meshes appear in the correct place. Neither Ogre nor Bullet support nested levels of sub-meshes with transformations applied to each level. */ NiTriShapeData *data = shape->data.getPtr(); int numVerts = data->vertices.length / 3; float *ptr = (float*)data->vertices.ptr; float *optr = ptr; // Rotate, scale and translate all the vertices const Matrix &rot = shape->trafo->rotation; const Vector &pos = shape->trafo->pos; float scale = shape->trafo->scale; for(int i=0; i<numVerts; i++) { vectorMulAdd(rot, pos, ptr, scale); ptr += 3; } // Remember to rotate all the vertex normals as well if(data->normals.length) { ptr = (float*)data->normals.ptr; for(int i=0; i<numVerts; i++) { vectorMul(rot, ptr); ptr += 3; } } if(!hidden) { // Add this vertex set to the bounding box bounds.add(optr, numVerts); // Create the submesh createOgreMesh(mesh, shape, material); } }
void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape) { const Nif::NiTriShapeData *data = shape->data.getPtr(); const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); std::vector<Ogre::Vector3> srcVerts = data->vertices; std::vector<Ogre::Vector3> srcNorms = data->normals; Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; bool vertShadowBuffer = false; bool geomMorpherController = false; if(!shape->controller.empty()) { Nif::ControllerPtr ctrl = shape->controller; do { if(ctrl->recType == Nif::RC_NiGeomMorpherController) { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertShadowBuffer = true; geomMorpherController = true; break; } } while(!(ctrl=ctrl->next).empty()); } if(skin != NULL) { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertShadowBuffer = true; // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be // explicitly attached later. mesh->setSkeletonName(mName); // Convert vertices and normals to bone space from bind position. It would be // better to transform the bones into bind position, but there doesn't seem to // be a reliable way to do that. std::vector<Ogre::Vector3> newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); std::vector<Ogre::Vector3> newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t b = 0;b < bones.length();b++) { Ogre::Matrix4 mat; mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), Ogre::Quaternion(data->bones[b].trafo.rotation)); mat = bones[b]->getWorldTransform() * mat; const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights; for(size_t i = 0;i < weights.size();i++) { size_t index = weights[i].vertex; float weight = weights[i].weight; newVerts.at(index) += (mat*srcVerts[index]) * weight; if(newNorms.size() > index) { Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); vec4 = mat*vec4 * weight; newNorms[index] += Ogre::Vector3(&vec4[0]); } } } srcVerts = newVerts; srcNorms = newNorms; } else { Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); if(skelMgr->getByName(mName).isNull()) { // No skinning and no skeleton, so just transform the vertices and // normals into position. Ogre::Matrix4 mat4 = shape->getWorldTransform(); for(size_t i = 0;i < srcVerts.size();i++) { Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); vec4 = mat4*vec4; srcVerts[i] = Ogre::Vector3(&vec4[0]); } for(size_t i = 0;i < srcNorms.size();i++) { Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); vec4 = mat4*vec4; srcNorms[i] = Ogre::Vector3(&vec4[0]); } } } // Set the bounding box first BoundsFinder bounds; bounds.add(&srcVerts[0][0], srcVerts.size()); if(!bounds.isValid()) { float v[3] = { 0.0f, 0.0f, 0.0f }; bounds.add(&v[0], 1); } mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); mesh->_setBoundingSphereRadius(bounds.getRadius()); // This function is just one long stream of Ogre-barf, but it works // great. Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); Ogre::HardwareVertexBufferSharedPtr vbuf; Ogre::HardwareIndexBufferSharedPtr ibuf; Ogre::VertexBufferBinding *bind; Ogre::VertexDeclaration *decl; int nextBuf = 0; Ogre::SubMesh *sub = mesh->createSubMesh(); // Add vertices sub->useSharedVertices = false; sub->vertexData = new Ogre::VertexData(); sub->vertexData->vertexStart = 0; sub->vertexData->vertexCount = srcVerts.size(); decl = sub->vertexData->vertexDeclaration; bind = sub->vertexData->vertexBufferBinding; if(srcVerts.size()) { vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), srcVerts.size(), vertUsage, vertShadowBuffer); vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); bind->setBinding(nextBuf++, vbuf); } // Vertex normals if(srcNorms.size()) { vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), srcNorms.size(), vertUsage, vertShadowBuffer); vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); bind->setBinding(nextBuf++, vbuf); } // Vertex colors const std::vector<Ogre::Vector4> &colors = data->colors; if(colors.size()) { Ogre::RenderSystem *rs = Ogre::Root::getSingleton().getRenderSystem(); std::vector<Ogre::RGBA> colorsRGB(colors.size()); for(size_t i = 0;i < colorsRGB.size();i++) { Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); rs->convertColourValue(clr, &colorsRGB[i]); } vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC); vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); bind->setBinding(nextBuf++, vbuf); } // Texture UV coordinates size_t numUVs = data->uvlist.size(); if (numUVs) { size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); for(size_t i = 0; i < numUVs; i++) decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC); std::vector<Ogre::Vector2> allUVs; allUVs.reserve(srcVerts.size()*numUVs); for (size_t vert = 0; vert<srcVerts.size(); ++vert) for(size_t i = 0; i < numUVs; i++) allUVs.push_back(data->uvlist[i][vert]); vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true); bind->setBinding(nextBuf++, vbuf); } // Triangle faces const std::vector<short> &srcIdx = data->triangles; if(srcIdx.size()) { ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), Ogre::HardwareBuffer::HBU_STATIC); ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); sub->indexData->indexBuffer = ibuf; sub->indexData->indexCount = srcIdx.size(); sub->indexData->indexStart = 0; } // Assign bone weights for this TriShape if(skin != NULL) { Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) { Ogre::VertexBoneAssignment boneInf; boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights; for(size_t j = 0;j < weights.size();j++) { boneInf.vertexIndex = weights[j].vertex; boneInf.weight = weights[j].weight; sub->addBoneAssignment(boneInf); } } } const Nif::NiTexturingProperty *texprop = NULL; const Nif::NiMaterialProperty *matprop = NULL; const Nif::NiAlphaProperty *alphaprop = NULL; const Nif::NiVertexColorProperty *vertprop = NULL; const Nif::NiZBufferProperty *zprop = NULL; const Nif::NiSpecularProperty *specprop = NULL; const Nif::NiWireframeProperty *wireprop = NULL; bool needTangents = false; shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup, texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, needTangents); if(matname.length() > 0) sub->setMaterialName(matname); // build tangents if the material needs them if (needTangents) { unsigned short src,dest; if (!mesh->suggestTangentVectorBuildParams(Ogre::VES_TANGENT, src,dest)) mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); } // Create a dummy vertex animation track if there's a geom morpher controller // This is required to make Ogre create the buffers we will use for software vertex animation if (srcVerts.size() && geomMorpherController) mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH); }
void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bounds, Transformation original, std::vector<std::string> boneSequence) { assert(shape != NULL); bool saveTheShape = inTheSkeletonTree; // Interpret flags bool hidden = (flags & 0x01) != 0; // Not displayed bool collide = (flags & 0x02) != 0; // Use mesh for collision bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision // Bounding box collision isn't implemented, always use mesh for now. if (bbcollide) { collide = true; bbcollide = false; } // If the object was marked "NCO" earlier, it shouldn't collide with // anything. if (flags & 0x800) { collide = false; bbcollide = false; } if (!collide && !bbcollide && hidden) // This mesh apparently isn't being used for anything, so don't // bother setting it up. return; // Material name for this submesh, if any String material; // Skip the entire material phase for hidden nodes if (!hidden) { // These are set below if present NiTexturingProperty *t = NULL; NiMaterialProperty *m = NULL; NiAlphaProperty *a = NULL; // Scan the property list for material information PropertyList &list = shape->props; int n = list.length(); for (int i=0; i<n; i++) { // Entries may be empty if (!list.has(i)) continue; Property *pr = &list[i]; if (pr->recType == RC_NiTexturingProperty) t = (NiTexturingProperty*)pr; else if (pr->recType == RC_NiMaterialProperty) m = (NiMaterialProperty*)pr; else if (pr->recType == RC_NiAlphaProperty) a = (NiAlphaProperty*)pr; } // Texture String texName; if (t && t->textures[0].inUse) { NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { SString tname = st->filename; /* findRealTexture checks if the file actually exists. If it doesn't, and the name ends in .tga, it will try replacing the extension with .dds instead and search for that. Bethesda at some at some point converted all their BSA textures from tga to dds for increased load speed, but all texture file name references were kept as .tga. The function replaces the name in place (that's why we cast away the const modifier), but this is no problem since all the nif data is stored in a local throwaway buffer. */ texName = "textures\\" + tname.toString(); findRealTexture(texName); } else warn("Found internal texture, ignoring."); } // Alpha modifiers int alphaFlags = -1; ubyte alphaTest = 0; if (a) { alphaFlags = a->flags; alphaTest = a->data->threshold; } // Material if (m || !texName.empty()) { // If we're here, then this mesh has a material. Thus we // need to calculate a snappy material name. It should // contain the mesh name (mesh->getName()) but also has to // be unique. One mesh may use many materials. material = getUniqueName(mesh->getName()); if (m) { // Use NiMaterialProperty data to create the data const S_MaterialProperty *d = m->data; std::multimap<std::string,std::string>::iterator itr = MaterialMap.find(texName); std::multimap<std::string,std::string>::iterator lastElement; lastElement = MaterialMap.upper_bound(texName); if (itr != MaterialMap.end()) { for ( ; itr != lastElement; ++itr) { //std::cout << "OK!"; //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); material = itr->second; //if( mat->getA } } else { //std::cout << "new"; createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, d->glossiness, d->alpha, alphaFlags, alphaTest, texName); MaterialMap.insert(std::make_pair(texName,material)); } } else { // We only have a texture name. Create a default // material for it. Vector zero, one; for (int i=0; i<3;i++) { zero.array[i] = 0.0; one.array[i] = 1.0; } createMaterial(material, one, one, zero, zero, 0.0, 1.0, alphaFlags, alphaTest, texName); } } } // End of material block, if(!hidden) ... /* Do in-place transformation of all the vertices and normals. This is pretty messy stuff, but we need it to make the sub-meshes appear in the correct place. Neither Ogre nor Bullet support nested levels of sub-meshes with transformations applied to each level. */ NiTriShapeData *data = shape->data.getPtr(); int numVerts = data->vertices.length / 3; float *ptr = (float*)data->vertices.ptr; float *optr = ptr; std::list<VertexBoneAssignment> vertexBoneAssignments; Nif::NiTriShapeCopy copy = shape->clone(); if(!shape->controller.empty()) { Nif::Controller* cont = shape->controller.getPtr(); if(cont->recType == RC_NiGeomMorpherController) { Nif::NiGeomMorpherController* morph = dynamic_cast<Nif::NiGeomMorpherController*> (cont); copy.morph = morph->data.get(); copy.morph.setStartTime(morph->timeStart); copy.morph.setStopTime(morph->timeStop); saveTheShape = true; } } //use niskindata for the position of vertices. if (!shape->skin.empty()) { // vector that stores if the position of a vertex is absolute std::vector<bool> vertexPosAbsolut(numVerts,false); std::vector<Ogre::Vector3> vertexPosOriginal(numVerts, Ogre::Vector3::ZERO); std::vector<Ogre::Vector3> vertexNormalOriginal(numVerts, Ogre::Vector3::ZERO); float *ptrNormals = (float*)data->normals.ptr; //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] //the first one contains a link to the bone, the second vertex transformation //relative to the bone int boneIndex = 0; Bone *bonePtr; Vector3 vecPos; Quaternion vecRot; std::vector<NiSkinData::BoneInfo> boneList = shape->skin->data->bones; /* Iterate through the boneList which contains what vertices are linked to the bone (it->weights array) and at what position (it->trafo) That position is added to every vertex. */ for (std::vector<NiSkinData::BoneInfo>::iterator it = boneList.begin(); it != boneList.end(); it++) { if(mSkel.isNull()) { std::cout << "No skeleton for :" << shape->skin->bones[boneIndex].name.toString() << std::endl; break; } //get the bone from bones array of skindata if(!mSkel->hasBone(shape->skin->bones[boneIndex].name.toString())) std::cout << "We don't have this bone"; bonePtr = mSkel->getBone(shape->skin->bones[boneIndex].name.toString()); // final_vector = old_vector + old_rotation*new_vector*old_scale Nif::NiSkinData::BoneInfoCopy boneinfocopy; boneinfocopy.trafo.rotation = convertRotation(it->trafo->rotation); boneinfocopy.trafo.trans = convertVector3(it->trafo->trans); boneinfocopy.bonename = shape->skin->bones[boneIndex].name.toString(); boneinfocopy.bonehandle = bonePtr->getHandle(); copy.boneinfo.push_back(boneinfocopy); for (unsigned int i=0; i<it->weights.length; i++) { vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans); vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation); unsigned int verIndex = (it->weights.ptr + i)->vertex; //boneinfo.weights.push_back(*(it->weights.ptr + i)); Nif::NiSkinData::IndividualWeight ind; ind.weight = (it->weights.ptr + i)->weight; ind.boneinfocopyindex = copy.boneinfo.size() - 1; if(copy.vertsToWeights.find(verIndex) == copy.vertsToWeights.end()) { std::vector<Nif::NiSkinData::IndividualWeight> blank; blank.push_back(ind); copy.vertsToWeights[verIndex] = blank; } else { copy.vertsToWeights[verIndex].push_back(ind); } //Check if the vertex is relativ, FIXME: Is there a better solution? if (vertexPosAbsolut[verIndex] == false) { //apply transformation to the vertices Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3); absVertPos = absVertPos * (it->weights.ptr + i)->weight; vertexPosOriginal[verIndex] = Vector3(ptr + verIndex *3); mBoundingBox.merge(absVertPos); //convert it back to float * for (int j=0; j<3; j++) (ptr + verIndex*3)[j] = absVertPos[j]; //apply rotation to the normals (not every vertex has a normal) //FIXME: I guessed that vertex[i] = normal[i], is that true? if (verIndex < data->normals.length) { Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos * (it->weights.ptr + i)->weight; vertexNormalOriginal[verIndex] = Vector3(ptrNormals + verIndex *3); for (int j=0; j<3; j++) (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; } vertexPosAbsolut[verIndex] = true; } else { Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; absVertPos = absVertPos * (it->weights.ptr + i)->weight; Vector3 old = Vector3(ptr + verIndex *3); absVertPos = absVertPos + old; mBoundingBox.merge(absVertPos); //convert it back to float * for (int j=0; j<3; j++) (ptr + verIndex*3)[j] = absVertPos[j]; //apply rotation to the normals (not every vertex has a normal) //FIXME: I guessed that vertex[i] = normal[i], is that true? if (verIndex < data->normals.length) { Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; absNormalsPos = absNormalsPos * (it->weights.ptr + i)->weight; Vector3 oldNormal = Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos + oldNormal; for (int j=0; j<3; j++) (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; } } VertexBoneAssignment vba; vba.boneIndex = bonePtr->getHandle(); vba.vertexIndex = verIndex; vba.weight = (it->weights.ptr + i)->weight; vertexBoneAssignments.push_back(vba); } boneIndex++; } } else { copy.boneSequence = boneSequence; // Rotate, scale and translate all the vertices, const Matrix &rot = shape->trafo->rotation; const Vector &pos = shape->trafo->pos; float scale = shape->trafo->scale; copy.trafo.trans = convertVector3(original.pos); copy.trafo.rotation = convertRotation(original.rotation); copy.trafo.scale = original.scale; //We don't use velocity for anything yet, so it does not need to be saved // Computes C = B + AxC*scale for (int i=0; i<numVerts; i++) { vectorMulAdd(rot, pos, ptr, scale); Ogre::Vector3 absVertPos = Ogre::Vector3(*(ptr + 3 * i), *(ptr + 3 * i + 1), *(ptr + 3 * i + 2)); mBoundingBox.merge(absVertPos); ptr += 3; } // Remember to rotate all the vertex normals as well if (data->normals.length) { ptr = (float*)data->normals.ptr; for (int i=0; i<numVerts; i++) { vectorMul(rot, ptr); ptr += 3; } } if(!mSkel.isNull() ){ int boneIndex; boneIndex = mSkel->getNumBones() - 1; for(int i = 0; i < numVerts; i++){ VertexBoneAssignment vba; vba.boneIndex = boneIndex; vba.vertexIndex = i; vba.weight = 1; vertexBoneAssignments.push_back(vba); } } } if (!hidden) { // Add this vertex set to the bounding box bounds.add(optr, numVerts); if(saveTheShape) shapes.push_back(copy); // Create the submesh createOgreSubMesh(shape, material, vertexBoneAssignments); } }