Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
14 kB · 315 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316#include "chunkmanager.hpp"
#include <osg/Texture2D>
#include <osgUtil/IncrementalCompileOperation>
#include <components/esm/util.hpp>#include <components/resource/objectcache.hpp>#include <components/resource/scenemanager.hpp>#include <components/sceneutil/lightmanager.hpp>#include <components/sceneutil/material.hpp>
#include "compositemaprenderer.hpp"#include "material.hpp"#include "storage.hpp"#include "terraindrawable.hpp"#include "texturemanager.hpp"
namespace Terrain{
struct UpdateTextureFilteringFunctor { UpdateTextureFilteringFunctor(Resource::SceneManager* sceneMgr) : mSceneManager(sceneMgr) { } Resource::SceneManager* mSceneManager;
void operator()(ChunkKey, osg::Object* obj) { TerrainDrawable* drawable = static_cast<TerrainDrawable*>(obj); CompositeMap* composite = drawable->getCompositeMap(); if (composite && composite->mTexture) mSceneManager->applyFilterSettings(composite->mTexture); } };
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer, ESM::RefId worldspace, double expiryDelay) : Resource::NodeResourceManager<ChunkKey>(nullptr, expiryDelay) , QuadTreeWorld::ChunkManager(worldspace) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) , mCompositeMapRenderer(renderer) , mNodeMask(0) , mCompositeMapSize(512) , mCompositeMapLevel(1.f) , mMaxCompGeometrySize(1.f) { mMultiPassRoot = new osg::StateSet; mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN); osg::ref_ptr<SceneUtil::Material> material( new SceneUtil::Material({ .mVertexColorMode = SceneUtil::VertexColorModes::AmbientAndDiffuse })); material->setVertexColorMode(SceneUtil::VertexColorModes::AmbientAndDiffuse); material->updateStateSet(mMultiPassRoot); mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON); }
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { // Override lod with the vertexLodMod adjusted value. // TODO: maybe we can refactor this code by moving all vertexLodMod code into this class. lod = static_cast<unsigned char>(lodFlags >> (4 * 4));
const ChunkKey key{ .mCenter = center, .mLod = lod, .mLodFlags = lodFlags }; if (osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(key)) return static_cast<osg::Node*>(obj.get());
const TerrainDrawable* templateGeometry = nullptr; const TemplateKey templateKey{ .mCenter = center, .mLod = lod }; const auto pair = mCache->lowerBound(templateKey); if (pair.has_value() && templateKey == TemplateKey{ .mCenter = pair->first.mCenter, .mLod = pair->first.mLod }) templateGeometry = static_cast<const TerrainDrawable*>(pair->second.get());
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry); mCache->addEntryToObjectCache(key, node.get()); return node; }
void ChunkManager::updateTextureFiltering() { UpdateTextureFilteringFunctor f(mSceneManager); mCache->call(f); }
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const { Resource::reportStats("Terrain Chunk", frameNumber, mCache->getStats(), *stats); }
void ChunkManager::clearCache() { Resource::NodeResourceManager<ChunkKey>::clearCache();
mBufferCache.clearCache(); }
void ChunkManager::releaseGLObjects(osg::State* state) { Resource::NodeResourceManager<ChunkKey>::releaseGLObjects(state); mBufferCache.releaseGLObjects(state); }
osg::ref_ptr<osg::Texture2D> ChunkManager::createCompositeMapRTT() { osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureWidth(mCompositeMapSize); texture->setTextureHeight(mCompositeMapSize); texture->setInternalFormat(GL_RGB); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mSceneManager->applyFilterSettings(texture);
return texture; }
void ChunkManager::createCompositeMapGeometry( float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& compositeMap) { if (chunkSize > mMaxCompGeometrySize) { createCompositeMapGeometry(chunkSize / 2.f, chunkCenter + osg::Vec2f(chunkSize / 4.f, chunkSize / 4.f), osg::Vec4f(texCoords.x() + texCoords.z() / 2.f, texCoords.y() + texCoords.w() / 2.f, texCoords.z() / 2.f, texCoords.w() / 2.f), compositeMap); createCompositeMapGeometry(chunkSize / 2.f, chunkCenter + osg::Vec2f(-chunkSize / 4.f, chunkSize / 4.f), osg::Vec4f( texCoords.x(), texCoords.y() + texCoords.w() / 2.f, texCoords.z() / 2.f, texCoords.w() / 2.f), compositeMap); createCompositeMapGeometry(chunkSize / 2.f, chunkCenter + osg::Vec2f(chunkSize / 4.f, -chunkSize / 4.f), osg::Vec4f( texCoords.x() + texCoords.z() / 2.f, texCoords.y(), texCoords.z() / 2.f, texCoords.w() / 2.f), compositeMap); createCompositeMapGeometry(chunkSize / 2.f, chunkCenter + osg::Vec2f(-chunkSize / 4.f, -chunkSize / 4.f), osg::Vec4f(texCoords.x(), texCoords.y(), texCoords.z() / 2.f, texCoords.w() / 2.f), compositeMap); } else { float left = texCoords.x() * 2.f - 1; float top = texCoords.y() * 2.f - 1; float width = texCoords.z() * 2.f; float height = texCoords.w() * 2.f;
std::vector<osg::ref_ptr<osg::StateSet>> passes = createPasses(chunkSize, chunkCenter, true); for (std::vector<osg::ref_ptr<osg::StateSet>>::iterator it = passes.begin(); it != passes.end(); ++it) { osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry( osg::Vec3(left, top, 0), osg::Vec3(width, 0, 0), osg::Vec3(0, height, 0)); geom->setUseDisplayList( false); // don't bother making a display list for an object that is just rendered once. geom->setUseVertexBufferObjects(false); geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
geom->setStateSet(*it);
compositeMap.mDrawables.emplace_back(geom); } } }
std::vector<osg::ref_ptr<osg::StateSet>> ChunkManager::createPasses( float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap) { std::vector<LayerInfo> layerList; std::vector<osg::ref_ptr<osg::Image>> blendmaps; mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList, mWorldspace);
std::vector<TextureLayer> layers; { for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { TextureLayer textureLayer; textureLayer.mParallax = it->mParallax; textureLayer.mSpecular = it->mSpecular;
textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap);
if (!forCompositeMap && !it->mNormalMap.empty()) textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap);
layers.push_back(textureLayer); } }
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->setResizeNonPowerOfTwoHint(false); blendmapTextures.push_back(texture); }
int tileCount = mStorage->getTextureTileCount(chunkSize, mWorldspace);
return ::Terrain::createPasses(mSceneManager, layers, blendmapTextures, tileCount, static_cast<float>(tileCount), forCompositeMap, ESM::isEsm4Ext(mWorldspace)); }
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile, const TerrainDrawable* templateGeometry) { osg::ref_ptr<TerrainDrawable> geometry(new TerrainDrawable);
if (!templateGeometry) { osg::ref_ptr<osg::Vec3Array> positions(new osg::Vec3Array); osg::ref_ptr<osg::Vec3Array> normals(new osg::Vec3Array); osg::ref_ptr<osg::Vec4ubArray> colors(new osg::Vec4ubArray); colors->setNormalize(true);
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, mWorldspace, *positions, *normals, *colors);
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject); positions->setVertexBufferObject(vbo); normals->setVertexBufferObject(vbo); colors->setVertexBufferObject(vbo);
geometry->setVertexArray(positions); geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); } else { // Unfortunately we need to copy vertex data because of poor coupling with VertexBufferObject. osg::ref_ptr<osg::Array> positions = static_cast<osg::Array*>(templateGeometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::ref_ptr<osg::Array> normals = static_cast<osg::Array*>(templateGeometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::ref_ptr<osg::Array> colors = static_cast<osg::Array*>(templateGeometry->getColorArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject); positions->setVertexBufferObject(vbo); normals->setVertexBufferObject(vbo); colors->setVertexBufferObject(vbo);
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);
if (chunkSize <= 1.f) geometry->setLightListCallback(new SceneUtil::LightListCallback);
unsigned int numVerts = static_cast<unsigned>((mStorage->getCellVertices(mWorldspace) - 1) * chunkSize / (1 << lod) + 1);
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));
bool useCompositeMap = chunkSize >= mCompositeMapLevel; unsigned int numUvSets = useCompositeMap ? 1 : 2;
geometry->setTexCoordArrayList(osg::Geometry::ArrayList(numUvSets, mBufferCache.getUVBuffer(numVerts)));
geometry->createClusterCullingCallback();
geometry->setStateSet(mMultiPassRoot);
if (templateGeometry) { if (templateGeometry->getCompositeMap()) { geometry->setCompositeMap(templateGeometry->getCompositeMap()); geometry->setCompositeMapRenderer(mCompositeMapRenderer); } geometry->setPasses(templateGeometry->getPasses()); } else { if (useCompositeMap) { osg::ref_ptr<CompositeMap> compositeMap = new CompositeMap; compositeMap->mTexture = createCompositeMapRTT();
createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0, 0, 1, 1), *compositeMap);
mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false);
geometry->setCompositeMap(compositeMap); geometry->setCompositeMapRenderer(mCompositeMapRenderer);
TextureLayer layer; layer.mDiffuseMap = compositeMap->mTexture; layer.mParallax = false; layer.mSpecular = false; geometry->setPasses(::Terrain::createPasses(mSceneManager, std::vector<TextureLayer>(1, layer), std::vector<osg::ref_ptr<osg::Texture2D>>(), 1, 1.f, false)); } else { geometry->setPasses(createPasses(chunkSize, chunkCenter, false)); } }
geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize(mWorldspace) / numVerts);
if (!templateGeometry && compile && mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } geometry->setNodeMask(mNodeMask);
return geometry; }
}