mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-25 20:56:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			410 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "mesh.hpp"
 | |
| 
 | |
| #include <limits>
 | |
| 
 | |
| #include <OgreMeshManager.h>
 | |
| #include <OgreMesh.h>
 | |
| #include <OgreSubMesh.h>
 | |
| #include <OgreBone.h>
 | |
| #include <OgreHardwareBufferManager.h>
 | |
| #include <OgreMaterialManager.h>
 | |
| #include <OgreSkeletonManager.h>
 | |
| #include <OgreRenderSystem.h>
 | |
| #include <OgreRoot.h>
 | |
| #include <OgreSkeleton.h>
 | |
| #include <OgreKeyFrame.h>
 | |
| 
 | |
| #include <components/nif/node.hpp>
 | |
| #include <components/misc/stringops.hpp>
 | |
| 
 | |
| #include "material.hpp"
 | |
| 
 | |
| namespace NifOgre
 | |
| {
 | |
| 
 | |
| // Helper class that computes the bounding box and of a mesh
 | |
| class BoundsFinder
 | |
| {
 | |
|     struct MaxMinFinder
 | |
|     {
 | |
|         float max, min;
 | |
| 
 | |
|         MaxMinFinder()
 | |
|         {
 | |
|             min = std::numeric_limits<float>::infinity();
 | |
|             max = -min;
 | |
|         }
 | |
| 
 | |
|         void add(float f)
 | |
|         {
 | |
|             if (f > max) max = f;
 | |
|             if (f < min) min = f;
 | |
|         }
 | |
| 
 | |
|         // Return Max(max**2, min**2)
 | |
|         float getMaxSquared()
 | |
|         {
 | |
|             float m1 = max*max;
 | |
|             float m2 = min*min;
 | |
|             if (m1 >= m2) return m1;
 | |
|             return m2;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     MaxMinFinder X, Y, Z;
 | |
| 
 | |
| public:
 | |
|     // Add 'verts' vertices to the calculation. The 'data' pointer is
 | |
|     // expected to point to 3*verts floats representing x,y,z for each
 | |
|     // point.
 | |
|     void add(float *data, int verts)
 | |
|     {
 | |
|         for (int i=0;i<verts;i++)
 | |
|         {
 | |
|             X.add(*(data++));
 | |
|             Y.add(*(data++));
 | |
|             Z.add(*(data++));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // True if this structure has valid values
 | |
|     bool isValid()
 | |
|     {
 | |
|         return
 | |
|             minX() <= maxX() &&
 | |
|             minY() <= maxY() &&
 | |
|             minZ() <= maxZ();
 | |
|     }
 | |
| 
 | |
|     // Compute radius
 | |
|     float getRadius()
 | |
|     {
 | |
|         assert(isValid());
 | |
| 
 | |
|         // The radius is computed from the origin, not from the geometric
 | |
|         // center of the mesh.
 | |
|         return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
 | |
|     }
 | |
| 
 | |
|     float minX() {
 | |
|         return X.min;
 | |
|     }
 | |
|     float maxX() {
 | |
|         return X.max;
 | |
|     }
 | |
|     float minY() {
 | |
|         return Y.min;
 | |
|     }
 | |
|     float maxY() {
 | |
|         return Y.max;
 | |
|     }
 | |
|     float minZ() {
 | |
|         return Z.min;
 | |
|     }
 | |
|     float maxZ() {
 | |
|         return Z.max;
 | |
|     }
 | |
| };
 | |
| 
 | |
| 
 | |
| NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
 | |
| 
 | |
| 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;
 | |
| 
 | |
|     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);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     if(!shape->controller.empty())
 | |
|     {
 | |
|         Nif::ControllerPtr ctrl = shape->controller;
 | |
|         do {
 | |
|             // Load GeomMorpherController into an Ogre::Pose and Animation
 | |
|             if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active)
 | |
|             {
 | |
|                 const Nif::NiGeomMorpherController *geom =
 | |
|                         static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
 | |
| 
 | |
|                 const std::vector<Nif::NiMorphData::MorphData>& morphs = geom->data.getPtr()->mMorphs;
 | |
|                 // Note we are not interested in morph 0, which just contains the original vertices
 | |
|                 for (unsigned int i = 1; i < morphs.size(); ++i)
 | |
|                 {
 | |
|                     Ogre::Pose* pose = mesh->createPose(i);
 | |
|                     const Nif::NiMorphData::MorphData& data = morphs[i];
 | |
|                     for (unsigned int v = 0; v < data.mVertices.size(); ++v)
 | |
|                         pose->addVertex(v, data.mVertices[v]);
 | |
| 
 | |
|                     Ogre::String animationID = Ogre::StringConverter::toString(ctrl->recIndex)
 | |
|                             + "_" + Ogre::StringConverter::toString(i);
 | |
|                     Ogre::VertexAnimationTrack* track =
 | |
|                             mesh->createAnimation(animationID, 0)
 | |
|                             ->createVertexTrack(1, Ogre::VAT_POSE);
 | |
|                     Ogre::VertexPoseKeyFrame* keyframe = track->createVertexPoseKeyFrame(0);
 | |
|                     keyframe->addPoseReference(i-1, 1);
 | |
|                 }
 | |
| 
 | |
|                 break;
 | |
|             }
 | |
|         } while(!(ctrl=ctrl->next).empty());
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| NIFMeshLoader::NIFMeshLoader(const std::string &name, const std::string &group, size_t idx)
 | |
|   : mName(name), mGroup(group), mShapeIndex(idx)
 | |
| {
 | |
| }
 | |
| 
 | |
| void NIFMeshLoader::loadResource(Ogre::Resource *resource)
 | |
| {
 | |
|     Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
 | |
|     OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!");
 | |
| 
 | |
|     Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName);
 | |
|     if(mShapeIndex >= nif->numRecords())
 | |
|     {
 | |
|         Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
 | |
|         if(!skelMgr->getByName(mName).isNull())
 | |
|             mesh->setSkeletonName(mName);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const Nif::Record *record = nif->getRecord(mShapeIndex);
 | |
|     createSubMesh(mesh, dynamic_cast<const Nif::NiTriShape*>(record));
 | |
| }
 | |
| 
 | |
| 
 | |
| void NIFMeshLoader::createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx)
 | |
| {
 | |
|     NIFMeshLoader::LoaderMap::iterator loader;
 | |
|     loader = sLoaders.insert(std::make_pair(fullname, NIFMeshLoader(name, group, idx))).first;
 | |
| 
 | |
|     Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
 | |
|     Ogre::MeshPtr mesh = meshMgr.createManual(fullname, group, &loader->second);
 | |
|     mesh->setAutoBuildEdgeLists(false);
 | |
| }
 | |
| 
 | |
| }
 |