diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 3a854bf5c9..5769b1d0a8 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -259,14 +259,19 @@ namespace value.isBone = false; } - void init(Nif::NiTriShape& value) + void init(Nif::NiGeometry& value) { init(static_cast(value)); - value.recType = Nif::RC_NiTriShape; - value.data = Nif::NiTriShapeDataPtr(nullptr); + value.data = Nif::NiGeometryDataPtr(nullptr); value.skin = Nif::NiSkinInstancePtr(nullptr); } + void init(Nif::NiTriShape& value) + { + init(static_cast(value)); + value.recType = Nif::RC_NiTriShape; + } + void init(Nif::NiSkinInstance& value) { value.data = Nif::NiSkinDataPtr(nullptr); @@ -361,13 +366,15 @@ namespace init(mNiStringExtraData2); init(mController); + mNiTriShapeData.recType = Nif::RC_NiTriShapeData; mNiTriShapeData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0)}; mNiTriShapeData.triangles = {0, 1, 2}; - mNiTriShape.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData); + mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); + mNiTriShapeData2.recType = Nif::RC_NiTriShapeData; mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)}; mNiTriShapeData2.triangles = {0, 1, 2}; - mNiTriShape2.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData2); + mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2); } }; @@ -873,7 +880,7 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape) { - mNiTriShape.data = Nif::NiTriShapeDataPtr(nullptr); + mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); @@ -888,7 +895,8 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape) { - mNiTriShape.data->triangles.clear(); + auto data = static_cast(mNiTriShape.data.getPtr()); + data->triangles.clear(); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index ab2b8dc173..c8116ce74e 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -47,6 +47,11 @@ namespace Nif data.post(nif); } + void BSShaderTextureSet::read(NIFStream *nif) + { + nif->getSizedStrings(textures, nif->getUInt()); + } + void NiParticleModifier::read(NIFStream *nif) { next.read(nif); diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 57d538f839..ac48e50498 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -66,6 +66,24 @@ public: void post(NIFFile *nif) override; }; +struct BSShaderTextureSet : public Record +{ + enum TextureType + { + TextureType_Base = 0, + TextureType_Normal = 1, + TextureType_Glow = 2, + TextureType_Parallax = 3, + TextureType_Env = 4, + TextureType_EnvMask = 5, + TextureType_Subsurface = 6, + TextureType_BackLighting = 7 + }; + std::vector textures; + + void read(NIFStream *nif) override; +}; + struct NiParticleModifier : public Record { NiParticleModifierPtr next; diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 1e909120e4..704c4928e6 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -325,4 +325,15 @@ namespace Nif data.post(nif); } + void NiColorInterpolator::read(NIFStream *nif) + { + defaultVal = nif->getVector4(); + data.read(nif); + } + + void NiColorInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 6b84d3749b..4016188ce0 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -37,7 +37,7 @@ public: float lifetime; float lifespan; float timestamp; - int vertex; + unsigned short vertex; }; float velocity; @@ -230,5 +230,13 @@ struct NiTransformInterpolator : public Interpolator void post(NIFFile *nif) override; }; +struct NiColorInterpolator : public Interpolator +{ + osg::Vec4f defaultVal; + NiColorDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 67c864f473..665533c91b 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -129,6 +129,10 @@ static std::map makeFactory() factory["NiPoint3Interpolator"] = {&construct , RC_NiPoint3Interpolator }; factory["NiTransformController"] = {&construct , RC_NiKeyframeController }; factory["NiTransformInterpolator"] = {&construct , RC_NiTransformInterpolator }; + factory["NiColorInterpolator"] = {&construct , RC_NiColorInterpolator }; + factory["BSShaderTextureSet"] = {&construct , RC_BSShaderTextureSet }; + factory["BSLODTriShape"] = {&construct , RC_BSLODTriShape }; + factory["BSShaderProperty"] = {&construct , RC_BSShaderProperty }; return factory; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 58397bdca4..08f066e361 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -240,43 +240,6 @@ struct NiNode : Node }; struct NiGeometry : Node -{ - struct MaterialData - { - std::vector materialNames; - std::vector materialExtraData; - unsigned int activeMaterial{0}; - bool materialNeedsUpdate{false}; - void read(NIFStream *nif) - { - if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0)) - return; - unsigned int numMaterials = 0; - if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,3)) - numMaterials = nif->getBoolean(); // Has Shader - else if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5)) - numMaterials = nif->getUInt(); - if (numMaterials) - { - nif->getStrings(materialNames, numMaterials); - nif->getInts(materialExtraData, numMaterials); - } - if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5)) - activeMaterial = nif->getUInt(); - if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) - { - materialNeedsUpdate = nif->getBoolean(); - if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) - nif->skip(8); - } - } - }; - - NiSkinInstancePtr skin; - MaterialData materialData; -}; - -struct NiTriShape : NiGeometry { /* Possible flags: 0x40 - mesh has no vertex normals ? @@ -285,14 +248,50 @@ struct NiTriShape : NiGeometry been observed so far. */ - NiTriShapeDataPtr data; + struct MaterialData + { + std::vector names; + std::vector extra; + unsigned int active{0}; + bool needsUpdate{false}; + void 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(); + } + }; + + NiGeometryDataPtr data; + NiSkinInstancePtr skin; + MaterialData material; + BSShaderPropertyPtr shaderprop; + NiAlphaPropertyPtr alphaprop; void read(NIFStream *nif) override { Node::read(nif); data.read(nif); skin.read(nif); - materialData.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 post(NIFFile *nif) override @@ -300,53 +299,28 @@ struct NiTriShape : NiGeometry Node::post(nif); data.post(nif); skin.post(nif); - if (!skin.empty()) + shaderprop.post(nif); + alphaprop.post(nif); + if (recType != RC_NiParticles && !skin.empty()) nif->setUseSkinning(true); } }; -struct NiTriStrips : NiGeometry +struct NiTriShape : NiGeometry {}; +struct BSLODTriShape : NiTriShape { - NiTriStripsDataPtr data; - + unsigned int lod0, lod1, lod2; void read(NIFStream *nif) override { - Node::read(nif); - data.read(nif); - skin.read(nif); - materialData.read(nif); - } - - void post(NIFFile *nif) override - { - Node::post(nif); - data.post(nif); - skin.post(nif); - if (!skin.empty()) - nif->setUseSkinning(true); - } -}; - -struct NiLines : NiGeometry -{ - NiLinesDataPtr data; - - void read(NIFStream *nif) override - { - Node::read(nif); - data.read(nif); - skin.read(nif); - } - - void post(NIFFile *nif) override - { - Node::post(nif); - data.post(nif); - skin.post(nif); - if (!skin.empty()) - nif->setUseSkinning(true); + NiTriShape::read(nif); + lod0 = nif->getUInt(); + lod1 = nif->getUInt(); + lod2 = nif->getUInt(); } }; +struct NiTriStrips : NiGeometry {}; +struct NiLines : NiGeometry {}; +struct NiParticles : NiGeometry { }; struct NiCamera : Node { @@ -401,25 +375,6 @@ struct NiCamera : Node } }; -struct NiParticles : NiGeometry -{ - NiParticlesDataPtr data; - void read(NIFStream *nif) override - { - Node::read(nif); - data.read(nif); - skin.read(nif); - materialData.read(nif); - } - - void post(NIFFile *nif) override - { - Node::post(nif); - data.post(nif); - skin.post(nif); - } -}; - // A node used as the base to switch between child nodes, such as for LOD levels. struct NiSwitchNode : public NiNode { diff --git a/components/nif/property.cpp b/components/nif/property.cpp index e6ae71c241..d5357e1230 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -99,6 +99,25 @@ void NiTexturingProperty::post(NIFFile *nif) shaderTextures[i].post(nif); } +void BSShaderProperty::read(NIFStream *nif) +{ + NiShadeProperty::read(nif); + if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + { + type = nif->getUInt(); + flags1 = nif->getUInt(); + flags2 = nif->getUInt(); + envMapIntensity = nif->getFloat(); + } +} + +void BSShaderLightingProperty::read(NIFStream *nif) +{ + BSShaderProperty::read(nif); + if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + clamp = nif->getUInt(); +} + void NiFogProperty::read(NIFStream *nif) { Property::read(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index c821b7c37e..813a1888d8 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -118,6 +118,19 @@ struct NiShadeProperty : public Property } }; +struct BSShaderProperty : public NiShadeProperty +{ + unsigned int type{0u}, flags1{0u}, flags2{0u}; + float envMapIntensity{0.f}; + void read(NIFStream *nif) override; +}; + +struct BSShaderLightingProperty : public BSShaderProperty +{ + unsigned int clamp{0u}; + void read(NIFStream *nif) override; +}; + struct NiDitherProperty : public Property { unsigned short flags; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 2217c588ee..efacd82462 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -118,7 +118,11 @@ enum RecordType RC_NiFloatInterpolator, RC_NiPoint3Interpolator, RC_NiBoolInterpolator, - RC_NiTransformInterpolator + RC_NiTransformInterpolator, + RC_NiColorInterpolator, + RC_BSShaderTextureSet, + RC_BSLODTriShape, + RC_BSShaderProperty }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index ed8f7ef6b9..4792895e2d 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -134,19 +134,20 @@ struct NiMorphData; class NiPixelData; class NiColorData; struct NiKeyframeData; -class NiTriShapeData; class NiTriStripsData; class NiSkinInstance; class NiSourceTexture; -class NiParticlesData; class NiPalette; struct NiParticleModifier; -struct NiLinesData; struct NiBoolData; struct NiSkinPartition; struct NiFloatInterpolator; struct NiPoint3Interpolator; struct NiTransformInterpolator; +struct BSShaderTextureSet; +struct NiGeometryData; +struct BSShaderProperty; +class NiAlphaProperty; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -161,12 +162,8 @@ using NiPixelDataPtr = RecordPtrT; using NiFloatDataPtr = RecordPtrT; using NiColorDataPtr = RecordPtrT; using NiKeyframeDataPtr = RecordPtrT; -using NiTriShapeDataPtr = RecordPtrT; -using NiTriStripsDataPtr = RecordPtrT; -using NiLinesDataPtr = RecordPtrT; using NiSkinInstancePtr = RecordPtrT; using NiSourceTexturePtr = RecordPtrT; -using NiParticlesDataPtr = RecordPtrT; using NiPalettePtr = RecordPtrT; using NiParticleModifierPtr = RecordPtrT; using NiBoolDataPtr = RecordPtrT; @@ -174,12 +171,17 @@ using NiSkinPartitionPtr = RecordPtrT; using NiFloatInterpolatorPtr = RecordPtrT; using NiPoint3InterpolatorPtr = RecordPtrT; using NiTransformInterpolatorPtr = RecordPtrT; +using BSShaderTextureSetPtr = RecordPtrT; +using NiGeometryDataPtr = RecordPtrT; +using BSShaderPropertyPtr = RecordPtrT; +using NiAlphaPropertyPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; using ExtraList = RecordListT; using NiSourceTextureList = RecordListT; using NiFloatInterpolatorList = RecordListT; +using NiTriStripsDataList = RecordListT; } // Namespace #endif diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index b24d08fd8c..d72cef1940 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -34,11 +34,10 @@ bool pathFileNameStartsWithX(const std::string& path) void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform) { - mesh.preallocateVertices(static_cast(data.vertices.size())); - mesh.preallocateIndices(static_cast(data.triangles.size())); - const std::vector &vertices = data.vertices; const std::vector &triangles = data.triangles; + mesh.preallocateVertices(static_cast(vertices.size())); + mesh.preallocateIndices(static_cast(triangles.size())); for (std::size_t i = 0; i < triangles.size(); i += 3) { @@ -54,8 +53,6 @@ void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, co { const std::vector &vertices = data.vertices; const std::vector> &strips = data.strips; - if (vertices.empty() || strips.empty()) - return; mesh.preallocateVertices(static_cast(vertices.size())); int numTriangles = 0; for (const std::vector& strip : strips) @@ -102,12 +99,12 @@ void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, co } } -void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform = osg::Matrixf()) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiGeometry* geometry, const osg::Matrixf &transform = osg::Matrixf()) { - if (nifNode->recType == Nif::RC_NiTriShape) - fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); - else if (nifNode->recType == Nif::RC_NiTriStrips) - fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); + if (geometry->recType == Nif::RC_NiTriShape || geometry->recType == Nif::RC_BSLODTriShape) + fillTriangleMesh(mesh, static_cast(geometry->data.get()), transform); + else if (geometry->recType == Nif::RC_NiTriStrips) + fillTriangleMesh(mesh, static_cast(geometry->data.get()), transform); } } @@ -312,7 +309,9 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. // (occurs in tr_ex_imp_wall_arch_04.nif) - if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape || node->recType == Nif::RC_NiTriStrips)) + if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape + || node->recType == Nif::RC_NiTriStrips + || node->recType == Nif::RC_BSLODTriShape)) { handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid); } @@ -341,23 +340,31 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if ((flags & 0x800)) return; - if (nifNode->recType == Nif::RC_NiTriShape) + auto niGeometry = static_cast(nifNode); + if (niGeometry->data.empty() || niGeometry->data->vertices.empty()) + return; + + if (niGeometry->recType == Nif::RC_NiTriShape || niGeometry->recType == Nif::RC_BSLODTriShape) { - const Nif::NiTriShape* shape = static_cast(nifNode); - if (!shape->skin.empty()) - isAnimated = false; - if (shape->data.empty() || shape->data->triangles.empty()) + if (niGeometry->data->recType != Nif::RC_NiTriShapeData) + return; + + auto data = static_cast(niGeometry->data.getPtr()); + if (data->triangles.empty()) return; } - else + else if (niGeometry->recType == Nif::RC_NiTriStrips) { - const Nif::NiTriStrips* shape = static_cast(nifNode); - if (!shape->skin.empty()) - isAnimated = false; - if (shape->data.empty() || shape->data->strips.empty()) + if (niGeometry->data->recType != Nif::RC_NiTriStripsData) + return; + + auto data = static_cast(niGeometry->data.getPtr()); + if (data->strips.empty()) return; } + if (!niGeometry->skin.empty()) + isAnimated = false; if (isAnimated) { @@ -366,7 +373,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons std::unique_ptr childMesh(new btTriangleMesh); - fillTriangleMesh(*childMesh, nifNode); + fillTriangleMesh(*childMesh, niGeometry); std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); childMesh.release(); @@ -394,7 +401,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if (!mAvoidStaticMesh) mAvoidStaticMesh.reset(new btTriangleMesh(false)); - fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform); + fillTriangleMesh(*mAvoidStaticMesh, niGeometry, transform); } else { @@ -402,7 +409,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position - fillTriangleMesh(*mStaticMesh, nifNode, transform); + fillTriangleMesh(*mStaticMesh, niGeometry, transform); } } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 0063b2ec0f..96beafcbb8 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -66,7 +66,9 @@ namespace NifOsg std::conjunction_v< std::disjunction< std::is_same, - std::is_same + std::is_same, + std::is_same, + std::is_same >, std::is_same >, diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 0db15237ec..5f68b12291 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -67,6 +67,7 @@ namespace case Nif::RC_NiTriShape: case Nif::RC_NiTriStrips: case Nif::RC_NiLines: + case Nif::RC_BSLODTriShape: return true; } return false; @@ -95,6 +96,15 @@ namespace } } } + + auto geometry = dynamic_cast(nifNode); + if (geometry) + { + if (!geometry->shaderprop.empty()) + out.emplace_back(geometry->shaderprop.getPtr()); + if (!geometry->alphaprop.empty()) + out.emplace_back(geometry->alphaprop.getPtr()); + } } // NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale @@ -365,6 +375,11 @@ namespace NifOsg handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags); } } + + auto geometry = dynamic_cast(nifNode); + // NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property + if (geometry && !geometry->shaderprop.empty()) + handleProperty(geometry->shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags); } void setupController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int animflags) @@ -466,19 +481,12 @@ namespace NifOsg texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - osg::ref_ptr texEnv = new osg::TexEnvCombine; - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); - texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); - int texUnit = 3; // FIXME osg::StateSet* stateset = node->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, texGen, osg::StateAttribute::ON); - stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("envMapColor", osg::Vec4f(1,1,1,1))); } @@ -946,11 +954,11 @@ namespace NifOsg // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { - const auto particleNode = static_cast(nifNode); - if (particleNode->data.empty()) + auto particleNode = static_cast(nifNode); + if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData) return; - const Nif::NiParticlesData* particledata = particleNode->data.getPtr(); + auto particledata = static_cast(particleNode->data.getPtr()); osg::BoundingBox box; @@ -963,6 +971,9 @@ namespace NifOsg if (particle.lifespan <= 0) continue; + if (particle.vertex >= particledata->vertices.size()) + continue; + ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); osgParticle::Particle* created = partsys->createParticle(&particletemplate); @@ -971,16 +982,16 @@ namespace NifOsg // Note this position and velocity is not correct for a particle system with absolute reference frame, // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager. created->setVelocity(particle.velocity); - const osg::Vec3f& position = particledata->vertices.at(particle.vertex); + const osg::Vec3f& position = particledata->vertices[particle.vertex]; created->setPosition(position); osg::Vec4f partcolor (1.f,1.f,1.f,1.f); - if (particle.vertex < int(particledata->colors.size())) - partcolor = particledata->colors.at(particle.vertex); + if (particle.vertex < particledata->colors.size()) + partcolor = particledata->colors[particle.vertex]; float size = partctrl->size; - if (particle.vertex < int(particledata->sizes.size())) - size *= particledata->sizes.at(particle.vertex); + if (particle.vertex < particledata->sizes.size()) + size *= particledata->sizes[particle.vertex]; created->setSizeRange(osgParticle::rangef(size, size)); box.expandBy(osg::BoundingSphere(position, size)); @@ -1177,51 +1188,50 @@ namespace NifOsg void handleNiGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - const Nif::NiGeometryData* niGeometryData = nullptr; - if (nifNode->recType == Nif::RC_NiTriShape) + const Nif::NiGeometry* niGeometry = static_cast(nifNode); + if (niGeometry->data.empty()) + return; + const Nif::NiGeometryData* niGeometryData = niGeometry->data.getPtr(); + + if (niGeometry->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_BSLODTriShape) { - const Nif::NiTriShape* triShape = static_cast(nifNode); - if (!triShape->data.empty()) - { - const Nif::NiTriShapeData* data = triShape->data.getPtr(); - niGeometryData = static_cast(data); - if (!data->triangles.empty()) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), - (unsigned short*)data->triangles.data())); - } + if (niGeometryData->recType != Nif::RC_NiTriShapeData) + return; + auto triangles = static_cast(niGeometryData)->triangles; + if (triangles.empty()) + return; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, triangles.size(), + (unsigned short*)triangles.data())); } - else if (nifNode->recType == Nif::RC_NiTriStrips) + else if (niGeometry->recType == Nif::RC_NiTriStrips) { - const Nif::NiTriStrips* triStrips = static_cast(nifNode); - if (!triStrips->data.empty()) + if (niGeometryData->recType != Nif::RC_NiTriStripsData) + return; + auto data = static_cast(niGeometryData); + bool hasGeometry = false; + for (const auto& strip : data->strips) { - const Nif::NiTriStripsData* data = triStrips->data.getPtr(); - niGeometryData = static_cast(data); - if (!data->strips.empty()) - { - for (const auto& strip : data->strips) - { - if (strip.size() >= 3) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), - (unsigned short*)strip.data())); - } - } + if (strip.size() < 3) + continue; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), + (unsigned short*)strip.data())); + hasGeometry = true; } + if (!hasGeometry) + return; } - else if (nifNode->recType == Nif::RC_NiLines) + else if (niGeometry->recType == Nif::RC_NiLines) { - const Nif::NiLines* lines = static_cast(nifNode); - if (!lines->data.empty()) - { - const Nif::NiLinesData* data = lines->data.getPtr(); - niGeometryData = static_cast(data); - const auto& line = data->lines; - if (!line.empty()) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), (unsigned short*)line.data())); - } + if (niGeometryData->recType != Nif::RC_NiLinesData) + return; + auto data = static_cast(niGeometryData); + const auto& line = data->lines; + if (line.empty()) + return; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), + (unsigned short*)line.data())); } - if (niGeometryData) - handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name); + handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name); // osg::Material properties are handled here for two reasons: // - if there are no vertex colors, we need to disable colorMode. @@ -1229,7 +1239,7 @@ namespace NifOsg // above the actual renderable would be tedious. std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, niGeometryData && !niGeometryData->colors.empty(), animflags); + applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->colors.empty(), animflags); } void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) @@ -1481,6 +1491,17 @@ namespace NifOsg return image; } + osg::ref_ptr createEmissiveTexEnv() + { + osg::ref_ptr texEnv(new osg::TexEnvCombine); + texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); + texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); + texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); + texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); + return texEnv; + } + void handleTextureProperty(const Nif::NiTexturingProperty* texprop, const std::string& nodeName, osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { if (!boundTextures.empty()) @@ -1567,14 +1588,7 @@ namespace NifOsg if (i == Nif::NiTexturingProperty::GlowTexture) { - osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); - texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); - - stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); } else if (i == Nif::NiTexturingProperty::DarkTexture) {