mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 12:45:33 +00:00
Terrain: support alternate coordinate systems. Get rid of LoadingListener for now
This commit is contained in:
parent
ab224f93c9
commit
c9e349f60f
11 changed files with 147 additions and 57 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
41
components/terrain/defs.hpp
Normal file
41
components/terrain/defs.hpp
Normal file
|
@ -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
|
|
@ -235,7 +235,6 @@ namespace Terrain
|
|||
|
||||
|
||||
sh::MaterialInstancePass* p = material->createPass ();
|
||||
|
||||
p->setProperty ("vertex_program", sh::makeProperty<sh::StringValue>(new sh::StringValue("terrain_vertex")));
|
||||
p->setProperty ("fragment_program", sh::makeProperty<sh::StringValue>(new sh::StringValue("terrain_fragment")));
|
||||
if (layerOffset != 0)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#include <OgreVector2.h>
|
||||
#include <OgreTexture.h>
|
||||
|
||||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
|
||||
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!
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef COMPONENTS_TERRAIN_STORAGE_H
|
||||
#define COMPONENTS_TERRAIN_STORAGE_H
|
||||
|
||||
#include <OgreAxisAlignedBox.h>
|
||||
|
||||
#include <OgreHardwareVertexBuffer.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@
|
|||
#include <OgreAxisAlignedBox.h>
|
||||
#include <OgreTexture.h>
|
||||
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue