#include "node.hpp" #include #include #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(); if (num) { nif->getStrings(names, num); nif->getInts(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->readVector(mBoundMinMax, 6); } 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(); } 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) { mTriangles.resize(triNum * 3); 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); } }