osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif) { mShape = new Resource::BulletShape; mCompoundShape.reset(); mStaticMesh.reset(); mAvoidStaticMesh.reset(); if (nif.numRoots() < 1) { warn("Found no root nodes in NIF."); return mShape; } Nif::Record *r = nif.getRoot(0); assert(r != nullptr); Nif::Node *node = dynamic_cast<Nif::Node*>(r); if (node == nullptr) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); return mShape; } if (findBoundingBox(node)) { std::unique_ptr<btCompoundShape> compound (new btCompoundShape); std::unique_ptr<btBoxShape> boxShape(new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents))); btTransform transform = btTransform::getIdentity(); transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); compound->addChildShape(transform, boxShape.get()); boxShape.release(); mShape->mCollisionShape = compound.release(); return mShape; } else { bool autogenerated = hasAutoGeneratedCollision(node); // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated const auto filename = nif.getFilename(); const bool isAnimated = pathFileNameStartsWithX(filename); handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated); if (mCompoundShape) { if (mStaticMesh) { btTransform trans; trans.setIdentity(); std::unique_ptr<btCollisionShape> child(new Resource::TriangleMeshShape(mStaticMesh.get(), true)); mCompoundShape->addChildShape(trans, child.get()); child.release(); mStaticMesh.release(); } mShape->mCollisionShape = mCompoundShape.release(); } else if (mStaticMesh) { mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true); mStaticMesh.release(); } if (mAvoidStaticMesh) { mShape->mAvoidCollisionShape = new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false); mAvoidStaticMesh.release(); } return mShape; } }
osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::NIFFilePtr& nif) { mShape = new Resource::BulletShape; mCompoundShape = NULL; mStaticMesh = NULL; if (nif->numRoots() < 1) { warn("Found no root nodes in NIF."); return mShape; } Nif::Record *r = nif->getRoot(0); assert(r != NULL); Nif::Node *node = dynamic_cast<Nif::Node*>(r); if (node == NULL) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); return mShape; } if (findBoundingBox(node)) { std::unique_ptr<btCompoundShape> compound (new btCompoundShape); btBoxShape* boxShape = new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents)); btTransform transform = btTransform::getIdentity(); transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); compound->addChildShape(transform, boxShape); mShape->mCollisionShape = compound.release(); return mShape; } else { bool autogenerated = hasAutoGeneratedCollision(node); bool isAnimated = false; // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated std::string filename = nif->getFilename(); size_t slashpos = filename.find_last_of("/\\"); if (slashpos == std::string::npos) slashpos = 0; if (slashpos+1 < filename.size() && (filename[slashpos+1] == 'x' || filename[slashpos+1] == 'X')) { isAnimated = true; } handleNode(node, 0, autogenerated, isAnimated, autogenerated); if (mCompoundShape) { mShape->mCollisionShape = mCompoundShape; if (mStaticMesh) { btTransform trans; trans.setIdentity(); mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh,true)); } } else if (mStaticMesh) mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh,true); return mShape; } }
void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, bool hasCollisionNode, bool isCollisionNode, bool raycastingOnly) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); // Marker objects: no collision /// \todo don't do this in the editor if (node->name.find("marker") != std::string::npos) { flags |= 0x800; cShape->mIgnore = true; } // Check for extra data Nif::Extra const *e = node; while (!e->extra.empty()) { // Get the next extra data in the list e = e->extra.getPtr(); assert(e != NULL); if (e->recType == Nif::RC_NiStringExtraData) { // String markers may contain important information // affecting the entire subtree of this node Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; // not sure what the difference between NCO and NCC is, or if there even is one if (sd->string == "NCO" || sd->string == "NCC") { // No collision. Use an internal flag setting to mark this. flags |= 0x800; } else if (sd->string == "MRK" && !raycastingOnly) // Marker objects. These are only visible in the // editor. Until and unless we add an editor component to // the engine, just skip this entire node. return; } } if(!hasCollisionNode || isCollisionNode) { if(node->hasBounds) { cShape->boxTranslation = node->boundPos; cShape->boxRotation = node->boundRot; mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); } if(node->recType == Nif::RC_NiTriShape) { cShape->mCollide = !(flags&0x800); handleNiTriShape(static_cast<const Nif::NiTriShape*>(node), flags, node->getWorldTransform(), raycastingOnly); } } // For NiNodes, loop through children const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); if(ninode) { const Nif::NodeList &list = ninode->children; for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) handleNode(list[i].getPtr(), flags, hasCollisionNode, isCollisionNode, raycastingOnly); } } }
void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) { assert(shape != NULL); // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. if ((flags & 0x800)) { return; } if (!shape->skin.empty()) isAnimated = false; if (shape->data.empty()) return; if (shape->data->triangles.empty()) return; if (isAnimated) { if (!mCompoundShape) mCompoundShape = new btCompoundShape(); btTriangleMesh* childMesh = new btTriangleMesh(); const Nif::NiTriShapeData *data = shape->data.getPtr(); childMesh->preallocateVertices(data->vertices.size()); childMesh->preallocateIndices(data->triangles.size()); const std::vector<osg::Vec3f> &vertices = data->vertices; const std::vector<unsigned short> &triangles = data->triangles; for(size_t i = 0;i < data->triangles.size();i+=3) { osg::Vec3f b1 = vertices[triangles[i+0]]; osg::Vec3f b2 = vertices[triangles[i+1]]; osg::Vec3f b3 = vertices[triangles[i+2]]; childMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } Resource::TriangleMeshShape* childShape = new Resource::TriangleMeshShape(childMesh,true); float scale = shape->trafo.scale; const Nif::Node* parent = shape; while (parent->parent) { parent = parent->parent; scale *= parent->trafo.scale; } osg::Quat q = transform.getRotate(); osg::Vec3f v = transform.getTrans(); childShape->setLocalScaling(btVector3(scale, scale, scale)); btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); mShape->mAnimatedShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); mCompoundShape->addChildShape(trans, childShape); } else { if (!mStaticMesh) mStaticMesh = new btTriangleMesh(false); // Static shape, just transform all vertices into position const Nif::NiTriShapeData *data = shape->data.getPtr(); const std::vector<osg::Vec3f> &vertices = data->vertices; const std::vector<unsigned short> &triangles = data->triangles; mStaticMesh->preallocateVertices(data->vertices.size()); mStaticMesh->preallocateIndices(data->triangles.size()); size_t numtris = data->triangles.size(); for(size_t i = 0;i < numtris;i+=3) { osg::Vec3f b1 = vertices[triangles[i+0]]*transform; osg::Vec3f b2 = vertices[triangles[i+1]]*transform; osg::Vec3f b3 = vertices[triangles[i+2]]*transform; mStaticMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } } }