forked from teamnwah/openmw-tes3coop
Terrain: create 4x4 terrain chunks per ESM::Cell to improve performance
Improves performance because the number of splatting layers per chunk is reduced, and finer grained frustum culling can be done.
This commit is contained in:
parent
95cf13e3f2
commit
ef18f4217f
6 changed files with 199 additions and 110 deletions
|
@ -1,6 +1,7 @@
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <osg/Image>
|
#include <osg/Image>
|
||||||
#include <osg/Plane>
|
#include <osg/Plane>
|
||||||
|
@ -34,19 +35,22 @@ namespace ESMTerrain
|
||||||
|
|
||||||
osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f);
|
osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f);
|
||||||
|
|
||||||
assert(origin.x() == (int) origin.x());
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
||||||
assert(origin.y() == (int) origin.y());
|
int cellY = static_cast<int>(std::floor(origin.y()));
|
||||||
|
|
||||||
int cellX = static_cast<int>(origin.x());
|
int startRow = (origin.x() - cellX) * ESM::Land::LAND_SIZE;
|
||||||
int cellY = static_cast<int>(origin.y());
|
int startColumn = (origin.y() - cellY) * ESM::Land::LAND_SIZE;
|
||||||
|
|
||||||
|
int endRow = startRow + size * (ESM::Land::LAND_SIZE-1) + 1;
|
||||||
|
int endColumn = startColumn + size * (ESM::Land::LAND_SIZE-1) + 1;
|
||||||
|
|
||||||
if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VHGT))
|
if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VHGT))
|
||||||
{
|
{
|
||||||
min = std::numeric_limits<float>::max();
|
min = std::numeric_limits<float>::max();
|
||||||
max = -std::numeric_limits<float>::max();
|
max = -std::numeric_limits<float>::max();
|
||||||
for (int row=0; row<ESM::Land::LAND_SIZE; ++row)
|
for (int row=startRow; row<endRow; ++row)
|
||||||
{
|
{
|
||||||
for (int col=0; col<ESM::Land::LAND_SIZE; ++col)
|
for (int col=startColumn; col<endColumn; ++col)
|
||||||
{
|
{
|
||||||
float h = data->mHeights[col*ESM::Land::LAND_SIZE+row];
|
float h = data->mHeights[col*ESM::Land::LAND_SIZE+row];
|
||||||
if (h > max)
|
if (h > max)
|
||||||
|
@ -143,11 +147,9 @@ namespace ESMTerrain
|
||||||
size_t increment = 1 << lodLevel;
|
size_t increment = 1 << lodLevel;
|
||||||
|
|
||||||
osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f);
|
osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f);
|
||||||
assert(origin.x() == (int) origin.x());
|
|
||||||
assert(origin.y() == (int) origin.y());
|
|
||||||
|
|
||||||
int startX = static_cast<int>(origin.x());
|
int startCellX = static_cast<int>(std::floor(origin.x()));
|
||||||
int startY = static_cast<int>(origin.y());
|
int startCellY = static_cast<int>(std::floor(origin.y()));
|
||||||
|
|
||||||
size_t numVerts = static_cast<size_t>(size*(ESM::Land::LAND_SIZE - 1) / increment + 1);
|
size_t numVerts = static_cast<size_t>(size*(ESM::Land::LAND_SIZE - 1) / increment + 1);
|
||||||
|
|
||||||
|
@ -162,10 +164,10 @@ namespace ESMTerrain
|
||||||
float vertX = 0;
|
float vertX = 0;
|
||||||
|
|
||||||
float vertY_ = 0; // of current cell corner
|
float vertY_ = 0; // of current cell corner
|
||||||
for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY)
|
for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY)
|
||||||
{
|
{
|
||||||
float vertX_ = 0; // of current cell corner
|
float vertX_ = 0; // of current cell corner
|
||||||
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
|
for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX)
|
||||||
{
|
{
|
||||||
const ESM::Land::LandData *heightData = getLandData (cellX, cellY, ESM::Land::DATA_VHGT);
|
const ESM::Land::LandData *heightData = getLandData (cellX, cellY, ESM::Land::DATA_VHGT);
|
||||||
const ESM::Land::LandData *normalData = getLandData (cellX, cellY, ESM::Land::DATA_VNML);
|
const ESM::Land::LandData *normalData = getLandData (cellX, cellY, ESM::Land::DATA_VNML);
|
||||||
|
@ -175,18 +177,31 @@ namespace ESMTerrain
|
||||||
int colStart = 0;
|
int colStart = 0;
|
||||||
// Skip the first row / column unless we're at a chunk edge,
|
// Skip the first row / column unless we're at a chunk edge,
|
||||||
// since this row / column is already contained in a previous cell
|
// 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 (colStart == 0 && vertY_ != 0)
|
if (colStart == 0 && vertY_ != 0)
|
||||||
colStart += increment;
|
colStart += increment;
|
||||||
if (rowStart == 0 && vertX_ != 0)
|
if (rowStart == 0 && vertX_ != 0)
|
||||||
rowStart += increment;
|
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 = rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1;
|
||||||
|
int colEnd = colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1;
|
||||||
|
|
||||||
vertY = vertY_;
|
vertY = vertY_;
|
||||||
for (int col=colStart; col<ESM::Land::LAND_SIZE; col += increment)
|
for (int col=colStart; col<colEnd; col += increment)
|
||||||
{
|
{
|
||||||
vertX = vertX_;
|
vertX = vertX_;
|
||||||
for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment)
|
for (int row=rowStart; row<rowEnd; row += increment)
|
||||||
{
|
{
|
||||||
int arrayIndex = col*ESM::Land::LAND_SIZE*3+row*3;
|
int srcArrayIndex = col*ESM::Land::LAND_SIZE*3+row*3;
|
||||||
|
|
||||||
|
assert(row >= 0 && row < ESM::Land::LAND_SIZE);
|
||||||
|
assert(col >= 0 && col < ESM::Land::LAND_SIZE);
|
||||||
|
|
||||||
|
assert (vertX < numVerts);
|
||||||
|
assert (vertY < numVerts);
|
||||||
|
|
||||||
float height = -2048;
|
float height = -2048;
|
||||||
if (heightData)
|
if (heightData)
|
||||||
|
@ -200,7 +215,7 @@ namespace ESMTerrain
|
||||||
if (normalData)
|
if (normalData)
|
||||||
{
|
{
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
normal[i] = normalData->mNormals[arrayIndex+i];
|
normal[i] = normalData->mNormals[srcArrayIndex+i];
|
||||||
|
|
||||||
normal.normalize();
|
normal.normalize();
|
||||||
}
|
}
|
||||||
|
@ -222,7 +237,7 @@ namespace ESMTerrain
|
||||||
if (colourData)
|
if (colourData)
|
||||||
{
|
{
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
color[i] = colourData->mColours[arrayIndex+i] / 255.f;
|
color[i] = colourData->mColours[srcArrayIndex+i] / 255.f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -305,8 +320,19 @@ namespace ESMTerrain
|
||||||
// and interpolate the rest of the cell by hand? :/
|
// and interpolate the rest of the cell by hand? :/
|
||||||
|
|
||||||
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f);
|
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f);
|
||||||
int cellX = static_cast<int>(origin.x());
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
||||||
int cellY = static_cast<int>(origin.y());
|
int cellY = static_cast<int>(std::floor(origin.y()));
|
||||||
|
|
||||||
|
int realTextureSize = ESM::Land::LAND_TEXTURE_SIZE+1; // add 1 to wrap around next cell
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
assert (rowStart >= 0 && colStart >= 0);
|
||||||
|
assert (rowEnd <= realTextureSize);
|
||||||
|
assert (colEnd <= realTextureSize);
|
||||||
|
|
||||||
// Save the used texture indices so we know the total number of textures
|
// Save the used texture indices so we know the total number of textures
|
||||||
// and number of required blend maps
|
// and number of required blend maps
|
||||||
|
@ -317,8 +343,15 @@ namespace ESMTerrain
|
||||||
// So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell.
|
// So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell.
|
||||||
textureIndices.insert(std::make_pair(0,0));
|
textureIndices.insert(std::make_pair(0,0));
|
||||||
|
|
||||||
for (int y=0; y<ESM::Land::LAND_TEXTURE_SIZE+1; ++y)
|
/*
|
||||||
for (int x=0; x<ESM::Land::LAND_TEXTURE_SIZE+1; ++x)
|
_________
|
||||||
|
| | | | - | | | | -
|
||||||
|
| | | | - | | | | -
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (int y=colStart; y<colEnd; ++y)
|
||||||
|
for (int x=rowStart; x<rowEnd; ++x)
|
||||||
{
|
{
|
||||||
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
|
||||||
textureIndices.insert(id);
|
textureIndices.insert(id);
|
||||||
|
@ -342,7 +375,7 @@ namespace ESMTerrain
|
||||||
int channels = pack ? 4 : 1;
|
int channels = pack ? 4 : 1;
|
||||||
|
|
||||||
// Second iteration - create and fill in the blend maps
|
// Second iteration - create and fill in the blend maps
|
||||||
const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1;
|
const int blendmapSize = (realTextureSize-1) * chunkSize + 1;
|
||||||
|
|
||||||
for (int i=0; i<numBlendmaps; ++i)
|
for (int i=0; i<numBlendmaps; ++i)
|
||||||
{
|
{
|
||||||
|
@ -356,7 +389,8 @@ namespace ESMTerrain
|
||||||
{
|
{
|
||||||
for (int x=0; x<blendmapSize; ++x)
|
for (int x=0; x<blendmapSize; ++x)
|
||||||
{
|
{
|
||||||
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart);
|
||||||
|
assert(textureIndicesMap.find(id) != textureIndicesMap.end());
|
||||||
int layerIndex = textureIndicesMap.find(id)->second;
|
int layerIndex = textureIndicesMap.find(id)->second;
|
||||||
int blendIndex = (pack ? static_cast<int>(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1);
|
int blendIndex = (pack ? static_cast<int>(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1);
|
||||||
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
|
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "buffercache.hpp"
|
#include "buffercache.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <osg/PrimitiveSet>
|
#include <osg/PrimitiveSet>
|
||||||
|
|
||||||
|
@ -208,6 +209,8 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
unsigned int verts = mNumVerts;
|
unsigned int verts = mNumVerts;
|
||||||
|
|
||||||
|
std::cout << "getting index buffer for " << verts << std::endl;
|
||||||
|
|
||||||
if (mIndexBufferMap.find(flags) != mIndexBufferMap.end())
|
if (mIndexBufferMap.find(flags) != mIndexBufferMap.end())
|
||||||
{
|
{
|
||||||
return mIndexBufferMap[flags];
|
return mIndexBufferMap[flags];
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
|
|
||||||
FixedFunctionTechnique::FixedFunctionTechnique(const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
|
FixedFunctionTechnique::FixedFunctionTechnique(const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
|
||||||
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapSize, float layerTileSize)
|
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize)
|
||||||
{
|
{
|
||||||
bool firstLayer = true;
|
bool firstLayer = true;
|
||||||
int i=0;
|
int i=0;
|
||||||
|
@ -36,7 +36,7 @@ namespace Terrain
|
||||||
|
|
||||||
// This is to map corner vertices directly to the center of a blendmap texel.
|
// This is to map corner vertices directly to the center of a blendmap texel.
|
||||||
osg::Matrixf texMat;
|
osg::Matrixf texMat;
|
||||||
float scale = (blendmapSize/(static_cast<float>(blendmapSize)+1.f));
|
float scale = (blendmapScale/(static_cast<float>(blendmapScale)+1.f));
|
||||||
texMat.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f));
|
texMat.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f));
|
||||||
texMat.preMultScale(osg::Vec3f(scale, scale, 1.f));
|
texMat.preMultScale(osg::Vec3f(scale, scale, 1.f));
|
||||||
texMat.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f));
|
texMat.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f));
|
||||||
|
@ -67,10 +67,10 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
|
|
||||||
Effect::Effect(const std::vector<osg::ref_ptr<osg::Texture2D> > &layers, const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps,
|
Effect::Effect(const std::vector<osg::ref_ptr<osg::Texture2D> > &layers, const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps,
|
||||||
int blendmapSize, float layerTileSize)
|
int blendmapScale, float layerTileSize)
|
||||||
: mLayers(layers)
|
: mLayers(layers)
|
||||||
, mBlendmaps(blendmaps)
|
, mBlendmaps(blendmaps)
|
||||||
, mBlendmapSize(blendmapSize)
|
, mBlendmapScale(blendmapScale)
|
||||||
, mLayerTileSize(layerTileSize)
|
, mLayerTileSize(layerTileSize)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Material> material (new osg::Material);
|
osg::ref_ptr<osg::Material> material (new osg::Material);
|
||||||
|
@ -82,7 +82,7 @@ namespace Terrain
|
||||||
|
|
||||||
bool Effect::define_techniques()
|
bool Effect::define_techniques()
|
||||||
{
|
{
|
||||||
addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapSize, mLayerTileSize));
|
addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Terrain
|
||||||
public:
|
public:
|
||||||
FixedFunctionTechnique(
|
FixedFunctionTechnique(
|
||||||
const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
|
const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
|
||||||
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapSize, float layerTileSize);
|
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void define_passes() {}
|
virtual void define_passes() {}
|
||||||
|
@ -30,7 +30,7 @@ namespace Terrain
|
||||||
public:
|
public:
|
||||||
Effect(
|
Effect(
|
||||||
const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
|
const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
|
||||||
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapSize, float layerTileSize);
|
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
|
||||||
|
|
||||||
virtual bool define_techniques();
|
virtual bool define_techniques();
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ namespace Terrain
|
||||||
private:
|
private:
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > mLayers;
|
std::vector<osg::ref_ptr<osg::Texture2D> > mLayers;
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > mBlendmaps;
|
std::vector<osg::ref_ptr<osg::Texture2D> > mBlendmaps;
|
||||||
int mBlendmapSize;
|
int mBlendmapScale;
|
||||||
float mLayerTileSize;
|
float mLayerTileSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "terraingrid.hpp"
|
#include "terraingrid.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <osg/io_utils>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/texturemanager.hpp>
|
#include <components/resource/texturemanager.hpp>
|
||||||
|
@ -47,8 +49,10 @@ namespace Terrain
|
||||||
TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico,
|
TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico,
|
||||||
Storage* storage, int nodeMask)
|
Storage* storage, int nodeMask)
|
||||||
: Terrain::World(parent, resourceSystem, ico, storage, nodeMask)
|
: Terrain::World(parent, resourceSystem, ico, storage, nodeMask)
|
||||||
|
, mNumSplits(4)
|
||||||
, mKdTreeBuilder(new osg::KdTreeBuilder)
|
, mKdTreeBuilder(new osg::KdTreeBuilder)
|
||||||
{
|
{
|
||||||
|
mCache = BufferCache((storage->getCellVertices()-1)/static_cast<float>(mNumSplits) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TerrainGrid::~TerrainGrid()
|
TerrainGrid::~TerrainGrid()
|
||||||
|
@ -62,108 +66,149 @@ TerrainGrid::~TerrainGrid()
|
||||||
class GridElement
|
class GridElement
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
osg::ref_ptr<osg::PositionAttitudeTransform> mNode;
|
osg::ref_ptr<osg::Node> mNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter)
|
||||||
|
{
|
||||||
|
if (chunkSize * mNumSplits > 1.f)
|
||||||
|
{
|
||||||
|
// keep splitting
|
||||||
|
osg::ref_ptr<osg::Group> group (new osg::Group);
|
||||||
|
if (parent)
|
||||||
|
parent->addChild(group);
|
||||||
|
std::cout << "splitting " << chunkSize << " " << std::endl;
|
||||||
|
float newChunkSize = chunkSize/2.f;
|
||||||
|
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, newChunkSize/2.f));
|
||||||
|
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, -newChunkSize/2.f));
|
||||||
|
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, newChunkSize/2.f));
|
||||||
|
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, -newChunkSize/2.f));
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float minH, maxH;
|
||||||
|
if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH))
|
||||||
|
return NULL; // no terrain defined
|
||||||
|
|
||||||
|
std::cout << "creating " << chunkSize << " " << chunkCenter << std::endl;
|
||||||
|
|
||||||
|
osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize();
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform);
|
||||||
|
transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f));
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
parent->addChild(transform);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
|
||||||
|
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
|
||||||
|
osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
|
||||||
|
positions->setVertexBufferObject(vbo);
|
||||||
|
normals->setVertexBufferObject(vbo);
|
||||||
|
colors->setVertexBufferObject(vbo);
|
||||||
|
|
||||||
|
mStorage->fillVertexBuffers(0, chunkSize, chunkCenter, positions, normals, colors);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
||||||
|
geometry->setVertexArray(positions);
|
||||||
|
geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
|
||||||
|
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
|
||||||
|
geometry->setUseDisplayList(false);
|
||||||
|
geometry->setUseVertexBufferObjects(true);
|
||||||
|
|
||||||
|
geometry->addPrimitiveSet(mCache.getIndexBuffer(0));
|
||||||
|
|
||||||
|
// we already know the bounding box, so no need to let OSG compute it.
|
||||||
|
osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize,
|
||||||
|
-0.5f*mStorage->getCellWorldSize()*chunkSize,
|
||||||
|
minH);
|
||||||
|
osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize,
|
||||||
|
0.5f*mStorage->getCellWorldSize()*chunkSize,
|
||||||
|
maxH);
|
||||||
|
osg::BoundingBox bounds(min, max);
|
||||||
|
geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds));
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
||||||
|
geode->addDrawable(geometry);
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<LayerInfo> layerList;
|
||||||
|
std::vector<osg::ref_ptr<osg::Image> > blendmaps;
|
||||||
|
mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList);
|
||||||
|
|
||||||
|
// For compiling textures, I don't think the osgFX::Effect does it correctly
|
||||||
|
osg::ref_ptr<osg::Node> textureCompileDummy (new osg::Node);
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<osg::Texture2D> > layerTextures;
|
||||||
|
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
|
||||||
|
{
|
||||||
|
layerTextures.push_back(mResourceSystem->getTextureManager()->getTexture2D(it->mDiffuseMap, osg::Texture::REPEAT, osg::Texture::REPEAT));
|
||||||
|
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;
|
||||||
|
for (std::vector<osg::ref_ptr<osg::Image> >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
|
||||||
|
texture->setImage(*it);
|
||||||
|
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||||
|
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
|
texture->setResizeNonPowerOfTwoHint(false);
|
||||||
|
blendmapTextures.push_back(texture);
|
||||||
|
|
||||||
|
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
// use texture coordinates for both texture units, the layer texture and blend texture
|
||||||
|
for (unsigned int i=0; i<2; ++i)
|
||||||
|
geometry->setTexCoordArray(i, mCache.getUVBuffer());
|
||||||
|
|
||||||
|
float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize;
|
||||||
|
osg::ref_ptr<osgFX::Effect> effect (new Terrain::Effect(layerTextures, blendmapTextures, blendmapScale, blendmapScale));
|
||||||
|
|
||||||
|
effect->addCullCallback(new SceneUtil::LightListCallback);
|
||||||
|
|
||||||
|
transform->addChild(effect);
|
||||||
|
effect->addChild(geode);
|
||||||
|
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TerrainGrid::loadCell(int x, int y)
|
void TerrainGrid::loadCell(int x, int y)
|
||||||
{
|
{
|
||||||
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
|
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
|
||||||
return; // already loaded
|
return; // already loaded
|
||||||
|
|
||||||
osg::Vec2f center(x+0.5f, y+0.5f);
|
osg::Vec2f center(x+0.5f, y+0.5f);
|
||||||
float minH, maxH;
|
|
||||||
if (!mStorage->getMinMaxHeights(1, center, minH, maxH))
|
osg::ref_ptr<osg::Node> terrainNode = buildTerrain(NULL, 1.f, center);
|
||||||
|
if (!terrainNode)
|
||||||
return; // no terrain defined
|
return; // no terrain defined
|
||||||
|
|
||||||
std::auto_ptr<GridElement> element (new GridElement);
|
std::auto_ptr<GridElement> element (new GridElement);
|
||||||
|
element->mNode = terrainNode;
|
||||||
osg::Vec2f worldCenter = center*mStorage->getCellWorldSize();
|
|
||||||
element->mNode = new osg::PositionAttitudeTransform;
|
|
||||||
element->mNode->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f));
|
|
||||||
mTerrainRoot->addChild(element->mNode);
|
mTerrainRoot->addChild(element->mNode);
|
||||||
|
|
||||||
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
|
/*
|
||||||
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
|
|
||||||
osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
|
|
||||||
positions->setVertexBufferObject(vbo);
|
|
||||||
normals->setVertexBufferObject(vbo);
|
|
||||||
colors->setVertexBufferObject(vbo);
|
|
||||||
|
|
||||||
mStorage->fillVertexBuffers(0, 1, center, positions, normals, colors);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
|
||||||
geometry->setVertexArray(positions);
|
|
||||||
geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
|
|
||||||
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
|
|
||||||
geometry->setUseDisplayList(false);
|
|
||||||
geometry->setUseVertexBufferObjects(true);
|
|
||||||
|
|
||||||
geometry->addPrimitiveSet(mCache.getIndexBuffer(0));
|
|
||||||
|
|
||||||
// we already know the bounding box, so no need to let OSG compute it.
|
|
||||||
osg::Vec3f min(-0.5f*mStorage->getCellWorldSize(),
|
|
||||||
-0.5f*mStorage->getCellWorldSize(),
|
|
||||||
minH);
|
|
||||||
osg::Vec3f max (0.5f*mStorage->getCellWorldSize(),
|
|
||||||
0.5f*mStorage->getCellWorldSize(),
|
|
||||||
maxH);
|
|
||||||
osg::BoundingBox bounds(min, max);
|
|
||||||
geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds));
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
|
||||||
geode->addDrawable(geometry);
|
|
||||||
|
|
||||||
// build a kdtree to speed up intersection tests with the terrain
|
// build a kdtree to speed up intersection tests with the terrain
|
||||||
// Note, the build could be optimized using a custom kdtree builder, since we know that the terrain can be represented by a quadtree
|
// Note, the build could be optimized using a custom kdtree builder, since we know that the terrain can be represented by a quadtree
|
||||||
geode->accept(*mKdTreeBuilder);
|
geode->accept(*mKdTreeBuilder);
|
||||||
|
*/
|
||||||
|
|
||||||
std::vector<LayerInfo> layerList;
|
|
||||||
std::vector<osg::ref_ptr<osg::Image> > blendmaps;
|
|
||||||
mStorage->getBlendmaps(1.f, center, false, blendmaps, layerList);
|
|
||||||
|
|
||||||
// For compiling textures, I don't think the osgFX::Effect does it correctly
|
|
||||||
osg::ref_ptr<osg::Node> textureCompileDummy (new osg::Node);
|
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > layerTextures;
|
|
||||||
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
|
|
||||||
{
|
|
||||||
layerTextures.push_back(mResourceSystem->getTextureManager()->getTexture2D(it->mDiffuseMap, osg::Texture::REPEAT, osg::Texture::REPEAT));
|
|
||||||
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;
|
|
||||||
for (std::vector<osg::ref_ptr<osg::Image> >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
|
|
||||||
{
|
|
||||||
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
|
|
||||||
texture->setImage(*it);
|
|
||||||
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
|
||||||
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
|
||||||
texture->setResizeNonPowerOfTwoHint(false);
|
|
||||||
blendmapTextures.push_back(texture);
|
|
||||||
|
|
||||||
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
// use texture coordinates for both texture units, the layer texture and blend texture
|
|
||||||
for (unsigned int i=0; i<2; ++i)
|
|
||||||
geometry->setTexCoordArray(i, mCache.getUVBuffer());
|
|
||||||
|
|
||||||
osg::ref_ptr<osgFX::Effect> effect (new Terrain::Effect(layerTextures, blendmapTextures, ESM::Land::LAND_TEXTURE_SIZE, ESM::Land::LAND_TEXTURE_SIZE));
|
|
||||||
|
|
||||||
effect->addCullCallback(new SceneUtil::LightListCallback);
|
|
||||||
|
|
||||||
effect->addChild(geode);
|
|
||||||
element->mNode->addChild(effect);
|
|
||||||
|
|
||||||
|
/*
|
||||||
if (mIncrementalCompileOperation)
|
if (mIncrementalCompileOperation)
|
||||||
{
|
{
|
||||||
mIncrementalCompileOperation->add(geode);
|
mIncrementalCompileOperation->add(geode);
|
||||||
mIncrementalCompileOperation->add(textureCompileDummy);
|
mIncrementalCompileOperation->add(textureCompileDummy);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
mGrid[std::make_pair(x,y)] = element.release();
|
mGrid[std::make_pair(x,y)] = element.release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef COMPONENTS_TERRAIN_TERRAINGRID_H
|
#ifndef COMPONENTS_TERRAIN_TERRAINGRID_H
|
||||||
#define COMPONENTS_TERRAIN_TERRAINGRID_H
|
#define COMPONENTS_TERRAIN_TERRAINGRID_H
|
||||||
|
|
||||||
|
#include <osg/Vec2f>
|
||||||
|
|
||||||
#include "world.hpp"
|
#include "world.hpp"
|
||||||
#include "material.hpp"
|
#include "material.hpp"
|
||||||
|
|
||||||
|
@ -26,6 +28,11 @@ namespace Terrain
|
||||||
virtual void unloadCell(int x, int y);
|
virtual void unloadCell(int x, int y);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);
|
||||||
|
|
||||||
|
// split each ESM::Cell into mNumSplits*mNumSplits terrain chunks
|
||||||
|
unsigned int mNumSplits;
|
||||||
|
|
||||||
typedef std::map<std::pair<int, int>, GridElement*> Grid;
|
typedef std::map<std::pair<int, int>, GridElement*> Grid;
|
||||||
Grid mGrid;
|
Grid mGrid;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue