Deduplicate NifLoader texture attachment

Handle non-existent shader materials more gracefully
Deduplicate shader material drawable property handling
pull/3235/head
Alexei Kotov 8 months ago
parent e680123482
commit 4ccf9c1917

@ -520,28 +520,18 @@ namespace NifOsg
sequenceNode->setMode(osg::Sequence::START); sequenceNode->setMode(osg::Sequence::START);
} }
osg::ref_ptr<osg::Image> handleSourceTexture(const Nif::NiSourceTexture* st) osg::ref_ptr<osg::Image> handleSourceTexture(const Nif::NiSourceTexture* st) const
{ {
if (!st) if (st)
return nullptr;
osg::ref_ptr<osg::Image> image;
if (st->mExternal)
{
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->mFile, mImageManager->getVFS());
image = mImageManager->getImage(filename);
}
else if (!st->mData.empty())
{ {
image = handleInternalTexture(st->mData.getPtr()); if (st->mExternal)
return getTextureImage(st->mFile);
if (!st->mData.empty())
return handleInternalTexture(st->mData.getPtr());
} }
return image;
}
void handleTextureWrapping(osg::Texture2D* texture, bool wrapS, bool wrapT) return nullptr;
{
texture->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
} }
bool handleEffect(const Nif::NiAVObject* nifNode, osg::StateSet* stateset) bool handleEffect(const Nif::NiAVObject* nifNode, osg::StateSet* stateset)
@ -587,16 +577,12 @@ namespace NifOsg
return false; return false;
} }
osg::ref_ptr<osg::Image> image(handleSourceTexture(textureEffect->mTexture.getPtr())); const unsigned int uvSet = 0;
osg::ref_ptr<osg::Texture2D> texture2d(new osg::Texture2D(image)); const unsigned int texUnit = 3; // FIXME
if (image) std::vector<unsigned int> boundTextures;
texture2d->setTextureSize(image->s(), image->t()); boundTextures.resize(3); // Dummy vector for attachNiSourceTexture
texture2d->setName("envMap"); attachNiSourceTexture("envMap", textureEffect->mTexture.getPtr(), textureEffect->wrapS(),
handleTextureWrapping(texture2d, textureEffect->wrapS(), textureEffect->wrapT()); textureEffect->wrapT(), uvSet, stateset, boundTextures);
int texUnit = 3; // FIXME
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(texUnit, texGen, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, texGen, osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON);
@ -1026,6 +1012,54 @@ namespace NifOsg
} }
} }
osg::ref_ptr<osg::Image> getTextureImage(std::string_view path) const
{
if (!mImageManager)
return nullptr;
std::string filename = Misc::ResourceHelpers::correctTexturePath(path, mImageManager->getVFS());
return mImageManager->getImage(filename);
}
osg::ref_ptr<osg::Texture2D> attachTexture(const std::string& name, osg::ref_ptr<osg::Image> image, bool wrapS,
bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) const
{
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
if (image)
texture2d->setTextureSize(image->s(), image->t());
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);
unsigned int texUnit = boundTextures.size();
if (stateset)
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
texture2d->setName(name);
boundTextures.emplace_back(uvSet);
return texture2d;
}
osg::ref_ptr<osg::Texture2D> attachExternalTexture(const std::string& name, const std::string& path, bool wrapS,
bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) const
{
return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures);
}
osg::ref_ptr<osg::Texture2D> attachNiSourceTexture(const std::string& name, const Nif::NiSourceTexture* st,
bool wrapS, bool wrapT, unsigned int uvSet, osg::StateSet* stateset,
std::vector<unsigned int>& boundTextures) const
{
return attachTexture(name, handleSourceTexture(st), wrapS, wrapT, uvSet, stateset, boundTextures);
}
static void clearBoundTextures(osg::StateSet* stateset, std::vector<unsigned int>& boundTextures)
{
if (!boundTextures.empty())
{
for (unsigned int i = 0; i < boundTextures.size(); ++i)
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
boundTextures.clear();
}
}
void handleTextureControllers(const Nif::NiProperty* texProperty, void handleTextureControllers(const Nif::NiProperty* texProperty,
SceneUtil::CompositeStateSetUpdater* composite, osg::StateSet* stateset, int animflags) SceneUtil::CompositeStateSetUpdater* composite, osg::StateSet* stateset, int animflags)
{ {
@ -1056,17 +1090,16 @@ namespace NifOsg
wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); wrapT = inherit->getWrap(osg::Texture2D::WRAP_T);
} }
const unsigned int uvSet = 0;
std::vector<unsigned int> boundTextures; // Dummy list for attachTexture
for (const auto& source : flipctrl->mSources) for (const auto& source : flipctrl->mSources)
{ {
if (source.empty()) if (source.empty())
continue; continue;
osg::ref_ptr<osg::Image> image(handleSourceTexture(source.getPtr())); // NB: not changing the stateset
osg::ref_ptr<osg::Texture2D> texture(new osg::Texture2D(image)); osg::ref_ptr<osg::Texture2D> texture
if (image) = attachNiSourceTexture({}, source.getPtr(), wrapS, wrapT, uvSet, nullptr, boundTextures);
texture->setTextureSize(image->s(), image->t());
texture->setWrap(osg::Texture::WRAP_S, wrapS);
texture->setWrap(osg::Texture::WRAP_T, wrapT);
textures.push_back(texture); textures.push_back(texture);
} }
osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures)); osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures));
@ -1811,7 +1844,7 @@ namespace NifOsg
} }
} }
osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData) osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData) const
{ {
if (pixelData->mMipmaps.empty()) if (pixelData->mMipmaps.empty())
return nullptr; return nullptr;
@ -1946,7 +1979,7 @@ namespace NifOsg
return image; return image;
} }
osg::ref_ptr<osg::TexEnvCombine> createEmissiveTexEnv() static osg::ref_ptr<osg::TexEnvCombine> createEmissiveTexEnv()
{ {
osg::ref_ptr<osg::TexEnvCombine> texEnv(new osg::TexEnvCombine); osg::ref_ptr<osg::TexEnvCombine> texEnv(new osg::TexEnvCombine);
// Sum the previous colour and the emissive colour. // Sum the previous colour and the emissive colour.
@ -1979,31 +2012,40 @@ namespace NifOsg
osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite, osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite,
std::vector<unsigned int>& boundTextures, int animflags) std::vector<unsigned int>& boundTextures, int animflags)
{ {
if (!boundTextures.empty()) // overriding a parent NiTexturingProperty, so remove what was previously bound
{ clearBoundTextures(stateset, boundTextures);
// overriding a parent NiTexturingProperty, so remove what was previously bound
for (unsigned int i = 0; i < boundTextures.size(); ++i)
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
boundTextures.clear();
}
// If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the // If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the
// shadow casting shader will need to be updated accordingly. // shadow casting shader will need to be updated accordingly.
for (size_t i = 0; i < texprop->mTextures.size(); ++i) for (size_t i = 0; i < texprop->mTextures.size(); ++i)
{ {
if (texprop->mTextures[i].mEnabled const Nif::NiTexturingProperty::Texture& tex = texprop->mTextures[i];
|| (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty())) if (tex.mEnabled || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty()))
{ {
std::string textureName;
switch (i) switch (i)
{ {
// These are handled later on // These are handled later on
case Nif::NiTexturingProperty::BaseTexture: case Nif::NiTexturingProperty::BaseTexture:
textureName = "diffuseMap";
break;
case Nif::NiTexturingProperty::GlowTexture: case Nif::NiTexturingProperty::GlowTexture:
textureName = "glowMap";
break;
case Nif::NiTexturingProperty::DarkTexture: case Nif::NiTexturingProperty::DarkTexture:
textureName = "darkMap";
break;
case Nif::NiTexturingProperty::BumpTexture: case Nif::NiTexturingProperty::BumpTexture:
textureName = "bumpMap";
break;
case Nif::NiTexturingProperty::DetailTexture: case Nif::NiTexturingProperty::DetailTexture:
textureName = "detailMap";
break;
case Nif::NiTexturingProperty::DecalTexture: case Nif::NiTexturingProperty::DecalTexture:
textureName = "decalMap";
break;
case Nif::NiTexturingProperty::GlossTexture: case Nif::NiTexturingProperty::GlossTexture:
textureName = "glossMap";
break; break;
default: default:
{ {
@ -2013,12 +2055,9 @@ namespace NifOsg
} }
} }
unsigned int uvSet = 0; const unsigned int texUnit = boundTextures.size();
// create a new texture, will later attempt to share using the SharedStateManager if (tex.mEnabled)
osg::ref_ptr<osg::Texture2D> texture2d;
if (texprop->mTextures[i].mEnabled)
{ {
const Nif::NiTexturingProperty::Texture& tex = texprop->mTextures[i];
if (tex.mSourceTexture.empty() && texprop->mController.empty()) if (tex.mSourceTexture.empty() && texprop->mController.empty())
{ {
if (i == 0) if (i == 0)
@ -2028,32 +2067,18 @@ namespace NifOsg
} }
if (!tex.mSourceTexture.empty()) if (!tex.mSourceTexture.empty())
{ attachNiSourceTexture(textureName, tex.mSourceTexture.getPtr(), tex.wrapS(), tex.wrapT(),
const Nif::NiSourceTexture* st = tex.mSourceTexture.getPtr(); tex.mUVSet, stateset, boundTextures);
osg::ref_ptr<osg::Image> image = handleSourceTexture(st);
texture2d = new osg::Texture2D(image);
if (image)
texture2d->setTextureSize(image->s(), image->t());
}
else else
texture2d = new osg::Texture2D; attachTexture(
textureName, nullptr, tex.wrapS(), tex.wrapT(), tex.mUVSet, stateset, boundTextures);
handleTextureWrapping(texture2d, tex.wrapS(), tex.wrapT());
uvSet = tex.mUVSet;
} }
else else
{ {
// Texture only comes from NiFlipController, so tex is ignored, set defaults // Texture only comes from NiFlipController, so tex is ignored, set defaults
texture2d = new osg::Texture2D; attachTexture(textureName, nullptr, true, true, 0, stateset, boundTextures);
handleTextureWrapping(texture2d, true, true);
uvSet = 0;
} }
unsigned int texUnit = boundTextures.size();
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
if (i == Nif::NiTexturingProperty::GlowTexture) if (i == Nif::NiTexturingProperty::GlowTexture)
{ {
stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON);
@ -2121,41 +2146,12 @@ namespace NifOsg
texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON);
} }
switch (i)
{
case Nif::NiTexturingProperty::BaseTexture:
texture2d->setName("diffuseMap");
break;
case Nif::NiTexturingProperty::BumpTexture:
texture2d->setName("bumpMap");
break;
case Nif::NiTexturingProperty::GlowTexture:
texture2d->setName("emissiveMap");
break;
case Nif::NiTexturingProperty::DarkTexture:
texture2d->setName("darkMap");
break;
case Nif::NiTexturingProperty::DetailTexture:
texture2d->setName("detailMap");
break;
case Nif::NiTexturingProperty::DecalTexture:
texture2d->setName("decalMap");
break;
case Nif::NiTexturingProperty::GlossTexture:
texture2d->setName("glossMap");
break;
default:
break;
}
boundTextures.push_back(uvSet);
} }
} }
handleTextureControllers(texprop, composite, stateset, animflags); handleTextureControllers(texprop, composite, stateset, animflags);
} }
Bgsm::MaterialFilePtr getShaderMaterial(const std::string& path) Bgsm::MaterialFilePtr getShaderMaterial(std::string_view path) const
{ {
if (!mMaterialManager) if (!mMaterialManager)
return nullptr; return nullptr;
@ -2164,81 +2160,43 @@ namespace NifOsg
return nullptr; return nullptr;
std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialManager->getVFS()); std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialManager->getVFS());
return mMaterialManager->get(VFS::Path::Normalized(normalizedPath)); try
{
return mMaterialManager->get(VFS::Path::Normalized(normalizedPath));
}
catch (std::exception& e)
{
Log(Debug::Error) << "Failed to load shader material: " << e.what();
return nullptr;
}
} }
void handleShaderMaterial( void handleShaderMaterialNodeProperties(
Bgsm::MaterialFilePtr material, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) Bgsm::MaterialFilePtr material, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures)
{ {
const unsigned int uvSet = 0;
const bool wrapS = (material->mClamp >> 1) & 0x1;
const bool wrapT = material->mClamp & 0x1;
if (material->mShaderType == Bgsm::ShaderType::Lighting) if (material->mShaderType == Bgsm::ShaderType::Lighting)
{ {
const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material.get()); const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material.get());
if (!boundTextures.empty())
{
for (unsigned int i = 0; i < boundTextures.size(); ++i)
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
boundTextures.clear();
}
const unsigned int uvSet = 0;
if (!bgsm->mDiffuseMap.empty()) if (!bgsm->mDiffuseMap.empty())
{ attachExternalTexture(
std::string filename "diffuseMap", bgsm->mDiffuseMap, wrapS, wrapT, uvSet, stateset, boundTextures);
= Misc::ResourceHelpers::correctTexturePath(bgsm->mDiffuseMap, mImageManager->getVFS());
osg::ref_ptr<osg::Image> image = mImageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
if (image)
texture2d->setTextureSize(image->s(), image->t());
handleTextureWrapping(texture2d, (bgsm->mClamp >> 1) & 0x1, bgsm->mClamp & 0x1);
unsigned int texUnit = boundTextures.size();
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
texture2d->setName("diffuseMap");
boundTextures.emplace_back(uvSet);
}
if (!bgsm->mNormalMap.empty()) if (!bgsm->mNormalMap.empty())
{ attachExternalTexture("normalMap", bgsm->mNormalMap, wrapS, wrapT, uvSet, stateset, boundTextures);
std::string filename
= Misc::ResourceHelpers::correctTexturePath(bgsm->mNormalMap, mImageManager->getVFS());
osg::ref_ptr<osg::Image> image = mImageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
if (image)
texture2d->setTextureSize(image->s(), image->t());
handleTextureWrapping(texture2d, (bgsm->mClamp >> 1) & 0x1, bgsm->mClamp & 0x1);
unsigned int texUnit = boundTextures.size();
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
texture2d->setName("normalMap");
boundTextures.emplace_back(uvSet);
}
if (bgsm->mTree) if (bgsm->mTree)
stateset->addUniform(new osg::Uniform("useTreeAnim", true)); stateset->addUniform(new osg::Uniform("useTreeAnim", true));
} }
else else if (material->mShaderType == Bgsm::ShaderType::Effect)
{ {
const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material.get()); const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material.get());
if (!bgem->mBaseMap.empty()) if (!bgem->mBaseMap.empty())
{ attachExternalTexture("diffuseMap", bgem->mBaseMap, wrapS, wrapT, uvSet, stateset, boundTextures);
if (!boundTextures.empty())
{
for (unsigned int i = 0; i < boundTextures.size(); ++i)
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
boundTextures.clear();
}
std::string filename
= Misc::ResourceHelpers::correctTexturePath(bgem->mBaseMap, mImageManager->getVFS());
osg::ref_ptr<osg::Image> image = mImageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
texture2d->setName("diffuseMap");
if (image)
texture2d->setTextureSize(image->s(), image->t());
handleTextureWrapping(texture2d, (bgem->mClamp >> 1) & 0x1, bgem->mClamp & 0x1);
const unsigned int texUnit = 0;
const unsigned int uvSet = 0;
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
boundTextures.push_back(uvSet);
}
bool useFalloff = bgem->mFalloff; bool useFalloff = bgem->mFalloff;
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
@ -2251,16 +2209,55 @@ namespace NifOsg
handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite); handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite);
} }
void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, void handleShaderMaterialDrawableProperties(
const std::string& nodeName, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) Bgsm::MaterialFilePtr shaderMat, osg::ref_ptr<osg::Material> mat, osg::Node& node, bool& hasSortAlpha)
{ {
if (!boundTextures.empty()) mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency);
if (shaderMat->mAlphaTest)
{ {
for (unsigned int i = 0; i < boundTextures.size(); ++i) osg::StateSet* stateset = node.getOrCreateStateSet();
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); osg::ref_ptr<osg::AlphaFunc> alphaFunc(
boundTextures.clear(); new osg::AlphaFunc(osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f));
alphaFunc = shareAttribute(alphaFunc);
stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);
}
if (shaderMat->mAlphaBlend)
{
osg::StateSet* stateset = node.getOrCreateStateSet();
osg::ref_ptr<osg::BlendFunc> blendFunc(new osg::BlendFunc(
getBlendMode(shaderMat->mSourceBlendMode), getBlendMode(shaderMat->mDestinationBlendMode)));
blendFunc = shareAttribute(blendFunc);
stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
hasSortAlpha = true;
}
if (shaderMat->mDecal)
{
osg::StateSet* stateset = node.getOrCreateStateSet();
if (!mPushedSorter && !hasSortAlpha)
stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT");
osg::ref_ptr<osg::PolygonOffset> polygonOffset(new osg::PolygonOffset);
polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f);
polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f);
stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON);
}
if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting)
{
auto bgsm = static_cast<const Bgsm::BGSMFile*>(shaderMat.get());
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mEmittanceColor, 1.f));
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mSpecularColor, 1.f));
} }
else if (shaderMat->mShaderType == Bgsm::ShaderType::Effect)
{
auto bgem = static_cast<const Bgsm::BGEMFile*>(shaderMat.get());
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgem->mEmittanceColor, 1.f));
if (bgem->mSoft)
SceneUtil::setupSoftEffect(node, bgem->mSoftDepth, true, bgem->mSoftDepth);
}
}
void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, bool wrapS, bool wrapT,
const std::string& nodeName, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures)
{
const unsigned int uvSet = 0; const unsigned int uvSet = 0;
for (size_t i = 0; i < textureSet->mTextures.size(); ++i) for (size_t i = 0; i < textureSet->mTextures.size(); ++i)
@ -2270,8 +2267,16 @@ namespace NifOsg
switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i)) switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
{ {
case Nif::BSShaderTextureSet::TextureType::Base: case Nif::BSShaderTextureSet::TextureType::Base:
attachExternalTexture(
"diffuseMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures);
break;
case Nif::BSShaderTextureSet::TextureType::Normal: case Nif::BSShaderTextureSet::TextureType::Normal:
attachExternalTexture(
"normalMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures);
break;
case Nif::BSShaderTextureSet::TextureType::Glow: case Nif::BSShaderTextureSet::TextureType::Glow:
attachExternalTexture(
"emissiveMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures);
break; break;
default: default:
{ {
@ -2280,31 +2285,6 @@ namespace NifOsg
continue; continue;
} }
} }
std::string filename
= Misc::ResourceHelpers::correctTexturePath(textureSet->mTextures[i], mImageManager->getVFS());
osg::ref_ptr<osg::Image> image = mImageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
if (image)
texture2d->setTextureSize(image->s(), image->t());
handleTextureWrapping(texture2d, (clamp >> 1) & 0x1, clamp & 0x1);
unsigned int texUnit = boundTextures.size();
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
// BSShaderTextureSet presence means there's no need for FFP support for the affected node
switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
{
case Nif::BSShaderTextureSet::TextureType::Base:
texture2d->setName("diffuseMap");
break;
case Nif::BSShaderTextureSet::TextureType::Normal:
texture2d->setName("normalMap");
break;
case Nif::BSShaderTextureSet::TextureType::Glow:
texture2d->setName("emissiveMap");
break;
default:
break;
}
boundTextures.emplace_back(uvSet);
} }
} }
@ -2458,11 +2438,12 @@ namespace NifOsg
node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType))); node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType)));
node->setUserValue("shaderRequired", shaderRequired); node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
clearBoundTextures(stateset, boundTextures);
const bool wrapS = (texprop->mClamp >> 1) & 0x1;
const bool wrapT = texprop->mClamp & 0x1;
if (!texprop->mTextureSet.empty()) if (!texprop->mTextureSet.empty())
{ handleTextureSet(
auto textureSet = texprop->mTextureSet.getPtr(); texprop->mTextureSet.getPtr(), wrapS, wrapT, node->getName(), stateset, boundTextures);
handleTextureSet(textureSet, texprop->mClamp, node->getName(), stateset, boundTextures);
}
handleTextureControllers(texprop, composite, stateset, animflags); handleTextureControllers(texprop, composite, stateset, animflags);
if (texprop->refraction()) if (texprop->refraction())
SceneUtil::setupDistortion(*node, texprop->mRefraction.mStrength); SceneUtil::setupDistortion(*node, texprop->mRefraction.mStrength);
@ -2476,31 +2457,17 @@ namespace NifOsg
node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType))); node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType)));
node->setUserValue("shaderRequired", shaderRequired); node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
clearBoundTextures(stateset, boundTextures);
if (!texprop->mFilename.empty()) if (!texprop->mFilename.empty())
{ {
if (!boundTextures.empty())
{
for (unsigned int i = 0; i < boundTextures.size(); ++i)
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
boundTextures.clear();
}
std::string filename
= Misc::ResourceHelpers::correctTexturePath(texprop->mFilename, mImageManager->getVFS());
osg::ref_ptr<osg::Image> image = mImageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
texture2d->setName("diffuseMap");
if (image)
texture2d->setTextureSize(image->s(), image->t());
handleTextureWrapping(texture2d, texprop->wrapS(), texprop->wrapT());
const unsigned int texUnit = 0;
const unsigned int uvSet = 0; const unsigned int uvSet = 0;
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); attachExternalTexture("diffuseMap", texprop->mFilename, texprop->wrapS(), texprop->wrapT(),
boundTextures.push_back(uvSet); uvSet, stateset, boundTextures);
if (mBethVersion >= 27) }
{ if (mBethVersion >= 27)
useFalloff = true; {
stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams)); useFalloff = true;
} stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams));
} }
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
handleTextureControllers(texprop, composite, stateset, animflags); handleTextureControllers(texprop, composite, stateset, animflags);
@ -2514,15 +2481,17 @@ namespace NifOsg
node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType))); node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType)));
node->setUserValue("shaderRequired", shaderRequired); node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); clearBoundTextures(stateset, boundTextures);
if (material) if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName))
{ {
handleShaderMaterial(material, stateset, boundTextures); handleShaderMaterialNodeProperties(material, stateset, boundTextures);
break; break;
} }
const bool wrapS = (texprop->mClamp >> 1) & 0x1;
const bool wrapT = texprop->mClamp & 0x1;
if (!texprop->mTextureSet.empty()) if (!texprop->mTextureSet.empty())
handleTextureSet( handleTextureSet(
texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset, boundTextures); texprop->mTextureSet.getPtr(), wrapS, wrapT, node->getName(), stateset, boundTextures);
handleTextureControllers(texprop, composite, stateset, animflags); handleTextureControllers(texprop, composite, stateset, animflags);
if (texprop->doubleSided()) if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
@ -2541,33 +2510,20 @@ namespace NifOsg
node->setUserValue("shaderPrefix", std::string("bs/nolighting")); node->setUserValue("shaderPrefix", std::string("bs/nolighting"));
node->setUserValue("shaderRequired", shaderRequired); node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); clearBoundTextures(stateset, boundTextures);
if (material) if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName))
{ {
handleShaderMaterial(material, stateset, boundTextures); handleShaderMaterialNodeProperties(material, stateset, boundTextures);
break; break;
} }
if (!texprop->mSourceTexture.empty()) if (!texprop->mSourceTexture.empty())
{ {
if (!boundTextures.empty())
{
for (unsigned int i = 0; i < boundTextures.size(); ++i)
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
boundTextures.clear();
}
std::string filename = Misc::ResourceHelpers::correctTexturePath(
texprop->mSourceTexture, mImageManager->getVFS());
osg::ref_ptr<osg::Image> image = mImageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
texture2d->setName("diffuseMap");
if (image)
texture2d->setTextureSize(image->s(), image->t());
handleTextureWrapping(texture2d, (texprop->mClamp >> 1) & 0x1, texprop->mClamp & 0x1);
const unsigned int texUnit = 0;
const unsigned int uvSet = 0; const unsigned int uvSet = 0;
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); const bool wrapS = (texprop->mClamp >> 1) & 0x1;
boundTextures.push_back(uvSet); const bool wrapT = texprop->mClamp & 0x1;
unsigned int texUnit = boundTextures.size();
attachExternalTexture(
"diffuseMap", texprop->mSourceTexture, wrapS, wrapT, uvSet, stateset, boundTextures);
{ {
osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat); osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat);
// This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController) // This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController)
@ -2819,40 +2775,11 @@ namespace NifOsg
case Nif::RC_BSLightingShaderProperty: case Nif::RC_BSLightingShaderProperty:
{ {
auto shaderprop = static_cast<const Nif::BSLightingShaderProperty*>(property); auto shaderprop = static_cast<const Nif::BSLightingShaderProperty*>(property);
Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName); if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName))
if (shaderMat)
{ {
mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha);
if (shaderMat->mAlphaTest) if (shaderMat->mAlphaBlend && !mPushedSorter)
{ setBin_Transparent(node->getOrCreateStateSet());
osg::StateSet* stateset = node->getOrCreateStateSet();
osg::ref_ptr<osg::AlphaFunc> alphaFunc(new osg::AlphaFunc(
osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f));
alphaFunc = shareAttribute(alphaFunc);
stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);
}
if (shaderMat->mAlphaBlend)
{
osg::StateSet* stateset = node->getOrCreateStateSet();
osg::ref_ptr<osg::BlendFunc> blendFunc(
new osg::BlendFunc(getBlendMode(shaderMat->mSourceBlendMode),
getBlendMode(shaderMat->mDestinationBlendMode)));
blendFunc = shareAttribute(blendFunc);
stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
hasSortAlpha = true;
if (!mPushedSorter)
setBin_Transparent(stateset);
}
if (shaderMat->mDecal)
{
osg::StateSet* stateset = node->getOrCreateStateSet();
if (!mPushedSorter && !hasSortAlpha)
stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT");
osg::ref_ptr<osg::PolygonOffset> polygonOffset(new osg::PolygonOffset);
polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f);
polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f);
stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON);
}
if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting)
{ {
auto bgsm = static_cast<const Bgsm::BGSMFile*>(shaderMat.get()); auto bgsm = static_cast<const Bgsm::BGSMFile*>(shaderMat.get());
@ -2860,8 +2787,6 @@ namespace NifOsg
= false; // bgsm->mSpecularEnabled; disabled until it can be implemented properly = false; // bgsm->mSpecularEnabled; disabled until it can be implemented properly
specStrength = bgsm->mSpecularMult; specStrength = bgsm->mSpecularMult;
emissiveMult = bgsm->mEmittanceMult; emissiveMult = bgsm->mEmittanceMult;
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mEmittanceColor, 1.f));
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mSpecularColor, 1.f));
} }
break; break;
} }
@ -2888,47 +2813,11 @@ namespace NifOsg
case Nif::RC_BSEffectShaderProperty: case Nif::RC_BSEffectShaderProperty:
{ {
auto shaderprop = static_cast<const Nif::BSEffectShaderProperty*>(property); auto shaderprop = static_cast<const Nif::BSEffectShaderProperty*>(property);
Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName); if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName))
if (shaderMat)
{ {
mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha);
if (shaderMat->mAlphaTest) if (shaderMat->mAlphaBlend && !mPushedSorter)
{ setBin_Transparent(node->getOrCreateStateSet());
osg::StateSet* stateset = node->getOrCreateStateSet();
osg::ref_ptr<osg::AlphaFunc> alphaFunc(new osg::AlphaFunc(
osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f));
alphaFunc = shareAttribute(alphaFunc);
stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);
}
if (shaderMat->mAlphaBlend)
{
osg::StateSet* stateset = node->getOrCreateStateSet();
osg::ref_ptr<osg::BlendFunc> blendFunc(
new osg::BlendFunc(getBlendMode(shaderMat->mSourceBlendMode),
getBlendMode(shaderMat->mDestinationBlendMode)));
blendFunc = shareAttribute(blendFunc);
stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
hasSortAlpha = true;
if (!mPushedSorter)
setBin_Transparent(stateset);
}
if (shaderMat->mDecal)
{
osg::StateSet* stateset = node->getOrCreateStateSet();
if (!mPushedSorter && !hasSortAlpha)
stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT");
osg::ref_ptr<osg::PolygonOffset> polygonOffset(new osg::PolygonOffset);
polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f);
polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f);
stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON);
}
if (shaderMat->mShaderType == Bgsm::ShaderType::Effect)
{
auto bgem = static_cast<const Bgsm::BGEMFile*>(shaderMat.get());
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgem->mEmittanceColor, 1.f));
if (bgem->mSoft)
SceneUtil::setupSoftEffect(*node, bgem->mSoftDepth, true, bgem->mSoftDepth);
}
break; break;
} }
if (shaderprop->decal()) if (shaderprop->decal())

Loading…
Cancel
Save