diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 5e4ca1365..3b314935d 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -72,173 +72,42 @@ namespace CSVRender return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX]; } - void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, - osg::ref_ptr positions, - osg::ref_ptr normals, - osg::ref_ptr colours) + void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) { - // LOD level n means every 2^n-th vertex is kept - size_t increment = static_cast(1) << lodLevel; - - osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); - - int startCellX = static_cast(std::floor(origin.x())); - int startCellY = static_cast(std::floor(origin.y())); - - size_t numVerts = static_cast(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); - - positions->resize(numVerts*numVerts); - normals->resize(numVerts*numVerts); - colours->resize(numVerts*numVerts); - - osg::Vec3f normal; - osg::Vec4ub color; - - float vertY = 0; - float vertX = 0; - - ESMTerrain::LandCache cache; + // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells + throw std::runtime_error("getBounds not implemented"); + } - float vertY_ = 0; // of current cell corner - for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) + void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const + { + // Highlight broken height changes + if ( ((col > 0 && row > 0) && + ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || + abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 )) || + ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && + ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || + abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 ))) { - float vertX_ = 0; // of current cell corner - for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) - { - const ESMTerrain::LandObject* land = ESMTerrain::Storage::getLand(cellX, cellY, cache); - const ESM::Land::LandData *heightData = 0; - const ESM::Land::LandData *normalData = 0; - const ESM::Land::LandData *colourData = 0; - if (land) - { - heightData = land->getData(ESM::Land::DATA_VHGT); - normalData = land->getData(ESM::Land::DATA_VNML); - colourData = land->getData(ESM::Land::DATA_VCLR); - } - - int rowStart = 0; - int colStart = 0; - // Skip the first row / column unless we're at a chunk edge, - // since this row / column is already contained in a previous cell - // This is only relevant if we're creating a chunk spanning multiple cells - if (vertY_ != 0) - colStart += increment; - if (vertX_ != 0) - rowStart += increment; - - // Only relevant for chunks smaller than (contained in) one cell - rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; - colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; - int rowEnd = std::min(static_cast(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast(ESM::Land::LAND_SIZE)); - int colEnd = std::min(static_cast(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast(ESM::Land::LAND_SIZE)); - - vertY = vertY_; - for (int col=colStart; col= 0 && row < ESM::Land::LAND_SIZE); - assert(col >= 0 && col < ESM::Land::LAND_SIZE); - - assert (vertX < numVerts); - assert (vertY < numVerts); - - float height = defaultHeight; - if (heightData) - height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; - - (*positions)[static_cast(vertX*numVerts + vertY)] - = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - height + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)]); - - if (normalData) - { - for (int i=0; i<3; ++i) - normal[i] = normalData->mNormals[srcArrayIndex+i]; - - normal.normalize(); - } - else - normal = osg::Vec3f(0,0,1); - - // Normals apparently don't connect seamlessly between cells - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixNormal(normal, cellX, cellY, col, row, cache); - - // some corner normals appear to be complete garbage (z < 0) - if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) - averageNormal(normal, cellX, cellY, col, row, cache); - - assert(normal.z() > 0); - - (*normals)[static_cast(vertX*numVerts + vertY)] = normal; - - if (colourData) - { - for (int i=0; i<3; ++i) - color[i] = colourData->mColours[srcArrayIndex+i]; - } - else - { - color.r() = 255; - color.g() = 255; - color.b() = 255; - } - - // Highlight broken height changes - if ( ((col > 0 && row > 0) && - ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + - mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || - abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 )) || - ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && - ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + - mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || - abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 ))) - { - color.r() = 255; - color.g() = 0; - color.b() = 0; - } - - // Unlike normals, colors mostly connect seamlessly between cells, but not always... - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixColour(color, cellX, cellY, col, row, cache); - - color.a() = 255; - - (*colours)[static_cast(vertX*numVerts + vertY)] = color; - - ++vertX; - } - ++vertY; - } - vertX_ = vertX; - } - vertY_ = vertY; - - assert(vertX_ == numVerts); // Ensure we covered whole area + color.r() = 255; + color.g() = 0; + color.b() = 0; } - assert(vertY_ == numVerts); // Ensure we covered whole area*/ } - void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) + float TerrainStorage::getAlteredHeight(int col, int row) const { - // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells - throw std::runtime_error("getBounds not implemented"); + return mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)]; } - } diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 5f808d843..fe2d94b4a 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -9,8 +9,6 @@ namespace CSVRender { - class LandCache; - /** * @brief A bridge between the terrain component and OpenCS's terrain data storage. */ @@ -30,13 +28,10 @@ namespace CSVRender virtual osg::ref_ptr getLand (int cellX, int cellY) override; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; - /// Draws temporarily altered land (transient change support) - void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, - osg::ref_ptr positions, - osg::ref_ptr normals, - osg::ref_ptr colours) override; - virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; + + void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override; + float getAlteredHeight(int col, int row) const override; }; } diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 151f0ff91..52af530f5 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,6 +16,13 @@ namespace ESMTerrain { + class LandCache + { + public: + typedef std::map, osg::ref_ptr > Map; + Map mMap; + }; + LandObject::LandObject() : mLand(nullptr) , mLoadFlags(0) @@ -91,6 +98,82 @@ namespace ESMTerrain return false; } + void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) + { + while (col >= ESM::Land::LAND_SIZE-1) + { + ++cellY; + col -= ESM::Land::LAND_SIZE-1; + } + while (row >= ESM::Land::LAND_SIZE-1) + { + ++cellX; + row -= ESM::Land::LAND_SIZE-1; + } + while (col < 0) + { + --cellY; + col += ESM::Land::LAND_SIZE-1; + } + while (row < 0) + { + --cellX; + row += ESM::Land::LAND_SIZE-1; + } + + const LandObject* land = getLand(cellX, cellY, cache); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; + if (data) + { + normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalize(); + } + else + normal = osg::Vec3f(0,0,1); + } + + void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row, LandCache& cache) + { + osg::Vec3f n1,n2,n3,n4; + fixNormal(n1, cellX, cellY, col+1, row, cache); + fixNormal(n2, cellX, cellY, col-1, row, cache); + fixNormal(n3, cellX, cellY, col, row+1, cache); + fixNormal(n4, cellX, cellY, col, row-1, cache); + normal = (n1+n2+n3+n4); + normal.normalize(); + } + + void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) + { + if (col == ESM::Land::LAND_SIZE-1) + { + ++cellY; + col = 0; + } + if (row == ESM::Land::LAND_SIZE-1) + { + ++cellX; + row = 0; + } + + const LandObject* land = getLand(cellX, cellY, cache); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; + if (data) + { + color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; + color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; + color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; + } + else + { + color.r() = 255; + color.g() = 255; + color.b() = 255; + } + } + void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, @@ -172,7 +255,7 @@ namespace ESMTerrain (*positions)[static_cast(vertX*numVerts + vertY)] = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - height); + height + getAlteredHeight(col, row)); if (normalData) { @@ -208,6 +291,8 @@ namespace ESMTerrain color.b() = 255; } + adjustColor(col, row, heightData, color); //Does nothing by default, override in OpenMW-CS + // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixColour(color, cellX, cellY, col, row, cache); @@ -438,6 +523,27 @@ namespace ESMTerrain } + const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) + { + LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); + if (found != cache.mMap.end()) + return found->second; + else + { + found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; + return found->second; + } + } + + void Storage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const + { + } + + float Storage::getAlteredHeight(int col, int row) const + { + return 0; + } + Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { OpenThreads::ScopedLock lock(mLayerInfoMutex); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 019bf0bab..65e531e5c 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -5,9 +5,6 @@ #include -#include -#include - #include #include @@ -21,6 +18,8 @@ namespace VFS namespace ESMTerrain { + class LandCache; + /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use class LandObject : public osg::Object { @@ -51,13 +50,6 @@ namespace ESMTerrain ESM::Land::LandData mData; }; - class LandCache - { - public: - typedef std::map, osg::ref_ptr > Map; - Map mMap; - }; - /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) /// into the terrain component, converting it on the fly as needed. class Storage : public Terrain::Storage @@ -117,9 +109,25 @@ namespace ESMTerrain virtual int getBlendmapScale(float chunkSize); + float getVertexHeight (const ESM::Land::LandData* data, int x, int y) + { + assert(x < ESM::Land::LAND_SIZE); + assert(y < ESM::Land::LAND_SIZE); + return data->mHeights[y * ESM::Land::LAND_SIZE + x]; + } + private: const VFS::Manager* mVFS; + inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); + inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + + inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); + + virtual void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const; + virtual float getAlteredHeight(int col, int row) const; + // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. // pair @@ -139,103 +147,6 @@ namespace ESMTerrain bool mAutoUseSpecularMaps; Terrain::LayerInfo getLayerInfo(const std::string& texture); - - protected: - - inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) - { - while (col >= ESM::Land::LAND_SIZE-1) - { - ++cellY; - col -= ESM::Land::LAND_SIZE-1; - } - while (row >= ESM::Land::LAND_SIZE-1) - { - ++cellX; - row -= ESM::Land::LAND_SIZE-1; - } - while (col < 0) - { - --cellY; - col += ESM::Land::LAND_SIZE-1; - } - while (row < 0) - { - --cellX; - row += ESM::Land::LAND_SIZE-1; - } - - const LandObject* land = getLand(cellX, cellY, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; - if (data) - { - normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalize(); - } - else - normal = osg::Vec3f(0,0,1); - }; - - inline void fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) - { - if (col == ESM::Land::LAND_SIZE-1) - { - ++cellY; - col = 0; - } - if (row == ESM::Land::LAND_SIZE-1) - { - ++cellX; - row = 0; - } - - const LandObject* land = getLand(cellX, cellY, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; - if (data) - { - color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; - color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; - color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; - } - else - { - color.r() = 255; - color.g() = 255; - color.b() = 255; - } - }; - - inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) - { - osg::Vec3f n1,n2,n3,n4; - fixNormal(n1, cellX, cellY, col+1, row, cache); - fixNormal(n2, cellX, cellY, col-1, row, cache); - fixNormal(n3, cellX, cellY, col, row+1, cache); - fixNormal(n4, cellX, cellY, col, row-1, cache); - normal = (n1+n2+n3+n4); - normal.normalize(); - }; - - inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y) - { - assert(x < ESM::Land::LAND_SIZE); - assert(y < ESM::Land::LAND_SIZE); - return data->mHeights[y * ESM::Land::LAND_SIZE + x]; - }; - - inline const LandObject* getLand(int cellX, int cellY, LandCache& cache) - { - LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); - if (found != cache.mMap.end()) - return found->second; - else - { - found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; - return found->second; - } - }; }; }