From 1d0ae9c8d122ea168b9515bb696a74e6d8c9aa05 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Wed, 29 Feb 2012 23:05:22 +0000 Subject: [PATCH] Start of a basic implementation of fake vertex colours --- apps/openmw/mwrender/terrain.cpp | 146 ++++++++++++++++++++++++++++--- apps/openmw/mwrender/terrain.hpp | 26 +++++- components/esm/loadland.cpp | 5 +- components/esm/loadland.hpp | 2 + 4 files changed, 167 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 710a3ad05..89c362c4c 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include "terrain.hpp" #include "components/esm/loadland.hpp" @@ -104,6 +106,9 @@ namespace MWRender const int terrainX = cellX * 2 + x; const int terrainY = cellY * 2 + y; + //it makes far more sense to reallocate the memory here, + //and let Ogre deal with it due to the issues with deleting + //it at the wrong time if using threads (Which Ogre::Terrain does) terrainData.inputFloat = OGRE_ALLOC_T(float, mLandSize*mLandSize, Ogre::MEMCATEGORY_GEOMETRY); @@ -122,10 +127,16 @@ namespace MWRender mLandSize*sizeof(float)); } + //this should really be 33*33 + Ogre::uchar* vertexColourAlpha = OGRE_ALLOC_T(Ogre::uchar, + mLandSize*mLandSize, + Ogre::MEMCATEGORY_GENERAL); + std::map indexes; initTerrainTextures(&terrainData, store, x * numTextures, y * numTextures, - numTextures, indexes); + numTextures, vertexColourAlpha, + indexes); assert( mTerrainGroup->getTerrain(cellX, cellY) == NULL && "The terrain for this cell already existed" ); @@ -133,9 +144,20 @@ namespace MWRender mTerrainGroup->loadTerrain(terrainX, terrainY, true); Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); + + Ogre::Image vertexColourBlendMap = Ogre::Image(); + vertexColourBlendMap.loadDynamicImage(vertexColourAlpha, + mLandSize, mLandSize, 1, + Ogre::PF_A8, true) + .resize(mTerrainGlobals->getLayerBlendMapSize(), + mTerrainGlobals->getLayerBlendMapSize(), + Ogre::Image::FILTER_BOX); + initTerrainBlendMaps(terrain, store, x * numTextures, y * numTextures, - numTextures, indexes); + numTextures, + vertexColourBlendMap.getData(), + indexes); } } @@ -154,16 +176,30 @@ namespace MWRender &store->land[1][1]->landData->heights[0], mLandSize*mLandSize*sizeof(float)); + Ogre::uchar* vertexColourAlpha = OGRE_ALLOC_T(Ogre::uchar, + mLandSize*mLandSize, + Ogre::MEMCATEGORY_GENERAL); + std::map indexes; initTerrainTextures(&terrainData, store, 0, 0, - ESM::Land::LAND_TEXTURE_SIZE, indexes); + ESM::Land::LAND_TEXTURE_SIZE, vertexColourAlpha, indexes); mTerrainGroup->defineTerrain(cellX, cellY, &terrainData); mTerrainGroup->loadTerrain(cellX, cellY, true); Ogre::Terrain* terrain = mTerrainGroup->getTerrain(cellX, cellY); + + Ogre::Image vertexColourBlendMap = Ogre::Image(); + vertexColourBlendMap.loadDynamicImage(vertexColourAlpha, + mLandSize, mLandSize, 1, + Ogre::PF_A8, true) + .resize(mTerrainGlobals->getLayerBlendMapSize(), + mTerrainGlobals->getLayerBlendMapSize(), + Ogre::Image::FILTER_BOX); + initTerrainBlendMaps(terrain, store, 0, 0, - ESM::Land::LAND_TEXTURE_SIZE, indexes); + ESM::Land::LAND_TEXTURE_SIZE, + vertexColourBlendMap.getData(), indexes); } mTerrainGroup->freeTemporaryResources(); @@ -196,6 +232,7 @@ namespace MWRender void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, + Ogre::uchar* vertexColourAlpha, std::map& indexes) { assert(store != NULL && "store must be a valid pointer"); @@ -227,7 +264,7 @@ namespace MWRender { //NB: All vtex ids are +1 compared to the ltex ids assert((int)ltexIndex >= 0 && - store->landTextures->ltex.size() > (size_t)ltexIndex - 1 && + (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); std::string texture; @@ -262,6 +299,17 @@ namespace MWRender } } } + + //add the vertex colour overlay + //TODO sort the *4 bit + Ogre::TexturePtr vclr = getVertexColours(store, vertexColourAlpha, fromX*4, fromY*4, mLandSize); + Ogre::TexturePtr normDisp = getNormalDisp("DOES_NOT_EXIST"); + + const size_t position = terrainData->layerList.size(); + terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); + terrainData->layerList[position].worldSize = mRealSize; + terrainData->layerList[position].textureNames.push_back(vclr->getName()); + terrainData->layerList[position].textureNames.push_back(normDisp->getName()); } //---------------------------------------------------------------------------------------------- @@ -269,6 +317,7 @@ namespace MWRender void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, + Ogre::uchar* vertexColourAlpha, const std::map& indexes) { assert(store != NULL && "store must be a valid pointer"); @@ -294,6 +343,15 @@ namespace MWRender ->getBlendPointer(); memset(pBlend, 0, sizeof(float) * blendSize * blendSize); } + //except the overlay, which we will just splat over the top + { + //the overlay is always the last texture layer + float* pBlend = terrain->getLayerBlendMap(terrain->getLayerCount() - 1) + ->getBlendPointer(); + for ( int i = 0; i < blendSize * blendSize; i++ ){ + *pBlend++ = (*vertexColourAlpha++)/255.0f; + } + } //covert the ltex data into a set of blend maps for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ ) @@ -354,10 +412,9 @@ namespace MWRender } } - //update the maps - for ( iter = indexes.begin(); iter != indexes.end(); ++iter ) + for ( int i = 1; i < terrain->getLayerCount(); i++ ) { - Ogre::TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(iter->second); + Ogre::TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i); blend->dirty(); blend->update(); } @@ -424,8 +481,7 @@ namespace MWRender return texMgr->getByName(normalTextureName); } - const std::string textureName = "default_terrain_normal"; - if ( !texMgr->getByName(textureName).isNull() ) + const std::string textureName = "default_terrain_normal"; if ( !texMgr->getByName(textureName).isNull() ) { return texMgr->getByName(textureName); } @@ -451,4 +507,74 @@ namespace MWRender return tex; } + //---------------------------------------------------------------------------------------------- + + Ogre::TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, + Ogre::uchar* alpha, + int fromX, int fromY, int size) + { + Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); + const char* const colours = store->land[1][1]->landData->colours; + + const std::string colourTextureName = "VtexColours_" + + boost::lexical_cast(store->cell->getGridX()) + + "_" + + boost::lexical_cast(store->cell->getGridY()) + + "_" + + boost::lexical_cast(fromX) + + "_" + + boost::lexical_cast(fromY); + + Ogre::TexturePtr tex = texMgr->getByName(colourTextureName); + if ( !tex.isNull() ) + { + return tex; + } + + tex = texMgr->createManual(colourTextureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, size, size, 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); + + for ( int y = 0; y < size; y++ ) + { + for ( int x = 0; x < size; x++ ) + { + const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3; + + assert( colourOffset >= 0 && colourOffset < 65*65*3 && + "Colour offset is out of the expected bounds of record" ); + + const unsigned char r = colours[colourOffset + 0]; + const unsigned char g = colours[colourOffset + 1]; + const unsigned char b = colours[colourOffset + 2]; + + //as is the case elsewhere we need to flip the y + const size_t imageOffset = (size - 1 - y)*size*4 + x*4; + pDest[imageOffset + 0] = b; + pDest[imageOffset + 1] = g; + pDest[imageOffset + 2] = r; + pDest[imageOffset + 3] = 255; //Alpha, TODO this needs to be removed + + const size_t alphaOffset = (size - 1 - y)*size + x; + if ( r == 255 && g == 255 && b == 255 ){ + alpha[alphaOffset] = 0; + }else{ + alpha[alphaOffset] = 128; + } + + } + } + + pixelBuffer->unlock(); + + return tex; + } + } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 548d00eab..a201fdb61 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -74,12 +74,16 @@ namespace MWRender{ * @param fromX the ltex index in the current cell to start making the texture from * @param fromY the ltex index in the current cell to start making the texture from * @param size the size (number of splats) to get + * @param vertexColourAlpha this should be an empty array containing the number of + * vertexes in this terrain segment. It is filled with the + * alpha values for the vertex colours * @param indexes a mapping of ltex index to the terrain texture layer that * can be used by initTerrainBlendMaps */ void initTerrainTextures(Ogre::Terrain::ImportData* terrainData, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, + Ogre::uchar* vertexColourAlpha, std::map& indexes); /** @@ -90,11 +94,16 @@ namespace MWRender{ * @param fromX the ltex index in the current cell to start making the texture from * @param fromY the ltex index in the current cell to start making the texture from * @param size the size (number of splats) to get + * @param vertexColourAlpha this should be an array containing the alpha values + * for the vertex colours. NOTE: This should be the + * size of a splat map, which is NOT the same size + * as retured from initTerrainTextures. * @param indexes the mapping of ltex to blend map produced by initTerrainTextures */ void initTerrainBlendMaps(Ogre::Terrain* terrain, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, + Ogre::uchar* vertexColourAlpha, const std::map& indexes); /** @@ -118,7 +127,22 @@ namespace MWRender{ * @param fileName the name of the *diffuse* texture */ Ogre::TexturePtr getNormalDisp(const std::string& fileName); - + + /** + * Due to the fact that Ogre terrain doesn't support vertex colours + * we have to generate them manually + * + * @param store the cell store for the given terrain cell + * @param vertexColourAlpha this should be an empty array containing the number of + * vertexes in this terrain segment. It is filled with the + * alpha values for the vertex colours + * @param fromX the *vertex* index in the current cell to start making texture from + * @param fromY the *vertex* index in the current cell to start making the texture from + * @param size the size (number of vertexes) to get + */ + Ogre::TexturePtr getVertexColours(MWWorld::Ptr::CellStore* store, + Ogre::uchar* alpha, + int fromX, int fromY, int size); }; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 1d670036e..3bca8692b 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -93,7 +93,10 @@ void Land::loadData(ESMReader &esm) } if (esm.isNextSub("VCLR")) { - esm.skipHSubSize(12675); + landData->usingColours = true; + esm.getHExact(&landData->colours, 3*LAND_NUM_VERTS); + }else{ + landData->usingColours = false; } //TODO fix magic numbers uint16_t vtex[512]; diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 4219f3eb6..eeb198e90 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -58,6 +58,8 @@ struct Land float heights[LAND_NUM_VERTS]; //float normals[LAND_NUM_VERTS * 3]; uint16_t textures[LAND_NUM_TEXTURES]; + + bool usingColours; char colours[3 * LAND_NUM_VERTS]; };