mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 03:56:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			503 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			503 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "node.hpp"
 | |
| 
 | |
| #include <components/misc/strings/algorithm.hpp>
 | |
| #include <cstdint>
 | |
| 
 | |
| #include "data.hpp"
 | |
| #include "exception.hpp"
 | |
| #include "physics.hpp"
 | |
| #include "property.hpp"
 | |
| 
 | |
| namespace Nif
 | |
| {
 | |
|     void NiBoundingVolume::read(NIFStream* nif)
 | |
|     {
 | |
|         type = nif->getUInt();
 | |
|         switch (type)
 | |
|         {
 | |
|             case BASE_BV:
 | |
|                 break;
 | |
|             case SPHERE_BV:
 | |
|             {
 | |
|                 sphere.read(nif);
 | |
|                 break;
 | |
|             }
 | |
|             case BOX_BV:
 | |
|             {
 | |
|                 box.center = nif->getVector3();
 | |
|                 box.axes = nif->getMatrix3();
 | |
|                 box.extents = nif->getVector3();
 | |
|                 break;
 | |
|             }
 | |
|             case CAPSULE_BV:
 | |
|             {
 | |
|                 capsule.center = nif->getVector3();
 | |
|                 capsule.axis = nif->getVector3();
 | |
|                 capsule.extent = nif->getFloat();
 | |
|                 capsule.radius = nif->getFloat();
 | |
|                 break;
 | |
|             }
 | |
|             case LOZENGE_BV:
 | |
|             {
 | |
|                 lozenge.radius = nif->getFloat();
 | |
|                 if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
 | |
|                 {
 | |
|                     lozenge.extent0 = nif->getFloat();
 | |
|                     lozenge.extent1 = nif->getFloat();
 | |
|                 }
 | |
|                 lozenge.center = nif->getVector3();
 | |
|                 lozenge.axis0 = nif->getVector3();
 | |
|                 lozenge.axis1 = nif->getVector3();
 | |
|                 break;
 | |
|             }
 | |
|             case UNION_BV:
 | |
|             {
 | |
|                 unsigned int numChildren = nif->getUInt();
 | |
|                 if (numChildren == 0)
 | |
|                     break;
 | |
|                 children.resize(numChildren);
 | |
|                 for (NiBoundingVolume& child : children)
 | |
|                     child.read(nif);
 | |
|                 break;
 | |
|             }
 | |
|             case HALFSPACE_BV:
 | |
|             {
 | |
|                 halfSpace.plane = osg::Plane(nif->getVector4());
 | |
|                 if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
 | |
|                     halfSpace.origin = nif->getVector3();
 | |
|                 break;
 | |
|             }
 | |
|             default:
 | |
|             {
 | |
|                 throw Nif::Exception(
 | |
|                     "Unhandled NiBoundingVolume type: " + std::to_string(type), nif->getFile().getFilename());
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void Node::read(NIFStream* nif)
 | |
|     {
 | |
|         Named::read(nif);
 | |
| 
 | |
|         flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt();
 | |
|         trafo = nif->getTrafo();
 | |
|         if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0))
 | |
|             velocity = nif->getVector3();
 | |
|         if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
 | |
|             readRecordList(nif, props);
 | |
| 
 | |
|         if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0))
 | |
|             hasBounds = nif->getBoolean();
 | |
|         if (hasBounds)
 | |
|             bounds.read(nif);
 | |
|         // Reference to the collision object in Gamebryo files.
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
 | |
|             collision.read(nif);
 | |
| 
 | |
|         parents.clear();
 | |
| 
 | |
|         isBone = false;
 | |
|     }
 | |
| 
 | |
|     void Node::post(Reader& nif)
 | |
|     {
 | |
|         Named::post(nif);
 | |
|         postRecordList(nif, props);
 | |
|         collision.post(nif);
 | |
|     }
 | |
| 
 | |
|     void Node::setBone()
 | |
|     {
 | |
|         isBone = true;
 | |
|     }
 | |
| 
 | |
|     void NiNode::read(NIFStream* nif)
 | |
