forked from teamnwah/openmw-tes3coop
Enabled terrain self shadows, implemented getHeightAt, some optimizations
This commit is contained in:
parent
d727b15580
commit
ebf9debb80
22 changed files with 421 additions and 164 deletions
|
@ -69,8 +69,8 @@ void OMW::Engine::setAnimationVerbose(bool animverbose)
|
|||
|
||||
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
|
||||
{
|
||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame);
|
||||
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused);
|
||||
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
|
||||
return true;
|
||||
}
|
||||
|
@ -91,12 +91,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
|||
MWBase::Environment::get().getSoundManager()->update(frametime);
|
||||
|
||||
// global scripts
|
||||
//MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
|
||||
|
||||
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
|
||||
|
||||
// local scripts
|
||||
//executeLocalScripts(); // This does not handle the case where a global script causes a cell
|
||||
executeLocalScripts(); // This does not handle the case where a global script causes a cell
|
||||
// change, followed by a cell change in a local script during the same
|
||||
// frame.
|
||||
|
||||
|
|
|
@ -371,7 +371,7 @@ namespace MWBase
|
|||
/// \todo this does not belong here
|
||||
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
||||
virtual void stopVideo() = 0;
|
||||
virtual void frameStarted (float dt) = 0;
|
||||
virtual void frameStarted (float dt, bool paused) = 0;
|
||||
|
||||
/// Find default position inside exterior cell specified by name
|
||||
/// \return false if exterior with given name not exists, true otherwise
|
||||
|
|
|
@ -125,7 +125,7 @@ namespace MWGui
|
|||
getWidget(mActorShadows, "ActorShadows");
|
||||
getWidget(mStaticsShadows, "StaticsShadows");
|
||||
getWidget(mMiscShadows, "MiscShadows");
|
||||
getWidget(mShadowsDebug, "ShadowsDebug");
|
||||
getWidget(mTerrainShadows, "TerrainShadows");
|
||||
getWidget(mControlsBox, "ControlsBox");
|
||||
getWidget(mResetControlsButton, "ResetControlsButton");
|
||||
getWidget(mInvertYButton, "InvertYButton");
|
||||
|
@ -161,7 +161,7 @@ namespace MWGui
|
|||
mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mMiscShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mShadowsDebug->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mTerrainShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
|
||||
mMasterVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mVoiceVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
|
@ -238,7 +238,7 @@ namespace MWGui
|
|||
mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||
mStaticsShadows->setCaptionWithReplacing(Settings::Manager::getBool("statics shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||
mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||
mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||
mTerrainShadows->setCaptionWithReplacing(Settings::Manager::getBool("terrain shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||
|
||||
float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2);
|
||||
mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1));
|
||||
|
@ -394,8 +394,8 @@ namespace MWGui
|
|||
Settings::Manager::setBool("statics shadows", "Shadows", newState);
|
||||
else if (_sender == mMiscShadows)
|
||||
Settings::Manager::setBool("misc shadows", "Shadows", newState);
|
||||
else if (_sender == mShadowsDebug)
|
||||
Settings::Manager::setBool("debug", "Shadows", newState);
|
||||
else if (_sender == mTerrainShadows)
|
||||
Settings::Manager::setBool("terrain shadows", "Shadows", newState);
|
||||
else if (_sender == mInvertYButton)
|
||||
Settings::Manager::setBool("invert y axis", "Input", newState);
|
||||
else if (_sender == mCrosshairButton)
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace MWGui
|
|||
MyGUI::Button* mActorShadows;
|
||||
MyGUI::Button* mStaticsShadows;
|
||||
MyGUI::Button* mMiscShadows;
|
||||
MyGUI::Button* mShadowsDebug;
|
||||
MyGUI::Button* mTerrainShadows;
|
||||
|
||||
// audio
|
||||
MyGUI::ScrollBar* mMasterVolumeSlider;
|
||||
|
|
|
@ -82,7 +82,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
Settings::Manager::setString("shader mode", "General", openGL ? (glES ? "glsles" : "glsl") : "hlsl");
|
||||
}
|
||||
|
||||
mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 50);
|
||||
mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5);
|
||||
|
||||
mRendering.getWindow()->addListener(this);
|
||||
mRendering.setWindowListener(this);
|
||||
|
@ -248,8 +248,10 @@ void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
|
|||
{
|
||||
if (!mTerrain)
|
||||
{
|
||||
mTerrain = new Terrain::Terrain(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain);
|
||||
mTerrain->update(mRendering.getCamera());
|
||||
mTerrain = new Terrain::Terrain(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
Settings::Manager::getBool("distant land", "Terrain"),
|
||||
Settings::Manager::getBool("shader", "Terrain"));
|
||||
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||
}
|
||||
}
|
||||
waterAdded(store);
|
||||
|
@ -554,13 +556,6 @@ void RenderingManager::setAmbientMode()
|
|||
}
|
||||
}
|
||||
|
||||
float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos)
|
||||
{
|
||||
assert(mTerrain);
|
||||
return mTerrain->getHeightAt(worldPos);
|
||||
}
|
||||
|
||||
|
||||
void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell)
|
||||
{
|
||||
mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient);
|
||||
|
@ -658,9 +653,14 @@ void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell)
|
|||
{
|
||||
if (cell->mCell->isExterior())
|
||||
{
|
||||
assert(mTerrain);
|
||||
|
||||
Ogre::AxisAlignedBox dims = mObjects.getDimensions(cell);
|
||||
Ogre::Vector2 center(cell->mCell->getGridX() + 0.5, -cell->mCell->getGridY() + 1 - 0.5);
|
||||
Ogre::Vector2 center(cell->mCell->getGridX() + 0.5, cell->mCell->getGridY() + 0.5);
|
||||
dims.merge(mTerrain->getWorldBoundingBox(center));
|
||||
|
||||
mTerrain->update(dims.getCenter());
|
||||
|
||||
mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z);
|
||||
}
|
||||
else
|
||||
|
@ -992,12 +992,13 @@ void RenderingManager::updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, con
|
|||
mWater->updateEmitterPtr(old, ptr);
|
||||
}
|
||||
|
||||
void RenderingManager::frameStarted(float dt)
|
||||
void RenderingManager::frameStarted(float dt, bool paused)
|
||||
{
|
||||
if (mTerrain)
|
||||
mTerrain->update(mRendering.getCamera());
|
||||
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||
|
||||
mWater->frameStarted(dt);
|
||||
if (!paused)
|
||||
mWater->frameStarted(dt);
|
||||
}
|
||||
|
||||
void RenderingManager::resetCamera()
|
||||
|
@ -1005,4 +1006,10 @@ void RenderingManager::resetCamera()
|
|||
mCamera->reset();
|
||||
}
|
||||
|
||||
float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos)
|
||||
{
|
||||
assert(mTerrain);
|
||||
return mTerrain->getHeightAt(worldPos);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -157,6 +157,8 @@ public:
|
|||
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }
|
||||
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }
|
||||
|
||||
float getTerrainHeightAt (Ogre::Vector3 worldPos);
|
||||
|
||||
Shadows* getShadows();
|
||||
|
||||
void switchToInterior();
|
||||
|
@ -164,8 +166,6 @@ public:
|
|||
|
||||
void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches);
|
||||
|
||||
float getTerrainHeightAt (Ogre::Vector3 worldPos);
|
||||
|
||||
void setGlare(bool glare);
|
||||
void skyEnable ();
|
||||
void skyDisable ();
|
||||
|
@ -209,7 +209,7 @@ public:
|
|||
|
||||
void playVideo(const std::string& name, bool allowSkipping);
|
||||
void stopVideo();
|
||||
void frameStarted(float dt);
|
||||
void frameStarted(float dt, bool paused);
|
||||
|
||||
protected:
|
||||
virtual void windowResized(int x, int y);
|
||||
|
|
|
@ -107,7 +107,8 @@ void Shadows::recreate()
|
|||
// Set visibility mask for the shadow render textures
|
||||
int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows")
|
||||
+ (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows")
|
||||
+ RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows");
|
||||
+ RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows")
|
||||
+ RV_Terrain * (Settings::Manager::getBool("terrain shadows", "Shadows"));
|
||||
for (int i = 0; i < (split ? 3 : 1); ++i)
|
||||
{
|
||||
TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "scene.hpp"
|
||||
|
||||
#include <OgreSceneNode.h>
|
||||
|
||||
#include <components/nif/niffile.hpp>
|
||||
|
||||
#include <libs/openengine/ogre/fader.hpp>
|
||||
|
@ -85,7 +87,6 @@ namespace MWWorld
|
|||
std::cout << "Unloading cell\n";
|
||||
ListAndResetHandles functor;
|
||||
|
||||
/*
|
||||
(*iter)->forEach<ListAndResetHandles>(functor);
|
||||
{
|
||||
// silence annoying g++ warning
|
||||
|
@ -96,7 +97,6 @@ namespace MWWorld
|
|||
mPhysics->removeObject (node->getName());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if ((*iter)->mCell->isExterior())
|
||||
{
|
||||
|
@ -150,7 +150,7 @@ namespace MWWorld
|
|||
|
||||
// ... then references. This is important for adjustPosition to work correctly.
|
||||
/// \todo rescale depending on the state of a new GMST
|
||||
//insertCell (*cell, true);
|
||||
insertCell (*cell, true);
|
||||
|
||||
mRendering.cellAdded (cell);
|
||||
|
||||
|
|
|
@ -835,7 +835,7 @@ namespace MWWorld
|
|||
bool isPlayer = ptr == mPlayer->getPlayer();
|
||||
bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell);
|
||||
|
||||
if (false ) //*currCell != newCell)
|
||||
if (*currCell != newCell)
|
||||
{
|
||||
removeContainerScripts(ptr);
|
||||
|
||||
|
@ -1026,10 +1026,13 @@ namespace MWWorld
|
|||
return;
|
||||
}
|
||||
|
||||
float terrainHeight = -std::numeric_limits<float>().max();// mRendering->getTerrainHeightAt(pos);
|
||||
if (ptr.getCell()->isExterior())
|
||||
{
|
||||
float terrainHeight = mRendering->getTerrainHeightAt(pos);
|
||||
|
||||
if (pos.z < terrainHeight)
|
||||
pos.z = terrainHeight;
|
||||
if (pos.z < terrainHeight)
|
||||
pos.z = terrainHeight;
|
||||
}
|
||||
|
||||
ptr.getRefData().getPosition().pos[2] = pos.z + 20; // place slightly above. will snap down to ground with code below
|
||||
|
||||
|
@ -1074,15 +1077,8 @@ namespace MWWorld
|
|||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
cellX = static_cast<int> (x/cellSize);
|
||||
|
||||
if (x<0)
|
||||
--cellX;
|
||||
|
||||
cellY = static_cast<int> (y/cellSize);
|
||||
|
||||
if (y<0)
|
||||
--cellY;
|
||||
cellX = std::floor(x/cellSize);
|
||||
cellY = std::floor(y/cellSize);
|
||||
}
|
||||
|
||||
void World::doPhysics(const PtrMovementList &actors, float duration)
|
||||
|
@ -1682,9 +1678,9 @@ namespace MWWorld
|
|||
mRendering->stopVideo();
|
||||
}
|
||||
|
||||
void World::frameStarted (float dt)
|
||||
void World::frameStarted (float dt, bool paused)
|
||||
{
|
||||
mRendering->frameStarted(dt);
|
||||
mRendering->frameStarted(dt, paused);
|
||||
}
|
||||
|
||||
void World::activateDoor(const MWWorld::Ptr& door)
|
||||
|
|
|
@ -415,7 +415,7 @@ namespace MWWorld
|
|||
/// \todo this does not belong here
|
||||
virtual void playVideo(const std::string& name, bool allowSkipping);
|
||||
virtual void stopVideo();
|
||||
virtual void frameStarted (float dt);
|
||||
virtual void frameStarted (float dt, bool paused);
|
||||
|
||||
/// Find center of exterior cell above land surface
|
||||
/// \return false if exterior with given name not exists, true otherwise
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreHardwareBufferManager.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreTechnique.h>
|
||||
#include <OgrePass.h>
|
||||
|
||||
#include "quadtreenode.hpp"
|
||||
#include "terrain.hpp"
|
||||
|
@ -53,7 +50,6 @@ 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(),
|
||||
mVertexBuffer, mNormalBuffer, mColourBuffer);
|
||||
|
||||
|
@ -66,6 +62,8 @@ namespace Terrain
|
|||
mIndexData->indexStart = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Chunk::updateIndexBuffer()
|
||||
{
|
||||
// Fetch a suitable index buffer (which may be shared)
|
||||
|
@ -75,7 +73,7 @@ namespace Terrain
|
|||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
QuadTreeNode* neighbour = mNode->searchNeighbour((Direction)i);
|
||||
QuadTreeNode* neighbour = mNode->getNeighbour((Direction)i);
|
||||
|
||||
// If the neighbour isn't currently rendering itself,
|
||||
// go up until we find one. NOTE: We don't need to go down,
|
||||
|
|
|
@ -106,6 +106,8 @@ namespace Terrain
|
|||
Ogre::Pass* pass = technique->createPass();
|
||||
pass->setLightingEnabled(false);
|
||||
pass->setVertexColourTracking(Ogre::TVC_NONE);
|
||||
// TODO: How to handle fog?
|
||||
pass->setFog(true, Ogre::FOG_NONE);
|
||||
|
||||
bool first = (layer == mLayerList.begin());
|
||||
|
||||
|
@ -127,9 +129,7 @@ namespace Terrain
|
|||
tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
|
||||
|
||||
float scale = (16/(16.f+1.f));
|
||||
float scroll = 1/16.f*0.5;
|
||||
tus->setTextureScale(scale,scale);
|
||||
tus->setTextureScroll(-scroll,-scroll);
|
||||
tus->setTextureScale(1.f/scale,1.f/scale);
|
||||
}
|
||||
|
||||
// Add the actual layer texture on top of the alpha map.
|
||||
|
@ -150,6 +150,7 @@ namespace Terrain
|
|||
Ogre::Pass* lightingPass = technique->createPass();
|
||||
lightingPass->setSceneBlending(Ogre::SBT_MODULATE);
|
||||
lightingPass->setVertexColourTracking(Ogre::TVC_AMBIENT|Ogre::TVC_DIFFUSE);
|
||||
lightingPass->setFog(true, Ogre::FOG_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Terrain
|
|||
void setLayerList (const std::vector<std::string>& layerList) { mLayerList = layerList; }
|
||||
bool hasLayers() { return mLayerList.size(); }
|
||||
void setBlendmapList (const std::vector<Ogre::TexturePtr>& blendmapList) { mBlendmapList = blendmapList; }
|
||||
const std::vector<Ogre::TexturePtr>& getBlendmapList() { return mBlendmapList; }
|
||||
void setCompositeMap (const std::string& name) { mCompositeMap = name; }
|
||||
|
||||
/// Creates a material suitable for displaying a chunk of terrain using alpha-blending.
|
||||
|
|
|
@ -142,17 +142,21 @@ QuadTreeNode::QuadTreeNode(Terrain* terrain, ChildDirection dir, float size, con
|
|||
, mTerrain(terrain)
|
||||
, mChunk(NULL)
|
||||
, mMaterialGenerator(NULL)
|
||||
, mBounds(Ogre::AxisAlignedBox::BOX_NULL)
|
||||
, mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL)
|
||||
{
|
||||
mBounds.setNull();
|
||||
for (int i=0; i<4; ++i)
|
||||
mChildren[i] = NULL;
|
||||
for (int i=0; i<4; ++i)
|
||||
mNeighbours[i] = NULL;
|
||||
|
||||
mSceneNode = mTerrain->getSceneManager()->getRootSceneNode()->createChildSceneNode(
|
||||
Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0));
|
||||
|
||||
mLodLevel = log2(mSize);
|
||||
|
||||
mMaterialGenerator = new MaterialGenerator(true);
|
||||
mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled());
|
||||
}
|
||||
|
||||
void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er)
|
||||
|
@ -168,38 +172,40 @@ QuadTreeNode::~QuadTreeNode()
|
|||
delete mMaterialGenerator;
|
||||
}
|
||||
|
||||
QuadTreeNode* QuadTreeNode::searchNeighbour(Direction dir)
|
||||
QuadTreeNode* QuadTreeNode::getNeighbour(Direction dir)
|
||||
{
|
||||
return searchNeighbourRecursive(this, dir);
|
||||
return mNeighbours[static_cast<int>(dir)];
|
||||
}
|
||||
|
||||
void QuadTreeNode::initNeighbours()
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mNeighbours[i] = searchNeighbourRecursive(this, (Direction)i);
|
||||
}
|
||||
|
||||
void QuadTreeNode::initAabb()
|
||||
{
|
||||
if (hasChildren())
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
mChildren[i]->initAabb();
|
||||
mBounds.merge(mChildren[i]->getBoundingBox());
|
||||
}
|
||||
mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*8192, -mSize/2*8192, mBounds.getMinimum().z),
|
||||
Ogre::Vector3(mSize/2*8192, mSize/2*8192, mBounds.getMaximum().z));
|
||||
}
|
||||
mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0),
|
||||
mBounds.getMaximum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0));
|
||||
}
|
||||
|
||||
void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box)
|
||||
{
|
||||
mBounds = box;
|
||||
}
|
||||
|
||||
const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox()
|
||||
{
|
||||
if (mIsDummy)
|
||||
return Ogre::AxisAlignedBox::BOX_NULL;
|
||||
if (mBounds.isNull())
|
||||
{
|
||||
if (hasChildren())
|
||||
{
|
||||
// X and Y are obvious, just need Z
|
||||
float min = std::numeric_limits<float>().max();
|
||||
float max = -std::numeric_limits<float>().max();
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
QuadTreeNode* child = getChild((ChildDirection)i);
|
||||
float v = child->getBoundingBox().getMaximum().z;
|
||||
if (v > max)
|
||||
max = v;
|
||||
v = child->getBoundingBox().getMinimum().z;
|
||||
if (v < min)
|
||||
min = v;
|
||||
}
|
||||
mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*8192, -mSize/2*8192, min),
|
||||
Ogre::Vector3(mSize/2*8192, mSize/2*8192, max));
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Leaf node should have bounds set!");
|
||||
}
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
|
@ -209,11 +215,19 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
|||
if (bounds.isNull())
|
||||
return;
|
||||
|
||||
Ogre::AxisAlignedBox worldBounds (bounds.getMinimum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0),
|
||||
bounds.getMaximum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0));
|
||||
float dist = distance(mWorldBounds, cameraPos);
|
||||
|
||||
if (!mTerrain->getDistantLandEnabled())
|
||||
{
|
||||
if (dist > 8192*2)
|
||||
{
|
||||
destroyChunks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float dist = distance(worldBounds, cameraPos);
|
||||
/// \todo implement error metrics or some other means of not using arbitrary values
|
||||
/// (general quality needs to be user configurable as well)
|
||||
size_t wantedLod = 0;
|
||||
if (dist > 8192*1)
|
||||
wantedLod = 1;
|
||||
|
@ -230,6 +244,7 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
|||
|
||||
if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod)
|
||||
{
|
||||
bool hadChunk = hasChunk();
|
||||
// Wanted LOD is small enough to render this node in one chunk
|
||||
if (!mChunk)
|
||||
{
|
||||
|
@ -250,14 +265,32 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
|||
}
|
||||
}
|
||||
|
||||
// Additional (index buffer) LOD is currently disabled.
|
||||
// This is due to a problem with the LOD selection when a node splits.
|
||||
// After splitting, the distance is measured from the children's bounding boxes, which are possibly
|
||||
// further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD
|
||||
// than the original node.
|
||||
// In short, we'd sometimes get a switch to a lesser detail when actually moving closer.
|
||||
// This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour
|
||||
// node hasn't split yet, and has a higher LOD than our node's child:
|
||||
// ----- ----- ------------
|
||||
// | LOD | LOD | |
|
||||
// | 1 | 1 | |
|
||||
// |-----|-----| LOD 0 |
|
||||
// | LOD | LOD | |
|
||||
// | 0 | 0 | |
|
||||
// ----- ----- ------------
|
||||
// To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're
|
||||
// doing here.
|
||||
// But this "solution" does increase triangle overhead, so eventually we need to find a more clever way.
|
||||
//mChunk->setAdditionalLod(wantedLod - mLodLevel);
|
||||
|
||||
mChunk->setAdditionalLod(wantedLod - mLodLevel);
|
||||
mChunk->setVisible(true);
|
||||
|
||||
if (hasChildren())
|
||||
if (!hadChunk && hasChildren())
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mChildren[i]->removeChunks();
|
||||
mChildren[i]->hideChunks();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -272,15 +305,41 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
|||
}
|
||||
}
|
||||
|
||||
void QuadTreeNode::removeChunks()
|
||||
void QuadTreeNode::hideChunks()
|
||||
{
|
||||
if (mChunk)
|
||||
mChunk->setVisible(false);
|
||||
if (hasChildren())
|
||||
{
|
||||
else if (hasChildren())
|
||||
for (int i=0; i<4; ++i)
|
||||
mChildren[i]->removeChunks();
|
||||
mChildren[i]->hideChunks();
|
||||
}
|
||||
|
||||
void QuadTreeNode::destroyChunks()
|
||||
{
|
||||
if (mChunk)
|
||||
{
|
||||
Ogre::MaterialManager::getSingleton().remove(mChunk->getMaterial()->getName());
|
||||
mSceneNode->detachObject(mChunk);
|
||||
|
||||
delete mChunk;
|
||||
mChunk = NULL;
|
||||
// destroy blendmaps
|
||||
if (mMaterialGenerator)
|
||||
{
|
||||
const std::vector<Ogre::TexturePtr>& list = mMaterialGenerator->getBlendmapList();
|
||||
for (std::vector<Ogre::TexturePtr>::const_iterator it = list.begin(); it != list.end(); ++it)
|
||||
Ogre::TextureManager::getSingleton().remove((*it)->getName());
|
||||
mMaterialGenerator->setBlendmapList(std::vector<Ogre::TexturePtr>());
|
||||
mMaterialGenerator->setLayerList(std::vector<std::string>());
|
||||
mMaterialGenerator->setCompositeMap("");
|
||||
}
|
||||
|
||||
Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName());
|
||||
mCompositeMap.setNull();
|
||||
}
|
||||
else if (hasChildren())
|
||||
for (int i=0; i<4; ++i)
|
||||
mChildren[i]->destroyChunks();
|
||||
}
|
||||
|
||||
void QuadTreeNode::updateIndexBuffers()
|
||||
|
@ -312,7 +371,7 @@ void QuadTreeNode::ensureLayerInfo()
|
|||
|
||||
std::vector<Ogre::TexturePtr> blendmaps;
|
||||
std::vector<std::string> layerList;
|
||||
mTerrain->getStorage()->getBlendmaps(mSize, mCenter, true, blendmaps, layerList);
|
||||
mTerrain->getStorage()->getBlendmaps(mSize, mCenter, mTerrain->getShadersEnabled(), blendmaps, layerList);
|
||||
|
||||
mMaterialGenerator->setLayerList(layerList);
|
||||
mMaterialGenerator->setBlendmapList(blendmaps);
|
||||
|
@ -324,7 +383,9 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
|||
|
||||
if (mIsDummy)
|
||||
{
|
||||
MaterialGenerator matGen(true);
|
||||
// TODO - why is this completely black?
|
||||
// TODO - store this default material somewhere instead of creating one for each empty cell
|
||||
MaterialGenerator matGen(mTerrain->getShadersEnabled());
|
||||
std::vector<std::string> layer;
|
||||
layer.push_back("_land_default.dds");
|
||||
matGen.setLayerList(layer);
|
||||
|
@ -357,12 +418,6 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool QuadTreeNode::hasCompositeMap()
|
||||
{
|
||||
return !mCompositeMap.isNull();
|
||||
}
|
||||
|
||||
void QuadTreeNode::ensureCompositeMap()
|
||||
{
|
||||
if (!mCompositeMap.isNull())
|
||||
|
|
|
@ -49,6 +49,13 @@ namespace Terrain
|
|||
QuadTreeNode (Terrain* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
||||
~QuadTreeNode();
|
||||
|
||||
/// Initialize neighbours - do this after the quadtree is built
|
||||
void initNeighbours();
|
||||
/// Initialize bounding boxes of non-leafs by merging children bounding boxes.
|
||||
/// Do this after the quadtree is built - note that leaf bounding boxes
|
||||
/// need to be set first via setBoundingBox!
|
||||
void initAabb();
|
||||
|
||||
/// @note takes ownership of \a child
|
||||
void createChild (ChildDirection id, float size, const Ogre::Vector2& center);
|
||||
|
||||
|
@ -66,15 +73,15 @@ namespace Terrain
|
|||
bool hasChildren() { return mChildren[0] != 0; }
|
||||
QuadTreeNode* getChild(ChildDirection dir) { return mChildren[dir]; }
|
||||
|
||||
/// Search for a neighbour node in this direction
|
||||
QuadTreeNode* searchNeighbour (Direction dir);
|
||||
/// Get neighbour node in this direction
|
||||
QuadTreeNode* getNeighbour (Direction dir);
|
||||
|
||||
/// Returns our direction relative to the parent node, or Root if we are the root node.
|
||||
ChildDirection getDirection() { return mDirection; }
|
||||
|
||||
/// Set bounding box in local coordinates. Should be done at load time for leaf nodes.
|
||||
/// Other nodes can merge AABB of child nodes.
|
||||
void setBoundingBox (const Ogre::AxisAlignedBox& box) { mBounds = box; }
|
||||
void setBoundingBox (const Ogre::AxisAlignedBox& box);
|
||||
|
||||
/// Get bounding box in local coordinates
|
||||
const Ogre::AxisAlignedBox& getBoundingBox();
|
||||
|
@ -88,8 +95,11 @@ namespace Terrain
|
|||
/// Call after QuadTreeNode::update!
|
||||
void updateIndexBuffers();
|
||||
|
||||
/// Remove chunks rendered by this node and all its children
|
||||
void removeChunks();
|
||||
/// Hide chunks rendered by this node and all its children
|
||||
void hideChunks();
|
||||
|
||||
/// Destroy chunks rendered by this node and all its children
|
||||
void destroyChunks();
|
||||
|
||||
/// Get the effective LOD level if this node was rendered in one chunk
|
||||
/// with ESM::Land::LAND_SIZE^2 vertices
|
||||
|
@ -101,8 +111,6 @@ namespace Terrain
|
|||
/// Is this node currently configured to render itself?
|
||||
bool hasChunk();
|
||||
|
||||
bool hasCompositeMap();
|
||||
|
||||
/// Add a textured quad to a specific 2d area in the composite map scenemanager.
|
||||
/// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply
|
||||
/// call this method on their children.
|
||||
|
@ -118,6 +126,7 @@ namespace Terrain
|
|||
float mSize;
|
||||
size_t mLodLevel; // LOD if we were to render this node in one chunk
|
||||
Ogre::AxisAlignedBox mBounds;
|
||||
Ogre::AxisAlignedBox mWorldBounds;
|
||||
ChildDirection mDirection;
|
||||
Ogre::Vector2 mCenter;
|
||||
|
||||
|
@ -125,6 +134,7 @@ namespace Terrain
|
|||
|
||||
QuadTreeNode* mParent;
|
||||
QuadTreeNode* mChildren[4];
|
||||
QuadTreeNode* mNeighbours[4];
|
||||
|
||||
Chunk* mChunk;
|
||||
|
||||
|
|
|
@ -53,6 +53,51 @@ namespace Terrain
|
|||
}
|
||||
|
||||
void Storage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row)
|
||||
{
|
||||
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;
|
||||
}
|
||||
ESM::Land* land = getLand(cellX, cellY);
|
||||
if (land && land->mHasData)
|
||||
{
|
||||
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
||||
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
||||
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
|
||||
normal.normalise();
|
||||
}
|
||||
else
|
||||
normal = Ogre::Vector3(0,0,1);
|
||||
}
|
||||
|
||||
void Storage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row)
|
||||
{
|
||||
Ogre::Vector3 n1,n2,n3,n4;
|
||||
fixNormal(n1, cellX, cellY, col+1, row);
|
||||
fixNormal(n2, cellX, cellY, col-1, row);
|
||||
fixNormal(n3, cellX, cellY, col, row+1);
|
||||
fixNormal(n4, cellX, cellY, col, row-1);
|
||||
normal = (n1+n2+n3+n4);
|
||||
normal.normalise();
|
||||
}
|
||||
|
||||
void Storage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row)
|
||||
{
|
||||
if (col == ESM::Land::LAND_SIZE-1)
|
||||
{
|
||||
|
@ -65,12 +110,19 @@ namespace Terrain
|
|||
row = 0;
|
||||
}
|
||||
ESM::Land* land = getLand(cellX, cellY);
|
||||
if (land && land->mHasData)
|
||||
if (land && land->mLandData->mUsingColours)
|
||||
{
|
||||
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
||||
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
||||
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
|
||||
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
||||
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
||||
color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
color.r = 1;
|
||||
color.g = 1;
|
||||
color.b = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Storage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center,
|
||||
|
@ -141,17 +193,21 @@ namespace Terrain
|
|||
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
||||
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
||||
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
|
||||
// Normals don't connect seamlessly between cells - wtf?
|
||||
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
|
||||
fixNormal(normal, cellX, cellY, col, row);
|
||||
// z < 0 should never happen, but it does - I hate this data set...
|
||||
if (normal.z < 0)
|
||||
normal *= -1;
|
||||
normal.normalise();
|
||||
}
|
||||
else
|
||||
normal = Ogre::Vector3(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);
|
||||
|
||||
// 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);
|
||||
|
||||
assert(normal.z > 0);
|
||||
|
||||
normals[vertX*numVerts*3 + vertY*3] = normal.x;
|
||||
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
|
||||
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
|
||||
|
@ -168,6 +224,11 @@ namespace Terrain
|
|||
color.g = 1;
|
||||
color.b = 1;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
color.a = 1;
|
||||
Ogre::uint32 rsColor;
|
||||
Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
|
||||
|
@ -193,18 +254,20 @@ namespace Terrain
|
|||
Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY,
|
||||
int x, int y)
|
||||
{
|
||||
// If we're at the last row (or last column), we need to get the texture from the neighbour cell
|
||||
// to get consistent blending at the border
|
||||
if (x >= ESM::Land::LAND_TEXTURE_SIZE)
|
||||
// For the first/last row/column, we need to get the texture from the neighbour cell
|
||||
// to get consistent blending at the borders
|
||||
--x;
|
||||
if (x < 0)
|
||||
{
|
||||
cellX++;
|
||||
x -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||
--cellX;
|
||||
x += ESM::Land::LAND_TEXTURE_SIZE;
|
||||
}
|
||||
if (y >= ESM::Land::LAND_TEXTURE_SIZE)
|
||||
if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
|
||||
{
|
||||
cellY++;
|
||||
++cellY;
|
||||
y -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||
}
|
||||
|
||||
assert(x<ESM::Land::LAND_TEXTURE_SIZE);
|
||||
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
|
||||
|
||||
|
@ -226,7 +289,7 @@ namespace Terrain
|
|||
std::string Storage::getTextureName(UniqueTextureId id)
|
||||
{
|
||||
if (id.first == 0)
|
||||
return "_land_default.dds"; // Not sure if the default texture really is hardcoded?
|
||||
return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded?
|
||||
|
||||
// NB: All vtex ids are +1 compared to the ltex ids
|
||||
const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second);
|
||||
|
@ -241,6 +304,10 @@ namespace Terrain
|
|||
void Storage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
|
||||
bool pack, std::vector<Ogre::TexturePtr> &blendmaps, std::vector<std::string> &layerList)
|
||||
{
|
||||
// TODO - blending isn't completely right yet; the blending radius appears to be
|
||||
// different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap
|
||||
// and interpolate the rest of the cell by hand? :/
|
||||
|
||||
Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f);
|
||||
int cellX = origin.x;
|
||||
int cellY = origin.y;
|
||||
|
@ -253,7 +320,7 @@ namespace Terrain
|
|||
// To get a consistent look, we need to make sure to use the same base layer in all cells.
|
||||
// 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));
|
||||
// NB +1 to get the last index from neighbour cell (see getVtexIndexAt)
|
||||
|
||||
for (int y=0; y<ESM::Land::LAND_TEXTURE_SIZE+1; ++y)
|
||||
for (int x=0; x<ESM::Land::LAND_TEXTURE_SIZE+1; ++x)
|
||||
{
|
||||
|
@ -314,5 +381,90 @@ namespace Terrain
|
|||
}
|
||||
}
|
||||
|
||||
float Storage::getHeightAt(const Ogre::Vector3 &worldPos)
|
||||
{
|
||||
int cellX = std::floor(worldPos.x / 8192.f);
|
||||
int cellY = std::floor(worldPos.y / 8192.f);
|
||||
|
||||
ESM::Land* land = getLand(cellX, cellY);
|
||||
if (!land)
|
||||
return -2048;
|
||||
|
||||
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
|
||||
|
||||
// Normalized position in the cell
|
||||
float nX = (worldPos.x - (cellX * 8192))/8192.f;
|
||||
float nY = (worldPos.y - (cellY * 8192))/8192.f;
|
||||
|
||||
// get left / bottom points (rounded down)
|
||||
float factor = ESM::Land::LAND_SIZE - 1.0f;
|
||||
float invFactor = 1.0f / factor;
|
||||
|
||||
int startX = static_cast<int>(nX * factor);
|
||||
int startY = static_cast<int>(nY * factor);
|
||||
int endX = startX + 1;
|
||||
int endY = startY + 1;
|
||||
|
||||
assert(endX < ESM::Land::LAND_SIZE);
|
||||
assert(endY < ESM::Land::LAND_SIZE);
|
||||
|
||||
// now get points in terrain space (effectively rounding them to boundaries)
|
||||
float startXTS = startX * invFactor;
|
||||
float startYTS = startY * invFactor;
|
||||
float endXTS = endX * invFactor;
|
||||
float endYTS = endY * invFactor;
|
||||
|
||||
// get parametric from start coord to next point
|
||||
float xParam = (nX - startXTS) * factor;
|
||||
float yParam = (nY - startYTS) * factor;
|
||||
|
||||
/* For even / odd tri strip rows, triangles are this shape:
|
||||
even odd
|
||||
3---2 3---2
|
||||
| / | | \ |
|
||||
0---1 0---1
|
||||
*/
|
||||
|
||||
// Build all 4 positions in terrain space, using point-sampled height
|
||||
Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f);
|
||||
Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f);
|
||||
Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f);
|
||||
Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f);
|
||||
// define this plane in terrain space
|
||||
Ogre::Plane plane;
|
||||
// (At the moment, all rows have the same triangle alignment)
|
||||
if (true)
|
||||
{
|
||||
// odd row
|
||||
bool secondTri = ((1.0 - yParam) > xParam);
|
||||
if (secondTri)
|
||||
plane.redefine(v0, v1, v3);
|
||||
else
|
||||
plane.redefine(v1, v2, v3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// even row
|
||||
bool secondTri = (yParam > xParam);
|
||||
if (secondTri)
|
||||
plane.redefine(v0, v2, v3);
|
||||
else
|
||||
plane.redefine(v0, v1, v2);
|
||||
}
|
||||
|
||||
// Solve plane equation for z
|
||||
return (-plane.normal.x * nX
|
||||
-plane.normal.y * nY
|
||||
- plane.d) / plane.normal.z * 8192;
|
||||
|
||||
}
|
||||
|
||||
float Storage::getVertexHeight(const ESM::Land *land, int x, int y)
|
||||
{
|
||||
assert(x < ESM::Land::LAND_SIZE);
|
||||
assert(y < ESM::Land::LAND_SIZE);
|
||||
return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -58,8 +58,14 @@ namespace Terrain
|
|||
std::vector<Ogre::TexturePtr>& blendmaps,
|
||||
std::vector<std::string>& layerList);
|
||||
|
||||
float getHeightAt (const Ogre::Vector3& worldPos);
|
||||
|
||||
private:
|
||||
void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
|
||||
void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row);
|
||||
void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
|
||||
|
||||
float getVertexHeight (const ESM::Land* land, int x, int y);
|
||||
|
||||
// Since plugins can define new texture palettes, we need to know the plugin index too
|
||||
// in order to retrieve the correct texture name.
|
||||
|
|
|
@ -51,12 +51,14 @@ namespace
|
|||
namespace Terrain
|
||||
{
|
||||
|
||||
Terrain::Terrain(Ogre::SceneManager* sceneMgr, Storage* storage, int visibilityFlags)
|
||||
Terrain::Terrain(Ogre::SceneManager* sceneMgr, Storage* storage, int visibilityFlags, bool distantLand, bool shaders)
|
||||
: mStorage(storage)
|
||||
, mMinBatchSize(1)
|
||||
, mMaxBatchSize(64)
|
||||
, mSceneMgr(sceneMgr)
|
||||
, mVisibilityFlags(visibilityFlags)
|
||||
, mDistantLand(distantLand)
|
||||
, mShaders(shaders)
|
||||
{
|
||||
mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
||||
|
||||
|
@ -81,6 +83,8 @@ namespace Terrain
|
|||
|
||||
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL);
|
||||
buildQuadTree(mRootNode);
|
||||
mRootNode->initAabb();
|
||||
mRootNode->initNeighbours();
|
||||
}
|
||||
|
||||
Terrain::~Terrain()
|
||||
|
@ -136,14 +140,19 @@ namespace Terrain
|
|||
node->markAsDummy();
|
||||
}
|
||||
|
||||
void Terrain::update(Ogre::Camera *camera)
|
||||
void Terrain::update(const Ogre::Vector3& cameraPos)
|
||||
{
|
||||
mRootNode->update(camera->getRealPosition());
|
||||
mRootNode->update(cameraPos);
|
||||
mRootNode->updateIndexBuffers();
|
||||
}
|
||||
|
||||
Ogre::AxisAlignedBox Terrain::getWorldBoundingBox (const Ogre::Vector2& center)
|
||||
{
|
||||
if (center.x > mBounds.getMaximum().x
|
||||
|| center.x < mBounds.getMinimum().x
|
||||
|| center.y > mBounds.getMaximum().y
|
||||
|| center.y < mBounds.getMinimum().y)
|
||||
return Ogre::AxisAlignedBox::BOX_NULL;
|
||||
QuadTreeNode* node = findNode(center, mRootNode);
|
||||
Ogre::AxisAlignedBox box = node->getBoundingBox();
|
||||
box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * 8192,
|
||||
|
@ -220,10 +229,10 @@ namespace Terrain
|
|||
for (size_t col = colStart; col < colEnd; col += increment)
|
||||
{
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+increment);
|
||||
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+increment);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment);
|
||||
}
|
||||
|
@ -242,17 +251,18 @@ namespace Terrain
|
|||
{
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row);
|
||||
// Make sure not to touch the left edge
|
||||
if (col == 0)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+innerStep);
|
||||
// Make sure not to touch the right edge
|
||||
if (col+outerStep == ESM::Land::LAND_SIZE-1)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row+innerStep);
|
||||
else
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+innerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row+innerStep);
|
||||
|
||||
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||
{
|
||||
// Make sure not to touch the left or right edges
|
||||
if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep)
|
||||
continue;
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col)+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row+innerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row+innerStep);
|
||||
}
|
||||
|
@ -265,11 +275,11 @@ namespace Terrain
|
|||
{
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
// Make sure not to touch the right edge
|
||||
if (col+outerStep == ESM::Land::LAND_SIZE-1)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row-innerStep);
|
||||
// Make sure not to touch the left edge
|
||||
if (col == 0)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row-innerStep);
|
||||
else
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row-innerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row-innerStep);
|
||||
|
||||
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||
{
|
||||
|
@ -278,7 +288,7 @@ namespace Terrain
|
|||
continue;
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row-innerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row-innerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,18 +299,18 @@ namespace Terrain
|
|||
{
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
// Make sure not to touch the bottom edge
|
||||
if (row == 0)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+innerStep);
|
||||
// Make sure not to touch the top edge
|
||||
if (row+outerStep == ESM::Land::LAND_SIZE-1)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep-innerStep);
|
||||
else
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep);
|
||||
|
||||
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||
{
|
||||
// Make sure not to touch the top or bottom edges
|
||||
if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep)
|
||||
continue;
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i+innerStep);
|
||||
}
|
||||
|
@ -313,18 +323,18 @@ namespace Terrain
|
|||
{
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep);
|
||||
// Make sure not to touch the top edge
|
||||
if (row+outerStep == ESM::Land::LAND_SIZE-1)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+outerStep-innerStep);
|
||||
// Make sure not to touch the bottom edge
|
||||
if (row == 0)
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+innerStep);
|
||||
else
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+outerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row);
|
||||
|
||||
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||
{
|
||||
// Make sure not to touch the top or bottom edges
|
||||
if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep)
|
||||
continue;
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i+innerStep);
|
||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i);
|
||||
}
|
||||
|
@ -355,5 +365,10 @@ namespace Terrain
|
|||
mCompositeMapSceneMgr->clearScene();
|
||||
}
|
||||
|
||||
float Terrain::getHeightAt(const Ogre::Vector3 &worldPos)
|
||||
{
|
||||
return mStorage->getHeightAt(worldPos);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -28,16 +28,25 @@ namespace Terrain
|
|||
{
|
||||
public:
|
||||
/// @note takes ownership of \a storage
|
||||
Terrain(Ogre::SceneManager* sceneMgr, Storage* storage, int visiblityFlags);
|
||||
/// @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
|
||||
/// @param distantLand Whether to draw all of the terrain, or only a 3x3 grid around the camera.
|
||||
/// 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.
|
||||
Terrain(Ogre::SceneManager* sceneMgr, Storage* storage, int visiblityFlags, bool distantLand, bool shaders);
|
||||
~Terrain();
|
||||
|
||||
bool getDistantLandEnabled() { return mDistantLand; }
|
||||
bool getShadersEnabled() { return mShaders; }
|
||||
|
||||
float getHeightAt (const Ogre::Vector3& worldPos);
|
||||
|
||||
/// Update chunk LODs according to this camera position
|
||||
/// @note Calling this method might lead to composite textures being rendered, so it is best
|
||||
/// not to call it when render commands are still queued, since that would cause a flush.
|
||||
void update (Ogre::Camera* camera);
|
||||
|
||||
/// \todo
|
||||
float getHeightAt (const Ogre::Vector3& worldPos) { return 0; }
|
||||
void update (const Ogre::Vector3& cameraPos);
|
||||
|
||||
/// Get the world bounding box of a chunk of terrain centered at \a center
|
||||
Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center);
|
||||
|
@ -63,6 +72,9 @@ namespace Terrain
|
|||
void enableSplattingShader(bool enabled);
|
||||
|
||||
private:
|
||||
bool mDistantLand;
|
||||
bool mShaders;
|
||||
|
||||
QuadTreeNode* mRootNode;
|
||||
Storage* mStorage;
|
||||
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
@shEndForeach
|
||||
lightResult.xyz += lightAmbient.xyz;
|
||||
lightResult.xyz *= colour.xyz;
|
||||
directionalResult.xyz *= colour.xyz;
|
||||
|
||||
@shPassthroughAssign(lightResult, lightResult);
|
||||
@shPassthroughAssign(directionalResult, directionalResult);
|
||||
|
|
|
@ -335,12 +335,12 @@
|
|||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<Widget type="HBox" position="4 172 350 24">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="ShadowsDebug"/>
|
||||
<Widget type="HBox" position="4 140 350 24">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="TerrainShadows"/>
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
|
||||
<Property key="Caption" value="Debug overlay"/>
|
||||
<Property key="Caption" value="Terrain shadows"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ texture size = 1024
|
|||
actor shadows = true
|
||||
misc shadows = true
|
||||
statics shadows = true
|
||||
terrain shadows = true
|
||||
|
||||
# Fraction of the total shadow distance after which the shadow starts to fade out
|
||||
fade start = 0.8
|
||||
|
@ -125,8 +126,9 @@ fog start factor = 0.5
|
|||
fog end factor = 1.0
|
||||
|
||||
[Terrain]
|
||||
# Max. number of lights that affect the terrain. Setting to 1 will only reflect sunlight
|
||||
num lights = 8
|
||||
distant land = false
|
||||
|
||||
shader = true
|
||||
|
||||
[Water]
|
||||
shader = true
|
||||
|
|
Loading…
Reference in a new issue