diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index dd05bf3336..30e0823f94 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -17,9 +17,6 @@ namespace MWRender { mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); - mTerrainGlobals->setMaxPixelError(8); - mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); - Ogre::TerrainMaterialGeneratorPtr matGen; TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB(); matGen.bind(matGenP); @@ -31,12 +28,27 @@ namespace MWRender TerrainMaterialGeneratorB::SM2Profile* matProfile = static_cast(activeProfile); + mTerrainGlobals->setMaxPixelError(8); + //mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); + //mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); + //mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); + //mTerrainGlobals->setDefaultGlobalColourMapSize(256); + + //10 (default) didn't seem to be quite enough + mTerrainGlobals->setSkirtSize(128); + + /* + * Here we are pushing the composite map distance beyond the edge + * of the rendered terrain due to not having setup lighting + */ + //mTerrainGlobals->setCompositeMapDistance(ESM::Land::REAL_SIZE*4); + matProfile->setLightmapEnabled(false); - matProfile->setReceiveDynamicShadowsEnabled(false); + matProfile->setLayerSpecularMappingEnabled(false); matProfile->setLayerNormalMappingEnabled(false); matProfile->setLayerParallaxMappingEnabled(false); - matProfile->setLayerSpecularMappingEnabled(false); - + matProfile->setReceiveDynamicShadowsEnabled(false); + mLandSize = ESM::Land::LAND_SIZE; mRealSize = ESM::Land::REAL_SIZE; if ( SPLIT_TERRAIN ) @@ -95,8 +107,6 @@ namespace MWRender const int cellX = store->cell->getGridX(); const int cellY = store->cell->getGridY(); - Ogre::Terrain::ImportData terrainData = - mTerrainGroup->getDefaultImportSettings(); if ( SPLIT_TERRAIN ) { @@ -107,6 +117,9 @@ namespace MWRender { for ( int y = 0; y < 2; y++ ) { + Ogre::Terrain::ImportData terrainData = + mTerrainGroup->getDefaultImportSettings(); + const int terrainX = cellX * 2 + x; const int terrainY = cellY * 2 + y; @@ -133,6 +146,8 @@ namespace MWRender x * numTextures, y * numTextures, numTextures, indexes); + assert( mTerrainGroup->getTerrain(cellX, cellY) == NULL && + "The terrain for this cell already existed" ); mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); mTerrainGroup->loadTerrain(terrainX, terrainY, true); @@ -146,6 +161,9 @@ namespace MWRender } else { + Ogre::Terrain::ImportData terrainData = + mTerrainGroup->getDefaultImportSettings(); + //one cell is one terrain segment terrainData.inputFloat = OGRE_ALLOC_T(float, mLandSize*mLandSize, @@ -167,6 +185,7 @@ namespace MWRender ESM::Land::LAND_TEXTURE_SIZE, indexes); } + mTerrainGroup->freeTemporaryResources(); } //---------------------------------------------------------------------------------------------- @@ -179,14 +198,14 @@ namespace MWRender { for ( int y = 0; y < 2; y++ ) { - mTerrainGroup->removeTerrain(store->cell->getGridX() * 2 + x, + mTerrainGroup->unloadTerrain(store->cell->getGridX() * 2 + x, store->cell->getGridY() * 2 + y); } } } else { - mTerrainGroup->removeTerrain(store->cell->getGridX(), + mTerrainGroup->unloadTerrain(store->cell->getGridX(), store->cell->getGridY()); } } @@ -206,19 +225,17 @@ namespace MWRender fromY+size <= ESM::Land::LAND_TEXTURE_SIZE && "Can't get a terrain texture on terrain outside the current cell"); - //have a base texture for now, but this is probably not needed on most cells - terrainData->layerList.resize(1); - terrainData->layerList[0].worldSize = 256; - terrainData->layerList[0].textureNames.push_back("textures\\_land_default.dds"); // diffuseSpec - //terrainData->layerList[0].textureNames.push_back("textures\\_land_default.dds"); // normalHeight - + //there is one texture that we want to use as a base (i.e. it won't have + //a blend map). This holds the ltex index of that base texture so that + //we know not to include it in the output map + int baseTexture = -1; for ( int y = fromY - 1; y < fromY + size + 1; y++ ) { for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { const uint16_t ltexIndex = getLtexIndexAt(store, x, y); //this is the base texture, so we can ignore this at present - if ( ltexIndex == 0 ) + if ( ltexIndex == baseTexture ) { continue; } @@ -232,21 +249,38 @@ namespace MWRender store->landTextures->ltex.size() > (size_t)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); - std::string texture = store->landTextures->ltex[ltexIndex-1].texture; - //TODO this is needed due to MWs messed up texture handling - texture = texture.substr(0, texture.rfind(".")) + ".dds"; + std::string texture; + if ( ltexIndex == 0 ) + { + texture = "_land_default.dds"; + } + else + { + texture = store->landTextures->ltex[ltexIndex-1].texture; + //TODO this is needed due to MWs messed up texture handling + texture = texture.substr(0, texture.rfind(".")) + ".dds"; + } const size_t position = terrainData->layerList.size(); terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); - terrainData->layerList[position].worldSize = 256; + Ogre::TexturePtr normDisp = getNormalDisp("textures\\" + texture); + + //terrainData->layerList[position].worldSize = 256; terrainData->layerList[position].textureNames.push_back("textures\\" + texture); //Normal map. This should be removed but it would require alterations to //the material generator. Another option would be to use a 1x1 blank texture - //terrainData->layerList[position].textureNames.push_back("textures\\" + texture); + //terrainData->layerList[position].textureNames.push_back(normDisp->getName()); - indexes[ltexIndex] = position; + if ( baseTexture == -1 ) + { + baseTexture = ltexIndex; + } + else + { + indexes[ltexIndex] = position; + } } } } @@ -278,9 +312,9 @@ namespace MWRender std::map::const_iterator iter; for ( iter = indexes.begin(); iter != indexes.end(); ++iter ) { - float* pBlend = terrain->getLayerBlendMap(iter->second) - ->getBlendPointer(); - memset(pBlend, 0, sizeof(float) * blendSize * blendSize); + float* pBlend = terrain->getLayerBlendMap(iter->second) + ->getBlendPointer(); + memset(pBlend, 0, sizeof(float) * blendSize * blendSize); } //covert the ltex data into a set of blend maps @@ -295,15 +329,13 @@ namespace MWRender const int relX = texX - fromX; const int relY = texY - fromY; - //this is the ground texture, which is currently the base texture - //so don't alter the splatting map - if ( ltexIndex == 0 ){ + //check if it is the base texture (which isn't in the map) and + //if it is don't bother altering the blend map for it + if ( indexes.find(ltexIndex) == indexes.end() ) + { continue; } - assert (indexes.find(ltexIndex) != indexes.end() && - "Texture layer must exist"); - const int layerIndex = indexes.find(ltexIndex)->second; float* const pBlend = terrain->getLayerBlendMap(layerIndex) @@ -317,7 +349,7 @@ namespace MWRender const int startY = std::max(0, relY*splatSize - blendDist); const int endY = std::min(blendSize, (relY+1)*splatSize + blendDist); - + for ( int blendX = startX; blendX < endX; blendX++ ) { for ( int blendY = startY; blendY < endY; blendY++ ) @@ -328,34 +360,16 @@ namespace MWRender assert(blendY >= 0 && blendY < blendSize && "index must be within texture bounds"); - //calculate the distance from the edge of the square - // to the point we are shading - int distX = relX*splatSize - blendX; - if ( distX < 0 ) - { - distX = std::max(0, blendX - (relX+1)*splatSize); - } - - int distY = relY*splatSize - blendY; - if ( distY < 0 ) - { - distY = std::max(0, blendY - (relY+1)*splatSize); - } - - float blendAmount = blendDist - std::sqrt((float)distX*distX + distY*distY); - blendAmount /= blendDist; - - //this is required as blendDist < sqrt(blendDist*blendDist + blendDist*blendDist) - //this means that the corners are slightly the "wrong" shape but totaly smooth - //shading for the edges - blendAmount = std::max( (float) 0.0, blendAmount); - - assert(blendAmount >= 0 && "Blend should never be negative"); - - //flips the y const int index = blendSize*(blendSize - 1 - blendY) + blendX; - pBlend[index] += blendAmount; - pBlend[index] = std::min((float)1, pBlend[index]); + if ( blendX >= relX*splatSize && blendX < (relX+1)*splatSize && + blendY >= relY*splatSize && blendY < (relY+1)*splatSize ) + { + pBlend[index] = 1; + } + else + { + pBlend[index] = std::max((float)pBlend[index], 0.5f); + } } } @@ -420,4 +434,43 @@ namespace MWRender ->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; } + //---------------------------------------------------------------------------------------------- + + Ogre::TexturePtr TerrainManager::getNormalDisp(const std::string& fileName) + { + Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); + const std::string normalTextureName = fileName.substr(0, fileName.rfind(".")) + + "_n.dds"; + if ( !texMgr->getByName(normalTextureName).isNull() ) + { + return texMgr->getByName(normalTextureName); + } + + const std::string textureName = "default_terrain_normal"; + if ( !texMgr->getByName(textureName).isNull() ) + { + return texMgr->getByName(textureName); + } + + Ogre::TexturePtr tex = texMgr->createManual( + textureName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, 1, 1, 0, Ogre::PF_BYTE_BGRA); + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); + + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + + Ogre::uint8* pDest = static_cast(pixelBox.data); + + *pDest++ = 128; // B + *pDest++ = 128; // G + *pDest++ = 128; // R + *pDest++ = 0; // A + + pixelBuffer->unlock(); + + return tex; + } + } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 3f2633ff9f..c8f225f259 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -65,7 +65,7 @@ namespace MWRender{ * The distance that the current cell should be shaded into the neighbouring * texture. The distance is in terms of the splat size of a texture */ - static const float TERRAIN_SHADE_DISTANCE = 0.5; + static const float TERRAIN_SHADE_DISTANCE = 0.25f; /** * Setups up the list of textures for part of a cell, using indexes as @@ -110,6 +110,17 @@ namespace MWRender{ * first splat of the current cell */ int getLtexIndexAt(MWWorld::Ptr::CellStore* store, int x, int y); + + /** + * Retrives the texture that is the normal and parallax map for the + * terrain. If it doesn't exist a blank texture is used + * + * The file name of the texture should be the same as the file name of + * the base diffuse texture, but with _n appended before the extension + * + * @param fileName the name of the *diffuse* texture + */ + Ogre::TexturePtr getNormalDisp(const std::string& fileName); }; diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index bbb6a66065..8985fc8ae2 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -544,7 +544,7 @@ namespace Ogre params->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); params->setNamedAutoConstant("lightPosObjSpace", GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, 0); params->setNamedAutoConstant("lightDiffuseColour", GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, 0); - params->setNamedAutoConstant("lightSpecularColour", GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, 0); + //params->setNamedAutoConstant("lightSpecularColour", GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, 0); params->setNamedAutoConstant("eyePosObjSpace", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE); params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); @@ -948,7 +948,7 @@ namespace Ogre "uniform float3 ambient,\n" "uniform float4 lightPosObjSpace,\n" "uniform float3 lightDiffuseColour,\n" - "uniform float3 lightSpecularColour,\n" + //"uniform float3 lightSpecularColour,\n" "uniform float3 eyePosObjSpace,\n" // pack scale, bias and specular "uniform float4 scaleBiasSpecular,\n"; @@ -1274,7 +1274,7 @@ namespace Ogre else { // Apply specular - outStream << " outputCol.rgb += litRes.z * lightSpecularColour * specular * shadow;\n"; + //outStream << " outputCol.rgb += litRes.z * lightSpecularColour * specular * shadow;\n"; if (prof->getParent()->getDebugLevel()) {