|     {
 | |
|         Node::read(nif);
 | |
|         readRecordList(nif, children);
 | |
|         if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
 | |
|             readRecordList(nif, effects);
 | |
| 
 | |
|         // FIXME: stopgap solution until we figure out what Oblivion does if it does anything
 | |
|         if (nif->getVersion() > NIFFile::NIFVersion::VER_MW && nif->getVersion() < NIFFile::NIFVersion::VER_BGS)
 | |
|             return;
 | |
| 
 | |
|         // Discard transformations for the root node, otherwise some meshes
 | |
|         // occasionally get wrong orientation. Only for NiNode-s for now, but
 | |
|         // can be expanded if needed.
 | |
|         // FIXME: if node 0 is *not* the only root node, this must not happen.
 | |
|         // FIXME: doing this here is awful.
 | |
|         // We want to do this on world scene graph level rather than local scene graph level.
 | |
|         if (0 == recIndex && !Misc::StringUtils::ciEqual(name, "bip01"))
 | |
|         {
 | |
|             trafo = Nif::Transformation::getIdentity();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void NiNode::post(Reader& nif)
 | |
|     {
 | |
|         Node::post(nif);
 | |
|         postRecordList(nif, children);
 | |
|         postRecordList(nif, effects);
 | |
| 
 | |
|         for (auto& child : children)
 | |
|         {
 | |
|             // Why would a unique list of children contain empty refs?
 | |
|             if (!child.empty())
 | |
|                 child->parents.push_back(this);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void NiGeometry::MaterialData::read(NIFStream* nif)
 | |
|     {
 | |
|         if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0))
 | |
|             return;
 | |
|         unsigned int num = 0;
 | |
|         if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 3))
 | |
|             num = nif->getBoolean(); // Has Shader
 | |
|         else if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
 | |
|             num = nif->getUInt();
 | |
| 
 | |
|         nif->readVector(names, num);
 | |
|         nif->readVector(extra, num);
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
 | |
|             active = nif->getUInt();
 | |
|         if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
 | |
|             needsUpdate = nif->getBoolean();
 | |
|     }
 | |
| 
 | |
|     void NiGeometry::read(NIFStream* nif)
 | |
|     {
 | |
|         Node::read(nif);
 | |
|         data.read(nif);
 | |
|         skin.read(nif);
 | |
|         material.read(nif);
 | |
|         if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS
 | |
|             && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
 | |
|         {
 | |
|             shaderprop.read(nif);
 | |
|             alphaprop.read(nif);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void NiGeometry::post(Reader& nif)
 | |
|     {
 | |
|         Node::post(nif);
 | |
|         data.post(nif);
 | |
|         skin.post(nif);
 | |
|         shaderprop.post(nif);
 | |
|         alphaprop.post(nif);
 | |
|         if (recType != RC_NiParticles && !skin.empty())
 | |
|             nif.setUseSkinning(true);
 | |
|     }
 | |
| 
 | |
|     void BSLODTriShape::read(NIFStream* nif)
 | |
|     {
 | |
|         NiTriShape::read(nif);
 | |
|         lod0 = nif->getUInt();
 | |
|         lod1 = nif->getUInt();
 | |
|         lod2 = nif->getUInt();
 | |
|     }
 | |
| 
 | |
|     void NiCamera::Camera::read(NIFStream* nif)
 | |
|     {
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
 | |
|             cameraFlags = nif->getUShort();
 | |
|         left = nif->getFloat();
 | |
|         right = nif->getFloat();
 | |
|         top = nif->getFloat();
 | |
|         bottom = nif->getFloat();
 | |
|         nearDist = nif->getFloat();
 | |
|         farDist = nif->getFloat();
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
 | |
|             orthographic = nif->getBoolean();
 | |
|         vleft = nif->getFloat();
 | |
|         vright = nif->getFloat();
 | |
|         vtop = nif->getFloat();
 | |
|         vbottom = nif->getFloat();
 | |
| 
 | |
|         LOD = nif->getFloat();
 | |
|     }
 | |
| 
 | |
|     void NiCamera::read(NIFStream* nif)
 | |
|     {
 | |
|         Node::read(nif);
 | |
| 
 | |
|         cam.read(nif);
 | |
| 
 | |
|         nif->getInt(); // -1
 | |
|         nif->getInt(); // 0
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
 | |
|             nif->getInt(); // 0
 | |
|     }
 | |
| 
 | |
|     void NiSwitchNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
 | |
|             switchFlags = nif->getUShort();
 | |
|         initialIndex = nif->getUInt();
 | |
|     }
 | |
| 
 | |
|     void NiLODNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiSwitchNode::read(nif);
 | |
|         if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW
 | |
|             && nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 0))
 | |
|             lodCenter = nif->getVector3();
 | |
|         else if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0))
 | |
