diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index ec2699aa79..f69cbb133c 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -352,11 +352,6 @@ namespace ESMTerrain std::string Storage::getTextureName(UniqueTextureId id) { - // Goes under used terrain blend transitions - static constexpr char baseTexture[] = "textures\\tx_black_01.dds"; - if (id.first == -1) - return baseTexture; - static constexpr char defaultTexture[] = "textures\\_land_default.dds"; if (id.first == 0) return defaultTexture; // Not sure if the default texture really is hardcoded? @@ -385,72 +380,44 @@ namespace ESMTerrain int rowStart = (origin.x() - cellX) * realTextureSize; int colStart = (origin.y() - cellY) * realTextureSize; - int rowEnd = rowStart + chunkSize * (realTextureSize-1) + 1; - int colEnd = colStart + chunkSize * (realTextureSize-1) + 1; - - // Save the used texture indices so we know the total number of textures - // and number of required blend maps - std::set textureIndices; - // Due to the way the blending works, the base layer will bleed between texture transitions so we want it to be a black texture - // The subsequent passes are added instead of blended, so this gives the correct result - textureIndices.insert(std::make_pair(-1,0)); // -1 goes to tx_black_01 - - LandCache cache; - - for (int y=colStart; y textureIndicesMap; - for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) - { - int size = textureIndicesMap.size(); - textureIndicesMap[*it] = size; - layerList.push_back(getLayerInfo(getTextureName(*it))); - } - - // size-1 since the base layer doesn't need blending - int numBlendmaps = textureIndices.size() - 1; - // Second iteration - create and fill in the blend maps const int blendmapSize = (realTextureSize-1) * chunkSize + 1; // We need to upscale the blendmap 2x with nearest neighbor sampling to look like Vanilla const int imageScaleFactor = 2; const int blendmapImageSize = blendmapSize * imageScaleFactor; - for (int i=0; i image (new osg::Image); - image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE); - unsigned char* pData = image->data(); + LandCache cache; + std::map textureIndicesMap; - for (int y=0; y::iterator found = textureIndicesMap.find(id); + if (found == textureIndicesMap.end()) { - UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache); - assert(textureIndicesMap.find(id) != textureIndicesMap.end()); - int layerIndex = textureIndicesMap.find(id)->second; - - int alpha = (layerIndex == i+1) ? 255 : 0; - - int realY = (blendmapSize - y - 1)*imageScaleFactor; - int realX = x*imageScaleFactor; - - pData[(realY+0)*blendmapImageSize + realX + 0] = alpha; - pData[(realY+1)*blendmapImageSize + realX + 0] = alpha; - pData[(realY+0)*blendmapImageSize + realX + 1] = alpha; - pData[(realY+1)*blendmapImageSize + realX + 1] = alpha; + found = textureIndicesMap.insert(std::make_pair(id, textureIndicesMap.size())).first; + layerList.push_back(getLayerInfo(getTextureName(id))); + osg::ref_ptr image (new osg::Image); + image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE); + unsigned char* pData = image->data(); + memset(pData, 0, image->getTotalDataSize()); + blendmaps.push_back(image); } + int layerIndex = found->second; + unsigned char* pData = blendmaps[layerIndex]->data(); + int realY = (blendmapSize - y - 1)*imageScaleFactor; + int realX = x*imageScaleFactor; + pData[((realY+0)*blendmapImageSize + realX + 0)] = 255; + pData[((realY+1)*blendmapImageSize + realX + 0)] = 255; + pData[((realY+0)*blendmapImageSize + realX + 1)] = 255; + pData[((realY+1)*blendmapImageSize + realX + 1)] = 255; } - blendmaps.push_back(image); } + + if (blendmaps.size() == 1) + blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend } float Storage::getHeightAt(const osg::Vec3f &worldPos) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 1c989cc2a7..fd385e793c 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -111,6 +111,24 @@ namespace } }; + class BlendFuncFirst + { + public: + static const osg::ref_ptr& value() + { + static BlendFuncFirst instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; + + BlendFuncFirst() + : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ZERO)) + { + } + }; + class BlendFunc { public: @@ -124,9 +142,8 @@ namespace osg::ref_ptr mValue; BlendFunc() - : mValue(new osg::BlendFunc) + : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE)) { - mValue->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); } }; @@ -166,19 +183,16 @@ namespace Terrain osg::ref_ptr stateset (new osg::StateSet); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + if (!firstLayer) { - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); } - // disable fog if we're the first layer of several - supposed to be completely black - if (firstLayer && blendmaps.size() > 0) + else { - osg::ref_ptr fog (new osg::Fog); - fog->setStart(10000000); - fog->setEnd(10000000); - stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); + stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); } @@ -193,7 +207,7 @@ namespace Terrain stateset->addUniform(new osg::Uniform("diffuseMap", texunit)); - if(!firstLayer) + if (!blendmaps.empty()) { ++texunit; osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); @@ -212,7 +226,7 @@ namespace Terrain Shader::ShaderManager::DefineMap defineMap; defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0"; - defineMap["blendMap"] = !firstLayer ? "1" : "0"; + defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0"; defineMap["specularMap"] = it->mSpecular ? "1" : "0"; defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0"; @@ -229,7 +243,17 @@ namespace Terrain } else { - if(!firstLayer) + // Add the actual layer texture + osg::ref_ptr tex = it->mDiffuseMap; + stateset->setTextureAttributeAndModes(texunit, tex.get()); + + if (layerTileSize != 1.f) + stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); + + ++texunit; + + // Multiply by the alpha map + if (!blendmaps.empty()) { osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); @@ -242,12 +266,6 @@ namespace Terrain ++texunit; } - // Add the actual layer texture multiplied by the alpha map. - osg::ref_ptr tex = it->mDiffuseMap; - stateset->setTextureAttributeAndModes(texunit, tex.get()); - - if (layerTileSize != 1.f) - stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); } stateset->setRenderBinDetails(passIndex++, "RenderBin");