mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-28 17:56:42 +00:00 
			
		
		
		
	Optimize blendmap generation
This commit is contained in:
		
							parent
							
								
									58f8d1ac04
								
							
						
					
					
						commit
						1da012f6ee
					
				
					 2 changed files with 62 additions and 77 deletions
				
			
		|  | @ -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<UniqueTextureId> 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<colEnd; ++y) | ||||
|             for (int x=rowStart; x<rowEnd; ++x) | ||||
|             { | ||||
|                 UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y, cache); | ||||
|                 textureIndices.insert(id); | ||||
|             } | ||||
| 
 | ||||
|         // Makes sure the indices are sorted, or rather,
 | ||||
|         // retrieved as sorted. This is important to keep the splatting order
 | ||||
|         // consistent across cells.
 | ||||
|         std::map<UniqueTextureId, int> textureIndicesMap; | ||||
|         for (std::set<UniqueTextureId>::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<numBlendmaps; ++i) | ||||
|         LandCache cache; | ||||
|         std::map<UniqueTextureId, int> textureIndicesMap; | ||||
| 
 | ||||
|         for (int y=0; y<blendmapSize; ++y) | ||||
|         { | ||||
|             osg::ref_ptr<osg::Image> image (new osg::Image); | ||||
|             image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE); | ||||
|             unsigned char* pData = image->data(); | ||||
| 
 | ||||
|             for (int y=0; y<blendmapSize; ++y) | ||||
|             for (int x=0; x<blendmapSize; ++x) | ||||
|             { | ||||
|                 for (int x=0; x<blendmapSize; ++x) | ||||
|                 UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache); | ||||
|                 std::map<UniqueTextureId, int>::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<osg::Image> 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) | ||||
|  |  | |||
|  | @ -111,6 +111,24 @@ namespace | |||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class BlendFuncFirst | ||||
|     { | ||||
|     public: | ||||
|         static const osg::ref_ptr<osg::BlendFunc>& value() | ||||
|         { | ||||
|             static BlendFuncFirst instance; | ||||
|             return instance.mValue; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         osg::ref_ptr<osg::BlendFunc> mValue; | ||||
| 
 | ||||
|         BlendFuncFirst() | ||||
|             : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ZERO)) | ||||
|         { | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class BlendFunc | ||||
|     { | ||||
|     public: | ||||
|  | @ -124,9 +142,8 @@ namespace | |||
|         osg::ref_ptr<osg::BlendFunc> 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<osg::StateSet> 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<osg::Fog> 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<osg::Texture2D> 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<osg::Texture2D> 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<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++); | ||||
| 
 | ||||
|  | @ -242,12 +266,6 @@ namespace Terrain | |||
|                     ++texunit; | ||||
|                 } | ||||
| 
 | ||||
|                 // Add the actual layer texture multiplied by the alpha map.
 | ||||
|                 osg::ref_ptr<osg::Texture2D> 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"); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue