Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <Ogre.h>
- #include <OgreMesh.h>
- #include <OgreAny.h>
- #include <OgreException.h>
- #include "nif_loader.h"
- #include <niflib.h>
- #include <obj/NiAVObject.h>
- #include <obj/NiObject.h>
- #include <obj/NiNode.h>
- #include <obj/NiTriShape.h>
- #include <obj/NiTriShapeData.h>
- #include <obj/NiStringExtraData.h>
- #include <obj/NiTextKeyExtraData.h>
- #include <obj/NiVertexColorProperty.h>
- #include <obj/NiTexturingProperty.h>
- #include <obj/NiMaterialProperty.h>
- #include <obj/NiZBufferProperty.h>
- #include <obj/NiAlphaProperty.h>
- #include <obj/NiSourceTexture.h>
- #include <obj/NiSkinInstance.h>
- #include <obj/NiSkinData.h>
- #include <obj/NiDynamicEffect.h>
- #include <obj/RootCollisionNode.h>
- #include <obj/NiSequenceStreamHelper.h>
- #include <obj/NiTimeController.h>
- #include <obj/NiKeyframeController.h>
- #include <obj/NiKeyframeData.h>
- /* An std::istream that reads from an Ogre::DataStream, for niflib */
- class InStream : public std::istream {
- class DataStreamBuf : public std::streambuf {
- Ogre::DataStreamPtr istream;
- char buffer[4096];
- virtual int_type underflow()
- {
- if(gptr() == egptr())
- {
- std::streamsize todo = sizeof(buffer);
- if((todo=istream->read(buffer, todo)) > 0)
- setg(buffer, buffer, buffer+todo);
- }
- if(gptr() == egptr())
- return traits_type::eof();
- return (*gptr())&0xFF;
- }
- virtual pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode)
- {
- if((mode&std::ios_base::out))
- return traits_type::eof();
- pos_type pos = -1;
- switch(whence)
- {
- case std::ios_base::beg:
- istream->seek(offset);
- pos = istream->tell();
- break;
- case std::ios_base::cur:
- offset -= off_type(egptr()-gptr());
- istream->skip(offset);
- pos = istream->tell();
- break;
- case std::ios_base::end:
- if(offset <= 0)
- {
- istream->seek(istream->size()+offset);
- pos = istream->tell();
- }
- break;
- default:
- pos = traits_type::eof();
- }
- if(pos >= 0)
- setg(0, 0, 0);
- return pos;
- }
- virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
- {
- if(pos != pos_type(off_type(pos)))
- return traits_type::eof();
- return seekoff(off_type(pos), std::ios_base::beg, mode);
- }
- public:
- DataStreamBuf(Ogre::DataStreamPtr stream)
- : istream(stream)
- { }
- virtual ~DataStreamBuf()
- { }
- };
- public:
- InStream(Ogre::DataStreamPtr stream)
- : std::istream(new DataStreamBuf(stream))
- { }
- virtual ~InStream()
- { delete rdbuf(); }
- };
- /* Easy niflib to Ogre type converters */
- static inline Ogre::Quaternion ConvertQuaternion(const Niflib::Quaternion &rhs)
- {
- return Ogre::Quaternion(rhs.w, rhs.x, rhs.y, rhs.z);
- }
- static inline Ogre::Vector3 ConvertVector3(const Niflib::Vector3 &rhs)
- {
- return Ogre::Vector3(rhs.x, rhs.y, rhs.z);
- }
- using namespace Niflib;
- static Ogre::SceneBlendFactor getBlendFactor(NiAlphaProperty::BlendFunc func)
- {
- switch(func)
- {
- case NiAlphaProperty::BF_ONE:
- return Ogre::SBF_ONE;
- case NiAlphaProperty::BF_ZERO:
- return Ogre::SBF_ZERO;
- case NiAlphaProperty::BF_SRC_COLOR:
- return Ogre::SBF_SOURCE_COLOUR;
- case NiAlphaProperty::BF_ONE_MINUS_SRC_COLOR:
- return Ogre::SBF_ONE_MINUS_SOURCE_COLOUR;
- case NiAlphaProperty::BF_DST_COLOR:
- return Ogre::SBF_DEST_COLOUR;
- case NiAlphaProperty::BF_ONE_MINUS_DST_COLOR:
- return Ogre::SBF_ONE_MINUS_DEST_COLOUR;
- case NiAlphaProperty::BF_SRC_ALPHA:
- return Ogre::SBF_SOURCE_ALPHA;
- case NiAlphaProperty::BF_ONE_MINUS_SRC_ALPHA:
- return Ogre::SBF_ONE_MINUS_SOURCE_ALPHA;
- case NiAlphaProperty::BF_DST_ALPHA:
- return Ogre::SBF_DEST_ALPHA;
- case NiAlphaProperty::BF_ONE_MINUS_DST_ALPHA:
- return Ogre::SBF_ONE_MINUS_DEST_ALPHA;
- case NiAlphaProperty::BF_SRC_ALPHA_SATURATE:
- // Can't handle this mode? :/
- //return Ogre::SBF_SOURCE_ALPHA_SATURATE;*/
- break;
- }
- Ogre::LogManager::getSingleton().stream() << "Unhandled blend function "<<func;
- return Ogre::SBF_SOURCE_ALPHA;
- }
- static Ogre::CompareFunction getTestMode(NiAlphaProperty::TestFunc func)
- {
- switch(func)
- {
- case NiAlphaProperty::TF_ALWAYS:
- return Ogre::CMPF_ALWAYS_PASS;
- case NiAlphaProperty::TF_LESS:
- return Ogre::CMPF_LESS;
- case NiAlphaProperty::TF_EQUAL:
- return Ogre::CMPF_EQUAL;
- case NiAlphaProperty::TF_LEQUAL:
- return Ogre::CMPF_LESS_EQUAL;
- case NiAlphaProperty::TF_GREATER:
- return Ogre::CMPF_GREATER;
- case NiAlphaProperty::TF_NOTEQUAL:
- return Ogre::CMPF_NOT_EQUAL;
- case NiAlphaProperty::TF_GEQUAL:
- return Ogre::CMPF_GREATER_EQUAL;
- case NiAlphaProperty::TF_NEVER:
- return Ogre::CMPF_ALWAYS_FAIL;
- }
- Ogre::LogManager::getSingleton().stream() << "Unhandled compare function "<<func;
- return Ogre::CMPF_ALWAYS_PASS;
- }
- static Ogre::CompareFunction getTestMode(ZCompareMode func)
- {
- switch(func)
- {
- case ZCOMP_ALWAYS:
- return Ogre::CMPF_ALWAYS_PASS;
- case ZCOMP_LESS:
- return Ogre::CMPF_LESS;
- case ZCOMP_EQUAL:
- return Ogre::CMPF_EQUAL;
- case ZCOMP_LESS_EQUAL:
- return Ogre::CMPF_LESS_EQUAL;
- case ZCOMP_GREATER:
- return Ogre::CMPF_GREATER;
- case ZCOMP_NOT_EQUAL:
- return Ogre::CMPF_NOT_EQUAL;
- case ZCOMP_GREATER_EQUAL:
- return Ogre::CMPF_GREATER_EQUAL;
- case ZCOMP_NEVER:
- return Ogre::CMPF_ALWAYS_FAIL;
- }
- Ogre::LogManager::getSingleton().stream() << "Unhandled compare function "<<func;
- return Ogre::CMPF_ALWAYS_PASS;
- }
- static Ogre::LayerBlendOperation getLayerBlend(ApplyMode mode)
- {
- switch(mode)
- {
- case APPLY_REPLACE:
- return Ogre::LBO_REPLACE;
- case APPLY_DECAL:
- return Ogre::LBO_ALPHA_BLEND;
- case APPLY_MODULATE:
- return Ogre::LBO_MODULATE;
- case APPLY_HILIGHT:
- case APPLY_HILIGHT2:
- /* How to? */
- break;
- }
- Ogre::LogManager::getSingleton().stream() << "Unhandled layer blend "<<mode;
- return Ogre::LBO_REPLACE;
- }
- static Ogre::TrackVertexColourType getVertColorTracking(VertMode mode)
- {
- switch(mode)
- {
- case VERT_MODE_SRC_IGNORE:
- return Ogre::TVC_NONE;
- case VERT_MODE_SRC_EMISSIVE:
- return Ogre::TVC_EMISSIVE;
- case VERT_MODE_SRC_AMB_DIF:
- return Ogre::TVC_AMBIENT | Ogre::TVC_DIFFUSE;
- }
- Ogre::LogManager::getSingleton().stream() << "Unhandled vertex color tacking " << mode;
- return Ogre::TVC_NONE;
- }
- static void createMaterial(const Ogre::String &name, const Ogre::String &groupName,
- NiTriShapeRef shape)
- {
- Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(name, "General");
- Ogre::Pass *pass = material->getTechnique(0)->getPass(0);
- // This assigns the textures to this material. If the texture name is
- // a file name, and this file exists (in a resource directory), it
- // will automatically be loaded when needed. If not (such as for
- // internal NIF textures that we might support later), we should
- // insert a manual loader for the texture.
- NiTexturingPropertyRef textures = DynamicCast<NiTexturingProperty>(shape->GetPropertyByType(NiTexturingProperty::TYPE));
- if(textures)
- {
- for(int i = 0;i < textures->GetTextureCount();i++)
- {
- if(!textures->HasTexture(i))
- continue;
- TexDesc desc = textures->GetTexture(i);
- NiSourceTextureRef st = desc.source;
- if(!st->IsTextureExternal())
- {
- Ogre::LogManager::getSingleton().logMessage("Found internal texture, ignoring.");
- continue;
- }
- /* This checks if the file actually exists. If it doesn't, 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.
- */
- Ogre::String tname = "textures/" + st->GetTextureFileName();
- if(!Ogre::ResourceGroupManager::getSingleton().resourceExists(groupName, tname))
- {
- // Change texture extension to .dds
- tname.replace(tname.rfind('.'), tname.length(), ".dds");
- }
- Ogre::TextureUnitState *txt = pass->createTextureUnitState(tname);
- if(i == 0) // diffuse texture
- txt->setColourOperation(getLayerBlend(textures->GetApplyMode()));
- else if(i == 1) // dark texture
- txt->setColourOperation(Ogre::LBO_MODULATE);
- else // skip remaining
- {
- txt->setColourOperationEx(Ogre::LBX_SOURCE1, Ogre::LBS_CURRENT);
- txt->setAlphaOperation(Ogre::LBX_SOURCE1, Ogre::LBS_CURRENT);
- }
- txt->setTextureCoordSet(desc.uvSet);
- if(desc.clampMode == CLAMP_S_CLAMP_T)
- txt->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP, Ogre::TextureUnitState::TAM_CLAMP,
- Ogre::TextureUnitState::TAM_CLAMP);
- else if(desc.clampMode == WRAP_S_CLAMP_T)
- txt->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP, Ogre::TextureUnitState::TAM_CLAMP,
- Ogre::TextureUnitState::TAM_CLAMP);
- else if(desc.clampMode == CLAMP_S_WRAP_T)
- txt->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP, Ogre::TextureUnitState::TAM_WRAP,
- Ogre::TextureUnitState::TAM_CLAMP);
- else if(desc.clampMode == WRAP_S_WRAP_T)
- txt->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP, Ogre::TextureUnitState::TAM_WRAP,
- Ogre::TextureUnitState::TAM_CLAMP);
- }
- }
- // Add vertex color tracking if NiVertexColorProperty is present
- NiVertexColorPropertyRef vtxc = DynamicCast<NiVertexColorProperty>(shape->GetPropertyByType(NiVertexColorProperty::TYPE));
- if(vtxc)
- {
- pass->setVertexColourTracking(getVertColorTracking(vtxc->GetVertexMode()));
- // FIXME: Handle vtxc->GetLightingMode()?
- }
- else
- {
- pass->setVertexColourTracking(Ogre::TVC_DIFFUSE);
- }
- // Add transparency if NiAlphaProperty is set to anything meaningful
- NiAlphaPropertyRef alpha = DynamicCast<NiAlphaProperty>(shape->GetPropertyByType(NiAlphaProperty::TYPE));
- if(alpha)
- {
- if(alpha->GetBlendState())
- pass->setSceneBlending(getBlendFactor(alpha->GetSourceBlendFunc()),
- getBlendFactor(alpha->GetDestBlendFunc()));
- if(alpha->GetTestState())
- pass->setAlphaRejectSettings(getTestMode(alpha->GetTestFunc()),
- alpha->GetTestThreshold());
- pass->setTransparentSortingEnabled(alpha->GetTriangleSortMode());
- }
- // Set z-buffer options
- NiZBufferPropertyRef zbuf = DynamicCast<NiZBufferProperty>(shape->GetPropertyByType(NiZBufferProperty::TYPE));
- if(zbuf)
- {
- pass->setDepthCheckEnabled(zbuf->GetFlags()&0x1);
- pass->setDepthWriteEnabled((zbuf->GetFlags()>>1)&0x1);
- pass->setDepthFunction(getTestMode(zbuf->GetDepthFunction()));
- }
- // Add material bells and whistles
- NiMaterialPropertyRef mat = DynamicCast<NiMaterialProperty>(shape->GetPropertyByType(NiMaterialProperty::TYPE));
- if(mat)
- {
- material->setAmbient(mat->GetAmbientColor().r, mat->GetAmbientColor().g, mat->GetAmbientColor().b);
- material->setDiffuse(mat->GetDiffuseColor().r, mat->GetDiffuseColor().g, mat->GetDiffuseColor().b,
- mat->GetTransparency());
- material->setSpecular(mat->GetSpecularColor().r, mat->GetSpecularColor().g, mat->GetSpecularColor().b,
- mat->GetTransparency());
- material->setSelfIllumination(mat->GetEmissiveColor().r, mat->GetEmissiveColor().g, mat->GetEmissiveColor().b);
- material->setShininess(mat->GetGlossiness());
- }
- }
- // Convert NiTriShape to Ogre::SubMesh
- static void handleNiTriShape(Ogre::Mesh *mesh, NiTriShapeRef shape, int /*flags*/)
- {
- // 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
- // This mesh apparently isn't being used for anything, so don't
- // bother setting it up.
- //if(!collide && !bbcollide && hidden)
- // return;
- // Skip the entire material/submesh phase for hidden nodes
- //if(hidden)
- // return;
- NiTriShapeDataRef data = DynamicCast<NiTriShapeData>(shape->GetData());
- NiSkinInstanceRef skin = shape->GetSkinInstance();
- std::vector<Vector3> srcVerts = data->GetVertices();
- std::vector<Vector3> srcNorms = data->GetNormals();
- if(skin != NULL)
- {
- // Convert vertices and normals back to bone space
- std::vector<Vector3> newVerts(srcVerts.size(), Vector3(0,0,0));
- std::vector<Vector3> newNorms(srcNorms.size(), Vector3(0,0,0));
- NiSkinDataRef data = skin->GetSkinData();
- const std::vector<NiNodeRef> &bones = skin->GetBones();
- for(size_t b = 0;b < bones.size();b++)
- {
- Matrix44 mat = data->GetBoneTransform(b) * bones[b]->GetWorldTransform();
- const std::vector<SkinWeight> &weights = data->GetBoneWeights(b);
- for(size_t i = 0;i < weights.size();i++)
- {
- size_t index = weights[i].index;
- float weight = weights[i].weight;
- newVerts.at(index) += (mat*srcVerts[index]) * weight;
- if(newNorms.size() > index)
- {
- for(size_t j = 0;j < 3;j++)
- {
- newNorms[index][j] += mat[j][0]*srcNorms[index][0] * weight;
- newNorms[index][j] += mat[j][1]*srcNorms[index][1] * weight;
- newNorms[index][j] += mat[j][2]*srcNorms[index][2] * weight;
- }
- }
- }
- }
- srcVerts = newVerts;
- srcNorms = newNorms;
- }
- else
- {
- /*// Since Ogre doesn't have any offsets for submeshes (like Nifs do for
- // trishape nodes), we need to manually apply them to the vertices.
- Matrix44 fullTrans = shape->GetWorldTransform() *
- parent->GetWorldTransform().Inverse();
- for(size_t i = 0;i < srcVerts.size();i++)
- srcVerts[i] = fullTrans * srcVerts[i];
- for(size_t i = 0;i < srcNorms.size();i++)
- {
- float norm[3] = { srcNorms[i].x, srcNorms[i].y, srcNorms[i].z };
- for(size_t j = 0;j < 3;j++)
- {
- srcNorms[i][j] = fullTrans[j][0] * norm[0];
- srcNorms[i][j] += fullTrans[j][1] * norm[1];
- srcNorms[i][j] += fullTrans[j][2] * norm[2];
- }
- }*/
- }
- Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr();
- Ogre::HardwareVertexBufferSharedPtr vbuf;
- Ogre::HardwareIndexBufferSharedPtr ibuf;
- Ogre::VertexBufferBinding *bind;
- Ogre::VertexDeclaration *decl;
- int nextBuf = 0;
- // This function is just one long stream of Ogre-barf, but it works
- // great.
- Ogre::SubMesh *sub = mesh->createSubMesh(shape->GetName());
- // Add vertices
- size_t numVerts = data->GetVertexCount();
- sub->useSharedVertices = false;
- sub->vertexData = new Ogre::VertexData();
- sub->vertexData->vertexStart = 0;
- sub->vertexData->vertexCount = numVerts;
- decl = sub->vertexData->vertexDeclaration;
- bind = sub->vertexData->vertexBufferBinding;
- if(numVerts)
- {
- std::vector<float> verts(numVerts*3);
- for(size_t i = 0;i < numVerts;i++)
- {
- verts[i*3 + 0] = srcVerts[i].x;
- verts[i*3 + 1] = srcVerts[i].y;
- verts[i*3 + 2] = srcVerts[i].z;
- }
- vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
- numVerts, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, true);
- vbuf->writeData(0, vbuf->getSizeInBytes(), verts.data(), true);
- decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
- bind->setBinding(nextBuf++, vbuf);
- }
- // Vertex normals
- if(data->GetHasNormals())
- {
- OgreAssert((numVerts==srcNorms.size()), "Normal count does not match vertex count");
- std::vector<float> norms(numVerts*3);
- for(size_t i = 0;i < numVerts;i++)
- {
- norms[i*3 + 0] = srcNorms[i].x;
- norms[i*3 + 1] = srcNorms[i].y;
- norms[i*3 + 2] = srcNorms[i].z;
- }
- vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
- numVerts, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, true);
- vbuf->writeData(0, vbuf->getSizeInBytes(), norms.data(), true);
- decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
- bind->setBinding(nextBuf++, vbuf);
- }
- // Vertex colors
- std::vector<Color4> colors = data->GetColors();
- if(colors.size())
- {
- OgreAssert((numVerts==colors.size()), "Color count does not match vertex count");
- Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem();
- std::vector<Ogre::RGBA> colorsRGB(numVerts);
- for(size_t i = 0;i < numVerts;i++)
- {
- Ogre::ColourValue clr(colors[i].r, colors[i].g, colors[i].b, colors[i].a);
- rs->convertColourValue(clr, &colorsRGB[i]);
- }
- vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
- numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true);
- vbuf->writeData(0, vbuf->getSizeInBytes(), colorsRGB.data(), true);
- decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
- bind->setBinding(nextBuf++, vbuf);
- }
- colors.clear();
- // Texture UV coordinates
- size_t numUVs = data->GetUVSetCount();
- if(numUVs)
- {
- size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
- vbuf = hwBufMgr->createVertexBuffer(elemSize, numVerts*numUVs,
- Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true);
- for(size_t i = 0;i < numUVs;i++)
- {
- std::vector<float> uvs(numVerts*2);
- const std::vector<TexCoord> &uvlist = data->GetUVSet(i);
- OgreAssert((numVerts==uvlist.size()), "UV list count does not match vertex count");
- for(size_t j = 0;j < numVerts;j++)
- {
- uvs[j*2 + 0] = uvlist[j].u;
- uvs[j*2 + 1] = uvlist[j].v;
- }
- vbuf->writeData(i*numVerts*elemSize, elemSize*numVerts, uvs.data(), true);
- decl->addElement(nextBuf, i*numVerts*elemSize, Ogre::VET_FLOAT2,
- Ogre::VES_TEXTURE_COORDINATES, i);
- }
- bind->setBinding(nextBuf++, vbuf);
- }
- // Triangle faces
- std::vector<Triangle> srcIdx = data->GetTriangles();
- if(srcIdx.size())
- {
- size_t numIdx = srcIdx.size()*3;
- std::vector<uint16_t> idxs(numIdx);
- for(uint32_t i = 0;i < srcIdx.size();i++)
- {
- idxs[i*3 + 0] = srcIdx[i].v1;
- idxs[i*3 + 1] = srcIdx[i].v2;
- idxs[i*3 + 2] = srcIdx[i].v3;
- }
- ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, numIdx,
- Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
- ibuf->writeData(0, ibuf->getSizeInBytes(), idxs.data(), true);
- sub->indexData->indexBuffer = ibuf;
- sub->indexData->indexCount = numIdx;
- sub->indexData->indexStart = 0;
- }
- srcIdx.clear();
- // Assign bone weights for this TriShape
- if(skin != NULL)
- {
- // Get the skeleton resource, so weights can be applied
- Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
- Ogre::SkeletonPtr skel = skelMgr->getByName(mesh->getSkeletonName());
- NiSkinDataRef data = skin->GetSkinData();
- const std::vector<NiNodeRef> &bones = skin->GetBones();
- for(size_t i = 0;i < bones.size();i++)
- {
- Ogre::VertexBoneAssignment boneInf;
- boneInf.boneIndex = skel->getBone(bones[i]->GetName())->getHandle();
- const std::vector<SkinWeight> &weights = data->GetBoneWeights(i);
- for(size_t j = 0;j < weights.size();j++)
- {
- boneInf.vertexIndex = weights[j].index;
- boneInf.weight = weights[j].weight;
- sub->addBoneAssignment(boneInf);
- }
- }
- }
- // Use the name of the mesh for the material. The mesh name is already
- // unique enough since there's only one NiTriShape/SubMesh, thus only one
- // material.
- const Ogre::String &material = mesh->getName();
- createMaterial(material, mesh->getGroup(), shape);
- sub->setMaterialName(material);
- }
- static bool findNiTriShape(Ogre::Mesh *mesh, const Ogre::String &target, NiAVObjectRef obj, int flags=0)
- {
- flags |= obj->GetFlags();
- // For trishapes, build the appropriate submesh, material, and junk
- if(obj->IsSameType(NiTriShape::TYPE) && target == obj->GetName())
- {
- handleNiTriShape(mesh, StaticCast<NiTriShape>(obj), flags);
- return true;
- }
- if(obj->IsDerivedType(NiNode::TYPE))
- {
- // For NiNodes, loop through children
- NiNodeRef node = StaticCast<NiNode>(obj);
- std::vector<NiAVObjectRef> list = node->GetChildren();
- for(size_t i = 0;i < list.size();i++)
- {
- if(findNiTriShape(mesh, target, list[i], flags))
- return true;
- }
- }
- return false;
- }
- static void buildBones(Ogre::Skeleton *skel, NiNodeRef node, Ogre::Bone *bone = NULL)
- {
- //if(bone || node->IsSkeletonRoot())
- {
- Ogre::Bone *newbone = skel->createBone(node->GetName());
- if(!node->IsSkeletonRoot() && bone)
- bone->addChild(newbone);
- Vector3 srcTrans = node->GetLocalTranslation();
- Matrix33 srcRot = node->GetLocalRotation();
- float scale = node->GetLocalScale();
- Ogre::Vector3 trans(srcTrans.x, srcTrans.y, srcTrans.z);
- Ogre::Matrix3 rot(srcRot[0][0], srcRot[1][0], srcRot[2][0],
- srcRot[0][1], srcRot[1][1], srcRot[2][1],
- srcRot[0][2], srcRot[1][2], srcRot[2][2]);
- newbone->setOrientation(rot);
- newbone->setPosition(trans);
- newbone->setScale(Ogre::Vector3(scale));
- newbone->setBindingPose();
- newbone->setInitialState();
- bone = newbone;
- }
- std::vector<NiAVObjectRef> list = node->GetChildren();
- for(size_t i = 0;i < list.size();i++)
- {
- if(list[i]->IsDerivedType(NiNode::TYPE))
- buildBones(skel, StaticCast<NiNode>(list[i]), bone);
- }
- }
- struct NIFMeshLoader : public Ogre::ManualResourceLoader {
- void loadResource(Ogre::Resource *resource)
- {
- // Get the mesh and possible skeleton
- Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
- OgreAssert(mesh, "Request to load a mesh in a non-mesh resource");
- // Look it up
- const Ogre::String &name = mesh->getName();
- size_t atpos = name.rfind('@');
- const Ogre::String &basename = name.substr(0, atpos);
- Ogre::ResourceGroupManager &resMgr = Ogre::ResourceGroupManager::getSingleton();
- std::auto_ptr<InStream> stream(new InStream(resMgr.openResource(basename)));
- NiAVObjectRef root = DynamicCast<NiAVObject>(ReadNifTree(*stream.get()));
- stream.reset();
- // Handle the node
- findNiTriShape(mesh, name.substr(atpos+1), root);
- // Finally, set the bounding value. Just use bogus info right now.
- mesh->_setBounds(Ogre::AxisAlignedBox(-200,-200,-200,200,200,200));
- mesh->_setBoundingSphereRadius(250);
- }
- };
- static void createMeshes(const Ogre::String &fname, const Ogre::String &group, Ogre::MeshManager *meshMgr, std::vector<Ogre::MeshPtr> *meshes, Ogre::Skeleton *skel, std::map<Ogre::String,Ogre::String> *meshbones, NiAVObjectRef obj, NiNodeRef parent=NULL, int flags=0)
- {
- flags |= obj->GetFlags();
- // Check for extra data
- const std::list<NiExtraDataRef> &e = obj->GetExtraData();
- for(std::list<NiExtraDataRef>::const_iterator iter=e.begin();iter != e.end();iter++)
- {
- // Get the next extra data in the list
- Ogre::LogManager::getSingleton().stream() << "Extra data in "<<obj->GetName()<<" (type "<<(*iter)->GetType().GetTypeName()<<")";
- NiStringExtraDataRef sd;
- NiTextKeyExtraDataRef tk;
- if((sd=DynamicCast<NiStringExtraData>(*iter)) != NULL)
- {
- // String markers may contain important information
- // affecting the entire subtree of this obj
- if(sd->GetData() == "NCO")
- {
- // No collision. Clear the appropriate flags.
- flags &= ~(0x02|0x04);
- }
- else if(sd->GetData() == "MRK")
- {
- // Marker objects. These are only visible in the
- // editor. Until and unless we add an editor component to
- // the engine, just skip this entire branch.
- return;
- }
- else
- Ogre::LogManager::getSingleton().stream() << "Unknown extra data string \""<<sd->GetData()<<"\"";
- }
- else if((tk=DynamicCast<NiTextKeyExtraData>(*iter)) != NULL)
- {
- // Animation and soundgen information is apparently stored in an array
- // like this. The string key is specified as like "Idle: Start",
- // "SwimWalkLeft: Loop Stop", and "Soundgen: Land", and the float value
- // is the time in seconds along the animation track that corresponds
- // with the action. There can be more than one text key action per
- // pair, separated by a newline. Need to look into why some text keys
- // are empty, though, and some have an errant newline at the end. A
- // blank action may mean something, or it may be left-over junk.
- std::vector<Key<std::string> > keys = tk->GetKeys();
- for(size_t i = 0;i < keys.size();i++)
- {
- Ogre::LogManager::getSingleton().stream() << "\""<<keys[i].data<<"\" = "<<keys[i].time;
- }
- }
- else
- Ogre::LogManager::getSingleton().stream() << "Unhandled extra data type: "<<(*iter)->GetType().GetTypeName();
- }
- if(obj->IsSameType(NiTriShape::TYPE))
- {
- static NIFMeshLoader loader;
- Ogre::MeshPtr mesh;
- if(!(flags&0x01))
- {
- mesh = meshMgr->createManual(fname+"@"+obj->GetName(), group, &loader);
- if(skel && StaticCast<NiTriShape>(obj)->GetSkinInstance())
- mesh->setSkeletonName(skel->getName());
- (*meshbones)[mesh->getName()] = parent->GetName();
- meshes->push_back(mesh);
- }
- }
- else if(!obj->IsSameType(NiNode::TYPE))
- Ogre::LogManager::getSingleton().stream() << "Unhandled object type "<<obj->GetType().GetTypeName();
- if(obj->IsDerivedType(NiNode::TYPE))
- {
- // For NiNodes, loop through children
- NiNodeRef node = StaticCast<NiNode>(obj);
- std::vector<NiAVObjectRef> list = node->GetChildren();
- for(size_t i = 0;i < list.size();i++)
- createMeshes(fname, group, meshMgr, meshes, skel, meshbones, list[i], node, flags);
- }
- }
- struct NIFSkeletonLoader : public Ogre::ManualResourceLoader {
- void loadResource(Ogre::Resource *resource)
- {
- // Get the mesh and possible skeleton
- Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
- OgreAssert(skel, "Request to load a skeleton in a non-skeleton resource");
- const Ogre::String &name = skel->getName();
- Ogre::ResourceGroupManager &resMgr = Ogre::ResourceGroupManager::getSingleton();
- std::auto_ptr<InStream> stream(new InStream(resMgr.openResource(name)));
- NiNodeRef root = DynamicCast<NiNode>(ReadNifTree(*stream.get()));
- stream.reset();
- // Handle the node
- buildBones(skel, root);
- }
- };
- static bool hasRootBone(NiNodeRef node)
- {
- if(node->IsSkeletonRoot())
- return true;
- std::vector<NiAVObjectRef> list = node->GetChildren();
- size_t n = list.size();
- for(size_t i = 0;i < n;i++)
- {
- if(list[i]->IsDerivedType(NiNode::TYPE))
- {
- if(hasRootBone(StaticCast<NiNode>(list[i])))
- return true;
- }
- }
- return false;
- }
- void NIFObject::create(const Ogre::String &fname, const Ogre::String &group,
- std::vector<Ogre::MeshPtr> *meshes, Ogre::SkeletonPtr *skel,
- std::map<Ogre::String,Ogre::String> *meshbones)
- {
- Ogre::MeshManager *meshMgr = Ogre::MeshManager::getSingletonPtr();
- Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
- Ogre::ResourceGroupManager *resMgr = Ogre::ResourceGroupManager::getSingletonPtr();
- meshes->clear();
- skel->setNull();
- std::auto_ptr<InStream> stream(new InStream(resMgr->openResource(fname)));
- NiObjectRef root = ReadNifTree(*stream.get());
- stream.reset();
- if(root->IsDerivedType(NiNode::TYPE) && hasRootBone(StaticCast<NiNode>(root)))
- {
- if((*skel=skelMgr->getByName(fname)).isNull())
- {
- static NIFSkeletonLoader loader;
- *skel = skelMgr->create(fname, group, true, &loader);
- }
- }
- if(root->IsDerivedType(NiAVObject::TYPE))
- createMeshes(fname, group, meshMgr, meshes, skel->get(), meshbones, StaticCast<NiAVObject>(root));
- }
- /* Comparitor to help sort Key<> vectors */
- template<class T>
- struct KeyTimeSort
- {
- bool operator()(const Key<T> &lhs, const Key<T> &rhs) const
- { return lhs.time < rhs.time; }
- };
- void NIFObject::create(const Ogre::String &fname, Ogre::SkeletonPtr skel,
- Ogre::Animation **anim)
- {
- Ogre::ResourceGroupManager &resMgr = Ogre::ResourceGroupManager::getSingleton();
- std::auto_ptr<InStream> stream(new InStream(resMgr.openResource(fname)));
- NiObjectRef root = ReadNifTree(*stream.get());
- stream.reset();
- skel->load();
- // Get the animation
- if(root->IsSameType(NiSequenceStreamHelper::TYPE))
- {
- NiSequenceStreamHelperRef seq = StaticCast<NiSequenceStreamHelper>(root);
- std::vector< Key<std::string> > animkeys;
- std::vector<Ogre::String> targetnames;
- float maxtime = 0.0f;
- const std::list<NiExtraDataRef> &e = seq->GetExtraData();
- for(std::list<NiExtraDataRef>::const_iterator iter=e.begin();iter != e.end();iter++)
- {
- Ogre::LogManager::getSingleton().stream() << "Extra data in \""<<seq->GetName()<<"\" (type "<<(*iter)->GetType().GetTypeName()<<")";
- // Get the bone targets from the extra data
- NiStringExtraDataRef sd;
- NiTextKeyExtraDataRef tk;
- if((sd=DynamicCast<NiStringExtraData>(*iter)) != NULL)
- targetnames.push_back(sd->GetData());
- else if((tk=DynamicCast<NiTextKeyExtraData>(*iter)) != NULL)
- {
- animkeys = tk->GetKeys();
- std::sort(animkeys.begin(), animkeys.end(), KeyTimeSort<std::string>());
- for(size_t i = 0;i < animkeys.size();i++)
- Ogre::LogManager::getSingleton().stream() << "\""<<animkeys[i].data<<"\" = "<<animkeys[i].time;
- }
- else
- Ogre::LogManager::getSingleton().stream() << "Unhandled extra data type: "<<(*iter)->GetType().GetTypeName();
- }
- std::list<NiTimeControllerRef> ctrls = seq->GetControllers();
- if(!targetnames.empty() && ctrls.size() != targetnames.size())
- OGRE_EXCEPT(Ogre::Exception::ERR_RT_ASSERTION_FAILED, "Bad targetnames size", __PRETTY_FUNCTION__);
- std::list<NiTimeControllerRef>::const_iterator ctrl;
- size_t idx = 0;
- for(ctrl = ctrls.begin();ctrl != ctrls.end();ctrl++,idx++)
- {
- maxtime = std::max(maxtime, (*ctrl)->GetStopTime());
- /* Find the bone target this track affects (.kf animations appear to
- * store the targets using the bone names in the sequence's extra
- * data, while .nif animations use the controller's target ref). */
- NiObjectNETRef target = (*ctrl)->GetTarget();
- if(target)
- targetnames[idx] = target->GetName();
- }
- *anim = skel->createAnimation(seq->GetName(), maxtime);
- /* HACK: Pre-create the node tracks by matching the track IDs with the
- * bone IDs. Otherwise, Ogre animates the wrong bones. */
- size_t bonecount = skel->getNumBones();
- for(size_t i = 0;i < bonecount;i++)
- (*anim)->createNodeTrack(i, skel->getBone(i));
- #if 1
- idx = 0;
- for(ctrl = ctrls.begin();ctrl != ctrls.end();ctrl++,idx++)
- {
- if(!(*ctrl)->IsSameType(NiKeyframeController::TYPE))
- {
- std::cout <<"Unhandled controller: "<<(*ctrl)->GetType().GetTypeName()<< std::endl;
- continue;
- }
- NiKeyframeControllerRef kfc = StaticCast<NiKeyframeController>(*ctrl);
- NiKeyframeDataRef kf = kfc->GetData();
- kf->NormalizeKeys(kfc->GetPhase(), kfc->GetFrequency());
- /* Get the keyframes and make sure they're sorted first to last */
- std::vector< Key<Quaternion> > quatkeys = kf->GetQuatRotateKeys();
- std::vector< Key<Vector3> > trankeys = kf->GetTranslateKeys();
- std::vector< Key<float> > scalekeys = kf->GetScaleKeys();
- std::sort(quatkeys.begin(), quatkeys.end(), KeyTimeSort<Quaternion>());
- std::sort(trankeys.begin(), trankeys.end(), KeyTimeSort<Vector3>());
- std::sort(scalekeys.begin(), scalekeys.end(), KeyTimeSort<float>());
- std::vector< Key<Quaternion> >::const_iterator quatiter = quatkeys.begin();
- std::vector< Key<Vector3> >::const_iterator traniter = trankeys.begin();
- std::vector< Key<float> >::const_iterator scaleiter = scalekeys.begin();
- Ogre::Bone *bone = skel->getBone(targetnames[idx]);
- const Ogre::Quaternion startquat = bone->getInitialOrientation();
- const Ogre::Vector3 starttrans = bone->getInitialPosition();
- const Ogre::Vector3 startscale = bone->getInitialScale();
- Ogre::NodeAnimationTrack *nodetrack = (*anim)->getNodeTrack(bone->getHandle());
- Ogre::Quaternion lastquat, curquat;
- Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
- Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
- if(quatiter != quatkeys.end())
- lastquat = curquat = startquat.Inverse() * ConvertQuaternion(quatiter->data);
- if(traniter != trankeys.end())
- lasttrans = curtrans = ConvertVector3(traniter->data) - starttrans;
- if(scaleiter != scalekeys.end())
- lastscale = curscale = Ogre::Vector3(scaleiter->data) / startscale;
- bool didlast = false;
- while(!didlast)
- {
- float curtime = kfc->GetStopTime();
- if(quatiter != quatkeys.end())
- curtime = std::min(curtime, quatiter->time);
- if(traniter != trankeys.end())
- curtime = std::min(curtime, traniter->time);
- if(scaleiter != scalekeys.end())
- curtime = std::min(curtime, scaleiter->time);
- curtime = std::max(curtime, kfc->GetStartTime());
- if(curtime >= kfc->GetStopTime())
- {
- didlast = true;
- curtime = kfc->GetStopTime();
- }
- // Get the latest quaternion, translation, and scale for the
- // current time
- while(quatiter != quatkeys.end() && curtime >= quatiter->time)
- {
- lastquat = curquat;
- curquat = startquat.Inverse() * ConvertQuaternion(quatiter->data);
- quatiter++;
- }
- while(traniter != trankeys.end() && curtime >= traniter->time)
- {
- lasttrans = curtrans;
- curtrans = ConvertVector3(traniter->data) - starttrans;
- traniter++;
- }
- while(scaleiter != scalekeys.end() && curtime >= scaleiter->time)
- {
- lastscale = curscale;
- curscale = Ogre::Vector3(scaleiter->data) / startscale;
- scaleiter++;
- }
- Ogre::TransformKeyFrame *kframe;
- kframe = nodetrack->createNodeKeyFrame(curtime);
- if(quatiter == quatkeys.end() || quatiter == quatkeys.begin())
- kframe->setRotation(curquat);
- else
- {
- std::vector< Key<Quaternion> >::const_iterator last = quatiter-1;
- float diff = (curtime-last->time) / (quatiter->time-last->time);
- kframe->setRotation(Ogre::Quaternion::Slerp(diff, lastquat, curquat));
- }
- if(traniter == trankeys.end() || traniter == trankeys.begin())
- kframe->setTranslate(curtrans);
- else
- {
- std::vector< Key<Vector3> >::const_iterator last = traniter-1;
- float diff = (curtime-last->time) / (traniter->time-last->time);
- kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
- }
- if(scaleiter == scalekeys.end() || scaleiter == scalekeys.begin())
- kframe->setScale(curscale);
- else
- {
- std::vector< Key<float> >::const_iterator last = scaleiter-1;
- float diff = (curtime-last->time) / (scaleiter->time-last->time);
- kframe->setScale(lastscale + ((curscale-lastscale)*diff));
- }
- }
- }
- (*anim)->optimise();
- #endif
- }
- else
- std::cout <<"Unhandled animation type: "<<root->GetType().GetTypeName()<< std::endl;
- }
Add Comment
Please, Sign In to add comment