Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
25 kB · 803 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804#include "node.hpp"
#include <cstdint>
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
#include <components/misc/convert.hpp>#include <components/misc/strings/algorithm.hpp>#include <components/resource/bulletshape.hpp>
#include "data.hpp"#include "exception.hpp"#include "physics.hpp"#include "property.hpp"
namespace{
void triBasedGeomToBtTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data) { // FIXME: copying vertices/indices individually is unreasonable const std::vector<osg::Vec3f>& vertices = data.mVertices; mesh.preallocateVertices(static_cast<int>(vertices.size())); for (const osg::Vec3f& vertex : vertices) mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false);
mesh.preallocateIndices(static_cast<int>(data.mNumTriangles) * 3); }
void trianglesToBtTriangleMesh(btTriangleMesh& mesh, const std::vector<unsigned short>& triangles) { for (std::size_t i = 0; i < triangles.size(); i += 3) mesh.addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]); }
void stripsToBtTriangleMesh(btTriangleMesh& mesh, const std::vector<std::vector<unsigned short>>& strips) { for (const auto& strip : strips) { if (strip.size() < 3) continue;
unsigned short a; unsigned short b = strip[0]; unsigned short c = strip[1]; for (size_t i = 2; i < strip.size(); i++) { a = b; b = c; c = strip[i]; if (a == b || b == c || a == c) continue; if (i % 2 == 0) mesh.addTriangleIndices(a, b, c); else mesh.addTriangleIndices(a, c, b); } } }
}
namespace Nif{
namespace { void readBSDistantObjectInstanceTransform(NIFStream& stream, osg::Matrixf& value) { std::array<float, 16> mat; stream.readArray(mat); value.set(mat.data()); } }
void BoundingVolume::read(NIFStream* nif) { nif->read(mType); switch (mType) { case BASE_BV: break; case SPHERE_BV: { nif->read(mSphere); break; } case BOX_BV: { nif->read(mBox.mCenter); nif->read(mBox.mAxes); nif->read(mBox.mExtents); break; } case CAPSULE_BV: { nif->read(mCapsule.mCenter); nif->read(mCapsule.mAxis); nif->read(mCapsule.mExtent); nif->read(mCapsule.mRadius); break; } case LOZENGE_BV: { nif->read(mLozenge.mRadius); if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0)) { nif->read(mLozenge.mExtent0); nif->read(mLozenge.mExtent1); } nif->read(mLozenge.mCenter); nif->read(mLozenge.mAxis0); nif->read(mLozenge.mAxis1); break; } case UNION_BV: { nif->readVectorOfRecords<uint32_t>(mChildren); break; } case HALFSPACE_BV: { mHalfSpace.mPlane = osg::Plane(nif->get<osg::Vec4f>()); if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0)) nif->read(mHalfSpace.mOrigin); break; } default: { throw Nif::Exception( "Unhandled BoundingVolume type: " + std::to_string(mType), nif->getFile().getFilename()); } } }
void NiAVObject::read(NIFStream* nif) { NiObjectNET::read(nif);
if (nif->getBethVersion() <= 26) mFlags = nif->get<uint16_t>(); else nif->read(mFlags); nif->read(mTransform.mTranslation); nif->read(mTransform.mRotation); nif->read(mTransform.mScale); if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) nif->read(mVelocity); if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) readRecordList(nif, mProperties); if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->get<bool>()) mBounds.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) mCollision.read(nif); }
void NiAVObject::post(Reader& nif) { NiObjectNET::post(nif);
postRecordList(nif, mProperties); mCollision.post(nif); }
void NiAVObject::setBone() { mIsBone = true; }
void NiNode::read(NIFStream* nif) { NiAVObject::read(nif);
readRecordList(nif, mChildren); if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) readRecordList(nif, mEffects);
// 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 (mRecordIndex == 0 && !Misc::StringUtils::ciEqual(mName, "bip01")) { mTransform = Nif::NiTransform::getIdentity(); } }
void NiNode::post(Reader& nif) { NiAVObject::post(nif);
postRecordList(nif, mChildren); postRecordList(nif, mEffects);
for (auto& child : mChildren) { // Why would a unique list of children contain empty refs? if (!child.empty()) child->mParents.push_back(this); } }
const NiNode* NiNode::findRootCollisionNode(bool recursive) const { // Yes, this search needs to be reversed for (auto it = mChildren.crbegin(); it != mChildren.crend(); ++it) { const NiAVObjectPtr& child = *it; if (child.empty()) continue;
if (child->mRecordType == Nif::RC_RootCollisionNode) return static_cast<const NiNode*>(child.getPtr());
if (recursive) { const NiNode* childNode = dynamic_cast<const NiNode*>(child.getPtr()); if (!childNode) continue; if (const NiNode* found = childNode->findRootCollisionNode(recursive)) return found; } }
return nullptr; }
void NiGeometry::MaterialData::read(NIFStream* nif) { if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0)) return; std::uint32_t numNames = 0; if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) numNames = nif->get<uint32_t>(); else if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 3)) numNames = nif->get<bool>(); nif->readVector(mNames, numNames); nif->readVector(mExtra, numNames); if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) nif->read(mActive); if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) nif->read(mNeedsUpdate); }
void NiGeometry::read(NIFStream* nif) { NiAVObject::read(nif);
mData.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13)) mSkin.read(nif); mMaterial.read(nif); if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) { mShaderProperty.read(nif); mAlphaProperty.read(nif); } }
void NiGeometry::post(Reader& nif) { NiAVObject::post(nif);
mData.post(nif); mSkin.post(nif); mShaderProperty.post(nif); mAlphaProperty.post(nif); if (mRecordType != RC_NiParticles && !mSkin.empty()) nif.setUseSkinning(true);
if (!mData.empty()) { switch (mRecordType) { case RC_NiTriShape: case RC_BSLODTriShape: case RC_BSSegmentedTriShape: if (mData->mRecordType != RC_NiTriShapeData) mData = NiGeometryDataPtr(nullptr); break; case RC_NiTriStrips: if (mData->mRecordType != RC_NiTriStripsData) mData = NiGeometryDataPtr(nullptr); break; case RC_NiParticles: if (mData->mRecordType != RC_NiParticlesData) mData = NiGeometryDataPtr(nullptr); break; case RC_NiLines: if (mData->mRecordType != RC_NiLinesData) mData = NiGeometryDataPtr(nullptr); break; default: break; } } }
std::unique_ptr<btCollisionShape> NiTriShape::getCollisionShape() const { if (mData.empty() || mData->mVertices.empty()) return nullptr;
std::vector<const std::vector<unsigned short>*> triangleLists; std::vector<const std::vector<std::vector<unsigned short>>*> stripsLists; auto data = static_cast<const NiTriShapeData*>(mData.getPtr()); const Nif::NiSkinPartition* partitions = nullptr; if (!mSkin.empty()) partitions = mSkin->getPartitions();
if (partitions) { triangleLists.reserve(partitions->mPartitions.size()); stripsLists.reserve(partitions->mPartitions.size()); for (auto& partition : partitions->mPartitions) { triangleLists.push_back(&partition.mTrueTriangles); stripsLists.push_back(&partition.mTrueStrips); } } else if (data->mNumTriangles != 0) triangleLists.push_back(&data->mTriangles);
// This makes a perhaps dangerous assumption that NiSkinPartition will never have more than 65536 triangles. auto mesh = std::make_unique<btTriangleMesh>(); triBasedGeomToBtTriangleMesh(*mesh, *data); for (const auto triangles : triangleLists) trianglesToBtTriangleMesh(*mesh, *triangles); for (const auto strips : stripsLists) stripsToBtTriangleMesh(*mesh, *strips);
if (mesh->getNumTriangles() == 0) return nullptr;
auto shape = std::make_unique<Resource::TriangleMeshShape>(mesh.get(), true); std::ignore = mesh.release();
return shape; }
std::unique_ptr<btCollisionShape> NiTriStrips::getCollisionShape() const { if (mData.empty() || mData->mVertices.empty()) return nullptr;
std::vector<const std::vector<unsigned short>*> triangleLists; std::vector<const std::vector<std::vector<unsigned short>>*> stripsLists; auto data = static_cast<const NiTriStripsData*>(mData.getPtr()); const Nif::NiSkinPartition* partitions = nullptr; if (!mSkin.empty()) partitions = mSkin->getPartitions();
if (partitions) { triangleLists.reserve(partitions->mPartitions.size()); stripsLists.reserve(partitions->mPartitions.size()); for (auto& partition : partitions->mPartitions) { triangleLists.push_back(&partition.mTrueTriangles); stripsLists.push_back(&partition.mTrueStrips); } } else if (data->mNumTriangles != 0) stripsLists.push_back(&data->mStrips);
auto mesh = std::make_unique<btTriangleMesh>(); triBasedGeomToBtTriangleMesh(*mesh, *data); for (const auto triangles : triangleLists) trianglesToBtTriangleMesh(*mesh, *triangles); for (const auto strips : stripsLists) stripsToBtTriangleMesh(*mesh, *strips);
if (mesh->getNumTriangles() == 0) return nullptr;
auto shape = std::make_unique<Resource::TriangleMeshShape>(mesh.get(), true); std::ignore = mesh.release();
return shape; }
std::unique_ptr<btCollisionShape> NiLines::getCollisionShape() const { return nullptr; }
std::unique_ptr<btCollisionShape> NiParticles::getCollisionShape() const { return nullptr; }
void BSSegmentedTriShape::SegmentData::read(NIFStream* nif) { nif->read(mFlags); nif->read(mStartIndex); nif->read(mNumTriangles); }
void BSSegmentedTriShape::read(NIFStream* nif) { NiTriShape::read(nif);
nif->readVectorOfRecords<uint32_t>(mSegments); }
void BSLODTriShape::read(NIFStream* nif) { NiTriBasedGeom::read(nif);
nif->readArray(mLOD); }
void NiCamera::read(NIFStream* nif) { NiAVObject::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) nif->read(mCameraFlags); nif->read(mLeft); nif->read(mRight); nif->read(mTop); nif->read(mBottom); nif->read(mNearDist); nif->read(mFarDist); if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) nif->read(mOrthographic); nif->read(mVLeft); nif->read(mVRight); nif->read(mVTop); nif->read(mVBottom); nif->read(mLODAdjust); mScene.read(nif); nif->skip(4); // Unused if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0)) nif->skip(4); // Unused }
void NiCamera::post(Reader& nif) { NiAVObject::post(nif);
mScene.post(nif); }
void NiSwitchNode::read(NIFStream* nif) { NiNode::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) nif->read(mSwitchFlags); nif->read(mInitialIndex); }
void NiLODNode::LODRange::read(NIFStream* nif) { nif->read(mMinRange); nif->read(mMaxRange); }
void NiLODNode::read(NIFStream* nif) { NiSwitchNode::read(nif);
if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0)) { nif->skip(4); // NiLODData, unsupported at the moment return; }
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW) nif->read(mLODCenter);
nif->readVectorOfRecords<uint32_t>(mLODLevels); }
void NiFltAnimationNode::read(NIFStream* nif) { NiSwitchNode::read(nif);
nif->read(mDuration); }
void NiSortAdjustNode::read(NIFStream* nif) { NiNode::read(nif);
mMode = static_cast<SortingMode>(nif->get<uint32_t>()); 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 = static_cast<BillboardMode>(nif->get<uint16_t>() & 0x7); else mMode = static_cast<BillboardMode>((mFlags >> 5) & 0x3); }
void NiDefaultAVObjectPalette::read(NIFStream* nif) { mScene.read(nif); uint32_t numObjects; nif->read(numObjects); for (uint32_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) mCullingType = static_cast<BSCPCullingType>(nif->get<uint32_t>()); }
void BSMultiBoundNode::post(Reader& nif) { NiNode::post(nif);
mMultiBound.post(nif); }
void BSTriShape::read(NIFStream* nif) { NiAVObject::read(nif);
nif->read(mBoundingSphere); if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) nif->readArray(mBoundMinMax);
mSkin.read(nif); mShaderProperty.read(nif); mAlphaProperty.read(nif); mVertDesc.read(nif);
size_t numTriangleIndices; if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4) numTriangleIndices = nif->get<uint32_t>() * 3; else numTriangleIndices = nif->get<uint16_t>() * 3; nif->read(mNumVertices); mVertData.reserve(mNumVertices); nif->read(mDataSize); if (mDataSize > 0) { for (uint16_t i = 0; i < mNumVertices; ++i) mVertData.emplace_back().read(nif, mVertDesc.mFlags); nif->readVector(mTriangles, numTriangleIndices); }
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) { nif->read(mParticleDataSize); if (mParticleDataSize > 0) { nif->readVector(mParticleVerts, mNumVertices * 3); nif->readVector(mParticleNormals, mNumVertices * 3); nif->readVector(mParticleTriangles, numTriangleIndices); } } }
void BSTriShape::post(Reader& nif) { NiAVObject::post(nif);
mSkin.post(nif); mShaderProperty.post(nif); mAlphaProperty.post(nif); if (!mSkin.empty()) nif.setUseSkinning(true); }
void BSDynamicTriShape::read(NIFStream* nif) { BSTriShape::read(nif);
nif->read(mDynamicDataSize); // nifly style. // Consider complaining if mDynamicDataSize * 16 != mNumVertices? nif->readVector(mDynamicData, mNumVertices); }
void BSMeshLODTriShape::read(NIFStream* nif) { BSTriShape::read(nif);
nif->readArray(mLOD); }
void BSSubIndexTriShape::SubSegment::read(NIFStream* nif) { nif->read(mStartIndex); nif->read(mNumPrimitives); nif->read(mArrayIndex); nif->skip(4); // Unknown }
void BSSubIndexTriShape::Segment::read(NIFStream* nif) { nif->read(mStartIndex); nif->read(mNumPrimitives); nif->read(mParentArrayIndex); nif->readVectorOfRecords<uint32_t>(mSubSegments); }
void BSSubIndexTriShape::SubSegmentDataRecord::read(NIFStream* nif) { nif->read(mUserSlotID); nif->read(mMaterial); nif->readVector(mExtraData, nif->get<uint32_t>()); }
void BSSubIndexTriShape::SubSegmentData::read(NIFStream* nif) { uint32_t numArrayIndices; nif->read(numArrayIndices); const uint32_t numRecords = nif->get<uint32_t>(); nif->readVector(mArrayIndices, numArrayIndices); nif->readVectorOfRecords(numRecords, mDataRecords); mSSFFile = nif->getSizedString(nif->get<uint16_t>()); }
void BSSubIndexTriShape::Segmentation::read(NIFStream* nif) { nif->read(mNumPrimitives); const uint32_t numSegments = nif->get<uint32_t>(); nif->read(mNumTotalSegments); nif->readVectorOfRecords(numSegments, mSegments); if (numSegments < mNumTotalSegments) mSubSegmentData.read(nif); }
void BSSubIndexTriShape::read(NIFStream* nif) { BSTriShape::read(nif);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) nif->readVectorOfRecords<uint32_t>(mSegments); else if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4 && mDataSize > 0) mSegmentation.read(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 = static_cast<uint8_t>((data & 0xF0000) >> 0x10); mTangentOffset = static_cast<uint8_t>((data & 0xF00000) >> 0x14); mColorOffset = (data & 0xF000000) >> 0x18; mSkinningDataOffset = (data & 0xF0000000) >> 0x1C; mLandscapeDataOffset = static_cast<uint8_t>((data & 0xF00000000) >> 0x20); mEyeDataOffset = static_cast<uint8_t>((data & 0xF000000000) >> 0x24); mFlags = static_cast<uint16_t>((data & 0xFFF00000000000) >> 0x2C); if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) mFlags |= BSVertexDesc::VertexAttribute::Full_Precision; }
void BSVertexData::read(NIFStream* nif, uint16_t flags) { bool fullPrecision = flags & BSVertexDesc::VertexAttribute::Full_Precision; bool hasVertex = flags & BSVertexDesc::VertexAttribute::Vertex; bool hasTangent = flags & BSVertexDesc::VertexAttribute::Tangents; bool hasUV = flags & BSVertexDesc::VertexAttribute::UVs; bool hasNormal = flags & BSVertexDesc::VertexAttribute::Normals; bool hasVertexColor = flags & BSVertexDesc::VertexAttribute::Vertex_Colors; bool hasSkinData = flags & BSVertexDesc::VertexAttribute::Skinned; bool hasEyeData = flags & BSVertexDesc::VertexAttribute::Eye_Data;
if (hasVertex) { if (fullPrecision) nif->read(mVertex); else nif->readArray(mHalfVertex); }
if (hasUV) nif->readArray(mUV);
if (hasNormal) { nif->readArray(mNormal); if (hasTangent) nif->readArray(mTangent); }
if (hasVertexColor) nif->readArray(mVertColor);
if (hasSkinData) { nif->readArray(mBoneWeights); nif->readArray(mBoneIndices); }
if (hasEyeData) 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); }
void BSResourceID::read(NIFStream* nif) { nif->read(mFileHash); nif->readArray(mExtension); nif->read(mDirectoryHash); }
void BSDistantObjectInstance::read(NIFStream* nif) { mResourceID.read(nif); nif->skip(12 * nif->get<uint32_t>()); // Unknown data nif->readVectorOfRecords<uint32_t>(readBSDistantObjectInstanceTransform, mTransforms); }
void BSShaderTextureArray::read(NIFStream* nif) { nif->skip(1); // Unknown const uint32_t numArrays = nif->get<uint32_t>(); mTextureArrays.reserve(numArrays); for (uint32_t i = 0; i < numArrays; ++i) nif->getSizedStrings(mTextureArrays.emplace_back(), nif->get<uint32_t>()); }
void BSDistantObjectInstancedNode::read(NIFStream* nif) { BSMultiBoundNode::read(nif);
nif->readVectorOfRecords<uint32_t>(mInstances); for (BSShaderTextureArray& textureArray : mShaderTextureArrays) textureArray.read(nif); }
}