From c9e349f60f2823cd7c98fdc13c2d5f4a8ee67c46 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Feb 2014 18:48:35 +0100 Subject: [PATCH] Terrain: support alternate coordinate systems. Get rid of LoadingListener for now --- apps/openmw/mwrender/renderingmanager.cpp | 7 +-- apps/openmw/mwrender/terrainstorage.cpp | 2 +- apps/openmw/mwrender/terrainstorage.hpp | 2 +- components/terrain/chunk.cpp | 2 +- components/terrain/defs.hpp | 41 ++++++++++++++ components/terrain/material.cpp | 1 - components/terrain/quadtreenode.cpp | 46 ++++++++++++---- components/terrain/quadtreenode.hpp | 6 +-- components/terrain/storage.hpp | 6 +-- components/terrain/world.cpp | 66 +++++++++++++++-------- components/terrain/world.hpp | 25 +++++---- 11 files changed, 147 insertions(+), 57 deletions(-) create mode 100644 components/terrain/defs.hpp diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 928a34107..cf97afa7a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1043,15 +1043,12 @@ void RenderingManager::enableTerrain(bool enable) { if (!mTerrain) { - Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(listener); - mTerrain = new Terrain::World(listener, mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::World(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, Settings::Manager::getBool("distant land", "Terrain"), - Settings::Manager::getBool("shader", "Terrain")); + Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), Settings::Manager::getBool("split", "Shadows")); mTerrain->update(mRendering.getCamera()->getRealPosition()); - mTerrain->setLoadingListener(NULL); } mTerrain->setVisible(true); } diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 750441f6a..2ccf3bd0a 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -167,7 +167,7 @@ namespace MWRender } - void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, Ogre::HardwareVertexBufferSharedPtr colourBuffer) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 2ef014aaf..f84da5c5d 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -36,7 +36,7 @@ namespace MWRender /// @param vertexBuffer buffer to write vertices /// @param normalBuffer buffer to write vertex normals /// @param colourBuffer buffer to write vertex colours - virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, Ogre::HardwareVertexBufferSharedPtr colourBuffer); diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index a5c629088..a05066313 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -52,7 +52,7 @@ namespace Terrain mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), + mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), mNode->getTerrain()->getAlign(), mVertexBuffer, mNormalBuffer, mColourBuffer); mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp new file mode 100644 index 000000000..9f0b8c09b --- /dev/null +++ b/components/terrain/defs.hpp @@ -0,0 +1,41 @@ +#ifndef COMPONENTS_TERRAIN_DEFS_HPP +#define COMPONENTS_TERRAIN_DEFS_HPP + +namespace Terrain +{ + + /// The alignment of the terrain + enum Alignment + { + /// Terrain is in the X/Z plane + Align_XZ = 0, + /// Terrain is in the X/Y plane + Align_XY = 1, + /// Terrain is in the Y/Z plane. + /// UNTESTED - use at own risk. + /// Besides, X as up axis? What is wrong with you? ;) + Align_YZ = 2 + }; + + inline void convertPosition(Alignment align, float &x, float &y, float &z) + { + switch (align) + { + case Align_XY: + return; + case Align_XZ: + std::swap(y, z); + // This is since -Z should be going *into* the screen + // If not doing this, we'd get wrong vertex winding + z *= -1; + return; + case Align_YZ: + std::swap(x, y); + std::swap(y, z); + return; + } + } + +} + +#endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 8e78d2216..31874af51 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -235,7 +235,6 @@ namespace Terrain sh::MaterialInstancePass* p = material->createPass (); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); if (layerOffset != 0) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 82ccc7c89..cb7660fed 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -169,7 +169,11 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const pos = mParent->getCenter(); pos = mCenter - pos; float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - mSceneNode->setPosition(Ogre::Vector3(pos.x*cellWorldSize, pos.y*cellWorldSize, 0)); + + Ogre::Vector3 sceneNodePos (pos.x*cellWorldSize, pos.y*cellWorldSize, 0); + mTerrain->convertPosition(sceneNodePos.x, sceneNodePos.y, sceneNodePos.z); + + mSceneNode->setPosition(sceneNodePos); mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } @@ -212,11 +216,31 @@ void QuadTreeNode::initAabb() mChildren[i]->initAabb(); mBounds.merge(mChildren[i]->getBoundingBox()); } - mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, mBounds.getMinimum().z), - Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, mBounds.getMaximum().z)); + float minH, maxH; + switch (mTerrain->getAlign()) + { + case Terrain::Align_XY: + minH = mBounds.getMinimum().z; + maxH = mBounds.getMaximum().z; + break; + case Terrain::Align_XZ: + minH = mBounds.getMinimum().y; + maxH = mBounds.getMinimum().y; + break; + case Terrain::Align_YZ: + minH = mBounds.getMinimum().x; + maxH = mBounds.getMaximum().x; + break; + } + Ogre::Vector3 min(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, minH); + Ogre::Vector3 max(Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, maxH)); + mBounds = Ogre::AxisAlignedBox (min, max); + mTerrain->convertBounds(mBounds); } - mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0), - mBounds.getMaximum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0)); + Ogre::Vector3 offset(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0); + mTerrain->convertPosition(offset); + mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + offset, + mBounds.getMaximum() + offset); } void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) @@ -229,7 +253,12 @@ const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox() return mBounds; } -void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loadingListener) +const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox() +{ + return mWorldBounds; +} + +void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) { const Ogre::AxisAlignedBox& bounds = getBoundingBox(); if (bounds.isNull()) @@ -263,9 +292,6 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loa bool hadChunk = hasChunk(); - if (loadingListener) - loadingListener->indicateProgress(); - if (!distantLand && dist > 8192*2) { if (mIsActive) @@ -353,7 +379,7 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loa } assert(hasChildren() && "Leaf node's LOD needs to be 0"); for (int i=0; i<4; ++i) - mChildren[i]->update(cameraPos, loadingListener); + mChildren[i]->update(cameraPos); } } diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index ea299c096..32c7dd56a 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -5,8 +5,6 @@ #include #include -#include - namespace Ogre { class Rectangle2D; @@ -95,10 +93,12 @@ namespace Terrain /// Get bounding box in local coordinates const Ogre::AxisAlignedBox& getBoundingBox(); + const Ogre::AxisAlignedBox& getWorldBoundingBox(); + World* getTerrain() { return mTerrain; } /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. - void update (const Ogre::Vector3& cameraPos, Loading::Listener* loadingListener); + void update (const Ogre::Vector3& cameraPos); /// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided. /// Call after QuadTreeNode::update! diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index d8cdab9ec..d7fb78492 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -1,10 +1,10 @@ #ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H -#include - #include +#include "defs.hpp" + namespace Terrain { @@ -43,7 +43,7 @@ namespace Terrain /// @param vertexBuffer buffer to write vertices /// @param normalBuffer buffer to write vertex normals /// @param colourBuffer buffer to write vertex colours - virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 4273f227d..49a561c0e 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -6,8 +6,6 @@ #include #include -#include - #include "storage.hpp" #include "quadtreenode.hpp" @@ -51,27 +49,25 @@ namespace namespace Terrain { - World::World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool distantLand, bool shaders) + World::World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) : mStorage(storage) - , mMinBatchSize(1) - , mMaxBatchSize(64) + , mMinBatchSize(minBatchSize) + , mMaxBatchSize(maxBatchSize) , mSceneMgr(sceneMgr) , mVisibilityFlags(visibilityFlags) , mDistantLand(distantLand) , mShaders(shaders) , mVisible(true) - , mLoadingListener(loadingListener) + , mAlign(align) , mMaxX(0) , mMinX(0) , mMaxY(0) , mMinY(0) { - loadingListener->setLabel("Creating terrain"); - loadingListener->indicateProgress(); - mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + /// \todo make composite map size configurable Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -96,11 +92,11 @@ namespace Terrain mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); buildQuadTree(mRootNode); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); mRootNode->initAabb(); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); mRootNode->initNeighbours(); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); } World::~World() @@ -120,8 +116,12 @@ namespace Terrain Ogre::Vector2 center = node->getCenter(); float cellWorldSize = getStorage()->getCellWorldSize(); if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) - node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), - Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ))); + { + Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), + Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); + convertBounds(bounds); + node->setBoundingBox(bounds); + } else node->markAsDummy(); // no data available for this node, skip it return; @@ -161,7 +161,7 @@ namespace Terrain { if (!mVisible) return; - mRootNode->update(cameraPos, mLoadingListener); + mRootNode->update(cameraPos); mRootNode->updateIndexBuffers(); } @@ -173,11 +173,7 @@ namespace Terrain || center.y < mMinY) return Ogre::AxisAlignedBox::BOX_NULL; QuadTreeNode* node = findNode(center, mRootNode); - Ogre::AxisAlignedBox box = node->getBoundingBox(); - float cellWorldSize = getStorage()->getCellWorldSize(); - box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize, - box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize); - return box; + return node->getWorldBoundingBox(); } Ogre::HardwareVertexBufferSharedPtr World::getVertexBuffer(int numVertsOneSide) @@ -414,5 +410,33 @@ namespace Terrain return mVisible; } + void World::convertPosition(float &x, float &y, float &z) + { + Terrain::convertPosition(mAlign, x, y, z); + } + + void World::convertPosition(Ogre::Vector3 &pos) + { + convertPosition(pos.x, pos.y, pos.z); + } + + void World::convertBounds(Ogre::AxisAlignedBox& bounds) + { + switch (mAlign) + { + case Align_XY: + return; + case Align_XZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + // Because we changed sign of Z + std::swap(bounds.getMinimum().z, bounds.getMaximum().z); + return; + case Align_YZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + return; + } + } } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index bf733b889..31bd51ab1 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -6,10 +6,7 @@ #include #include -namespace Loading -{ - class Listener; -} +#include "defs.hpp" namespace Ogre { @@ -33,7 +30,6 @@ namespace Terrain { public: /// @note takes ownership of \a storage - /// @param loadingListener Listener to update with progress /// @param sceneMgr scene manager to use /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param visbilityFlags visibility flags for the created meshes @@ -41,12 +37,13 @@ namespace Terrain /// This is a temporary option until it can be streamlined. /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually /// faster so this is just here for compatibility. - World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, - Storage* storage, int visiblityFlags, bool distantLand, bool shaders); + /// @param align The align of the terrain, see Alignment enum + /// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree. + /// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree. + World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visiblityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize); ~World(); - void setLoadingListener(Loading::Listener* loadingListener) { mLoadingListener = loadingListener; } - bool getDistantLandEnabled() { return mDistantLand; } bool getShadersEnabled() { return mShaders; } bool getShadowsEnabled() { return mShadows; } @@ -86,14 +83,15 @@ namespace Terrain void enableSplattingShader(bool enabled); + Alignment getAlign() { return mAlign; } + private: bool mDistantLand; bool mShaders; bool mShadows; bool mSplitShadows; bool mVisible; - - Loading::Listener* mLoadingListener; + Alignment mAlign; QuadTreeNode* mRootNode; Ogre::SceneNode* mRootSceneNode; @@ -138,6 +136,11 @@ namespace Terrain void clearCompositeMapSceneManager(); void renderCompositeMap (Ogre::TexturePtr target); + // Convert the given position from Z-up align, i.e. Align_XY to the wanted align set in mAlign + void convertPosition (float& x, float& y, float& z); + void convertPosition (Ogre::Vector3& pos); + void convertBounds (Ogre::AxisAlignedBox& bounds); + private: // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need.