|         {
 | |
|             nif->skip(4); // NiLODData, unsupported at the moment
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         unsigned int numLodLevels = nif->getUInt();
 | |
|         for (unsigned int i = 0; i < numLodLevels; ++i)
 | |
|         {
 | |
|             LODRange r;
 | |
|             r.minRange = nif->getFloat();
 | |
|             r.maxRange = nif->getFloat();
 | |
|             lodLevels.push_back(r);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void NiFltAnimationNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiSwitchNode::read(nif);
 | |
|         mDuration = nif->getFloat();
 | |
|     }
 | |
| 
 | |
|     void NiSortAdjustNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         mMode = nif->getInt();
 | |
|         if (nif->getVersion() <= NIFStream::generateVersion(20, 0, 0, 3))
 | |
|             mSubSorter.read(nif);
 | |
|     }
 | |
| 
 | |
|     void NiSortAdjustNode::post(Reader& nif)
 | |
|     {
 | |
|         NiNode::post(nif);
 | |
|         mSubSorter.post(nif);
 | |
|     }
 | |
| 
 | |
|     void NiBillboardNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
 | |
|             mMode = nif->getUShort() & 0x7;
 | |
|         else
 | |
|             mMode = (flags >> 5) & 0x3;
 | |
|     }
 | |
| 
 | |
|     void NiDefaultAVObjectPalette::read(NIFStream* nif)
 | |
|     {
 | |
|         mScene.read(nif);
 | |
|         size_t numObjects = nif->getUInt();
 | |
|         for (size_t i = 0; i < numObjects; i++)
 | |
|             mObjects[nif->getSizedString()].read(nif);
 | |
|     }
 | |
| 
 | |
|     void NiDefaultAVObjectPalette::post(Reader& nif)
 | |
|     {
 | |
|         mScene.post(nif);
 | |
|         for (auto& object : mObjects)
 | |
|             object.second.post(nif);
 | |
|     }
 | |
| 
 | |
|     void BSTreeNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         readRecordList(nif, mBones1);
 | |
|         readRecordList(nif, mBones2);
 | |
|     }
 | |
| 
 | |
|     void BSTreeNode::post(Reader& nif)
 | |
|     {
 | |
|         NiNode::post(nif);
 | |
|         postRecordList(nif, mBones1);
 | |
|         postRecordList(nif, mBones2);
 | |
|     }
 | |
| 
 | |
|     void BSMultiBoundNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         mMultiBound.read(nif);
 | |
|         if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_SKY)
 | |
|             mType = nif->getUInt();
 | |
|     }
 | |
| 
 | |
|     void BSMultiBoundNode::post(Reader& nif)
 | |
|     {
 | |
|         NiNode::post(nif);
 | |
|         mMultiBound.post(nif);
 | |
|     }
 | |
| 
 | |
|     void BSTriShape::read(NIFStream* nif)
 | |
|     {
 | |
|         Node::read(nif);
 | |
|         mBoundingSphere.read(nif);
 | |
| 
 | |
|         if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
 | |
|         {
 | |
|             nif->readArray(mBoundMinMax);
 | |
|         }
 | |
| 
 | |
|         mSkin.read(nif);
 | |
|         mShaderProperty.read(nif);
 | |
|         mAlphaProperty.read(nif);
 | |
| 
 | |
|         mVertDesc.read(nif);
 | |
| 
 | |
|         unsigned int triNum;
 | |
|         if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
 | |
|         {
 | |
|             triNum = nif->get<unsigned short>();
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             nif->read(triNum);
 | |
|         }
 | |
| 
 | |
|         unsigned short vertNum;
 | |
|         nif->read(vertNum);
 | |
|         nif->read(mDataSize);
 | |
| 
 | |
|         if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
 | |
|         {
 | |
|             mVertData.resize(vertNum);
 | |
|             for (auto& vertex : mVertData)
 | |
|                 vertex.read(nif, mVertDesc.mFlags);
 | |
|         }
 | |
|         else if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
 | |
|         {
 | |
|             throw Nif::Exception("FO4 BSTriShape is not supported yet: ", nif->getFile().getFilename());
 | |
|         }
 | |
| 
 | |
|         if (mDataSize > 0)
 | |
|             nif->readVector(mTriangles, triNum * 3);
 | |
| 
 | |
|         if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
 | |
|         {
 | |
|             nif->read(mParticleDataSize);
 | |
|             if (mParticleDataSize > 0)
 | |
|             {
 | |
|                 throw Nif::Exception("Unhandled Particle Data in BSTriShape: ", nif->getFile().getFilename());
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void BSTriShape::post(Reader& nif)
 | |
|     {
 | |
|         Node::post(nif);
 | |
|         mSkin.post(nif);
 | |
|         mShaderProperty.post(nif);
 | |
|         mAlphaProperty.post(nif);
 | |
|     }
 | |
| 
 | |
|     void BSVertexDesc::read(NIFStream* nif)
 | |
|     {
 | |
|         uint64_t data;
 | |
|         nif->read(data);
 | |
|         mVertexDataSize = (data & 0xF) >> 0x00;
 | |
|         mDynamicVertexSize = (data & 0xF0) >> 0x04;
 | |
|         mUV1Offset = (data & 0xF00) >> 0x08;
 | |
|         mUV2Offset = (data & 0xF000) >> 0x0C;
 | |
|         mNormalOffset = (data & 0xF0000) >> 0x10;
 | |
|         mTangentOffset = (data & 0xF00000) >> 0x14;
 | |
|         mColorOffset = (data & 0xF000000) >> 0x18;
 | |
|         mSkinningDataOffset = (data & 0xF0000000) >> 0x1C;
 | |
|         mLandscapeDataOffset = (data & 0xF00000000) >> 0x20;
 | |
|         mEyeDataOffset = (data & 0xF000000000) >> 0x24;
 | |
|         mFlags = (data & 0xFFF00000000000) >> 0x2C;
 | |
|     }
 | |
| 
 | |
|     void NiBoundingVolume::NiSphereBV::read(NIFStream* nif)
 | |
|     {
 | |
|         nif->read(center);
 | |
|         nif->read(radius);
 | |
|     }
 | |
| 
 | |
|     void BSVertexData::read(NIFStream* nif, uint16_t flags)
 | |
|     {
 | |
|         uint16_t vertexFlag = flags & BSVertexDesc::VertexAttribute::Vertex;
 | |
|         uint16_t tangentsFlag = flags & BSVertexDesc::VertexAttribute::Tangents;
 | |
|         uint16_t UVsFlag = flags & BSVertexDesc::VertexAttribute::UVs;
 | |
|         uint16_t normalsFlag = flags & BSVertexDesc::VertexAttribute::Normals;
 | |
| 
 | |
|         if (vertexFlag == BSVertexDesc::VertexAttribute::Vertex)
 | |
|         {
 | |
|             nif->read(mVertex);
 | |
|         }
 | |
| 
 | |
|         if ((vertexFlag | tangentsFlag)
 | |
|             == (BSVertexDesc::VertexAttribute::Vertex | BSVertexDesc::VertexAttribute::Tangents))
 | |
|         {
 | |
|             nif->read(mBitangentX);
 | |
|         }
 | |
| 
 | |
|         if ((vertexFlag | tangentsFlag) == BSVertexDesc::VertexAttribute::Vertex)
 | |
|         {
 | |
|             nif->read(mUnusedW);
 | |
|         }
 | |
| 
 | |
|         if (UVsFlag == BSVertexDesc::VertexAttribute::UVs)
 | |
|         {
 | |
|             nif->readArray(mUV);
 | |
|         }
 | |
| 
 | |
|         if (normalsFlag)
 | |
|         {
 | |
|             nif->readArray(mNormal);
 | |
| 
 | |
|             nif->read(mBitangentY);
 | |
|         }
 | |
| 
 | |
|         if ((normalsFlag | tangentsFlag)
 | |
|             == (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents))
 | |
|         {
 | |
|             nif->readArray(mTangent);
 | |
| 
 | |
|             nif->read(mBitangentZ);
 | |
|         }
 | |
| 
 | |
|         if (flags & BSVertexDesc::VertexAttribute::Vertex_Colors)
 | |
|         {
 | |
|             nif->readArray(mVertColors);
 | |
|         }
 | |
| 
 | |
|         if (flags & BSVertexDesc::VertexAttribute::Skinned)
 | |
|         {
 | |
|             nif->readArray(mBoneWeights);
 | |
|             nif->readArray(mBoneIndices);
 | |
|         }
 | |
| 
 | |
|         if (flags & BSVertexDesc::VertexAttribute::Eye_Data)
 | |
|         {
 | |
|             throw Nif::Exception("Unhandled Eye Data in BSTriShape: ", nif->getFile().getFilename());
 | |
|             // nif->read(mEyeData);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void BSValueNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         nif->read(mValue);
 | |
|         nif->read(mValueFlags);
 | |
|     }
 | |
| 
 | |
|     void BSOrderedNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         nif->read(mAlphaSortBound);
 | |
|         nif->read(mStaticBound);
 | |
|     }
 | |
| 
 | |
|     void BSRangeNode::read(NIFStream* nif)
 | |
|     {
 | |
|         NiNode::read(nif);
 | |
|         nif->read(mMin);
 | |
|         nif->read(mMax);
 | |
|         nif->read(mCurrent);
 | |
|     }
 | |
| }
 |