forked from mirror/openmw-tes3mp
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)
|
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
|
||||||
{
|
{
|
||||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||||
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame);
|
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused);
|
||||||
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
|
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -91,12 +91,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
||||||
MWBase::Environment::get().getSoundManager()->update(frametime);
|
MWBase::Environment::get().getSoundManager()->update(frametime);
|
||||||
|
|
||||||
// global scripts
|
// global scripts
|
||||||
//MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
|
||||||
|
|
||||||
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
|
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
|
||||||
|
|
||||||
// local scripts
|
// 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
|
// change, followed by a cell change in a local script during the same
|
||||||
// frame.
|
// frame.
|
||||||
|
|
||||||
|
|
|
@ -371,7 +371,7 @@ namespace MWBase
|
||||||
/// \todo this does not belong here
|
/// \todo this does not belong here
|
||||||
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
||||||
virtual void stopVideo() = 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
|
/// Find default position inside exterior cell specified by name
|
||||||
/// \return false if exterior with given name not exists, true otherwise
|
/// \return false if exterior with given name not exists, true otherwise
|
||||||
|
|
|
@ -125,7 +125,7 @@ namespace MWGui
|
||||||
getWidget(mActorShadows, "ActorShadows");
|
getWidget(mActorShadows, "ActorShadows");
|
||||||
getWidget(mStaticsShadows, "StaticsShadows");
|
getWidget(mStaticsShadows, "StaticsShadows");
|
||||||
getWidget(mMiscShadows, "MiscShadows");
|
getWidget(mMiscShadows, "MiscShadows");
|
||||||
getWidget(mShadowsDebug, "ShadowsDebug");
|
getWidget(mTerrainShadows, "TerrainShadows");
|
||||||
getWidget(mControlsBox, "ControlsBox");
|
getWidget(mControlsBox, "ControlsBox");
|
||||||
getWidget(mResetControlsButton, "ResetControlsButton");
|
getWidget(mResetControlsButton, "ResetControlsButton");
|
||||||
getWidget(mInvertYButton, "InvertYButton");
|
getWidget(mInvertYButton, "InvertYButton");
|
||||||
|
@ -161,7 +161,7 @@ namespace MWGui
|
||||||
mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||||
mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||||
mMiscShadows->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);
|
mMasterVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||||
mVoiceVolumeSlider->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}");
|
mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||||
mStaticsShadows->setCaptionWithReplacing(Settings::Manager::getBool("statics shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
mStaticsShadows->setCaptionWithReplacing(Settings::Manager::getBool("statics shadows", "Shadows") ? "#{sOn}" : "#{sOff}");
|
||||||
mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc 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);
|
float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2);
|
||||||
mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1));
|
mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1));
|
||||||
|
@ -394,8 +394,8 @@ namespace MWGui
|
||||||
Settings::Manager::setBool("statics shadows", "Shadows", newState);
|
Settings::Manager::setBool("statics shadows", "Shadows", newState);
|
||||||
else if (_sender == mMiscShadows)
|
else if (_sender == mMiscShadows)
|
||||||
Settings::Manager::setBool("misc shadows", "Shadows", newState);
|
Settings::Manager::setBool("misc shadows", "Shadows", newState);
|
||||||
else if (_sender == mShadowsDebug)
|
else if (_sender == mTerrainShadows)
|
||||||
Settings::Manager::setBool("debug", "Shadows", newState);
|
Settings::Manager::setBool("terrain shadows", "Shadows", newState);
|
||||||
else if (_sender == mInvertYButton)
|
else if (_sender == mInvertYButton)
|
||||||
Settings::Manager::setBool("invert y axis", "Input", newState);
|
Settings::Manager::setBool("invert y axis", "Input", newState);
|
||||||
else if (_sender == mCrosshairButton)
|
else if (_sender == mCrosshairButton)
|
||||||
|
|
|
@ -59,7 +59,7 @@ namespace MWGui
|
||||||
MyGUI::Button* mActorShadows;
|
MyGUI::Button* mActorShadows;
|
||||||
MyGUI::Button* mStaticsShadows;
|
MyGUI::Button* mStaticsShadows;
|
||||||
MyGUI::Button* mMiscShadows;
|
MyGUI::Button* mMiscShadows;
|
||||||
MyGUI::Button* mShadowsDebug;
|
MyGUI::Button* mTerrainShadows;
|
||||||
|
|
||||||
// audio
|
// audio
|
||||||
MyGUI::ScrollBar* mMasterVolumeSlider;
|
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");
|
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.getWindow()->addListener(this);
|
||||||
mRendering.setWindowListener(this);
|
mRendering.setWindowListener(this);
|
||||||
|
@ -248,8 +248,10 @@ void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
|
||||||
{
|
{
|
||||||
if (!mTerrain)
|
if (!mTerrain)
|
||||||
{
|
{
|
||||||
mTerrain = new Terrain::Terrain(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain);
|
mTerrain = new Terrain::Terrain(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||||
mTerrain->update(mRendering.getCamera());
|
Settings::Manager::getBool("distant land", "Terrain"),
|
||||||
|
Settings::Manager::getBool("shader", "Terrain"));
|
||||||
|
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waterAdded(store);
|
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)
|
void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell)
|
||||||
{
|
{
|
||||||
mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient);
|
mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient);
|
||||||
|
@ -658,9 +653,14 @@ void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell)
|
||||||
{
|
{
|
||||||
if (cell->mCell->isExterior())
|
if (cell->mCell->isExterior())
|
||||||
{
|
{
|
||||||
|
assert(mTerrain);
|
||||||
|
|
||||||
Ogre::AxisAlignedBox dims = mObjects.getDimensions(cell);
|
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));
|
dims.merge(mTerrain->getWorldBoundingBox(center));
|
||||||
|
|
||||||
|
mTerrain->update(dims.getCenter());
|
||||||
|
|
||||||
mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z);
|
mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -992,12 +992,13 @@ void RenderingManager::updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, con
|
||||||
mWater->updateEmitterPtr(old, ptr);
|
mWater->updateEmitterPtr(old, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::frameStarted(float dt)
|
void RenderingManager::frameStarted(float dt, bool paused)
|
||||||
{
|
{
|
||||||
if (mTerrain)
|
if (mTerrain)
|
||||||
mTerrain->update(mRendering.getCamera());
|
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||||
|
|
||||||
mWater->frameStarted(dt);
|
if (!paused)
|
||||||
|
mWater->frameStarted(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::resetCamera()
|
void RenderingManager::resetCamera()
|
||||||
|
@ -1005,4 +1006,10 @@ void RenderingManager::resetCamera()
|
||||||
mCamera->reset();
|
mCamera->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos)
|
||||||
|
{
|
||||||
|
assert(mTerrain);
|
||||||
|
return mTerrain->getHeightAt(worldPos);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -157,6 +157,8 @@ public:
|
||||||
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }
|
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }
|
||||||
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }
|
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }
|
||||||
|
|
||||||
|
float getTerrainHeightAt (Ogre::Vector3 worldPos);
|
||||||
|
|
||||||
Shadows* getShadows();
|
Shadows* getShadows();
|
||||||
|
|
||||||
void switchToInterior();
|
void switchToInterior();
|
||||||
|
@ -164,8 +166,6 @@ public:
|
||||||
|
|
||||||
void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches);
|
void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches);
|
||||||
|
|
||||||
float getTerrainHeightAt (Ogre::Vector3 worldPos);
|
|
||||||
|
|
||||||
void setGlare(bool glare);
|
void setGlare(bool glare);
|
||||||
void skyEnable ();
|
void skyEnable ();
|
||||||
void skyDisable ();
|
void skyDisable ();
|
||||||
|
@ -209,7 +209,7 @@ public:
|
||||||
|
|
||||||
void playVideo(const std::string& name, bool allowSkipping);
|
void playVideo(const std::string& name, bool allowSkipping);
|
||||||
void stopVideo();
|
void stopVideo();
|
||||||
void frameStarted(float dt);
|
void frameStarted(float dt, bool paused);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void windowResized(int x, int y);
|
virtual void windowResized(int x, int y);
|
||||||
|
|
|
@ -107,7 +107,8 @@ void Shadows::recreate()
|
||||||
// Set visibility mask for the shadow render textures
|
// Set visibility mask for the shadow render textures
|
||||||
int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows")
|
int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows")
|
||||||
+ (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics 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)
|
for (int i = 0; i < (split ? 3 : 1); ++i)
|
||||||
{
|
{
|
||||||
TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i);
|
TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "scene.hpp"
|
#include "scene.hpp"
|
||||||
|
|
||||||
|
#include <OgreSceneNode.h>
|
||||||
|
|
||||||
#include <components/nif/niffile.hpp>
|
#include <components/nif/niffile.hpp>
|
||||||
|
|
||||||
#include <libs/openengine/ogre/fader.hpp>
|
#include <libs/openengine/ogre/fader.hpp>
|
||||||
|
@ -85,7 +87,6 @@ namespace MWWorld
|
||||||
std::cout << "Unloading cell\n";
|
std::cout << "Unloading cell\n";
|
||||||
ListAndResetHandles functor;
|
ListAndResetHandles functor;
|
||||||
|
|
||||||
/*
|
|
||||||
(*iter)->forEach<ListAndResetHandles>(functor);
|
(*iter)->forEach<ListAndResetHandles>(functor);
|
||||||
{
|
{
|
||||||
// silence annoying g++ warning
|
// silence annoying g++ warning
|
||||||
|
@ -96,7 +97,6 @@ namespace MWWorld
|
||||||
mPhysics->removeObject (node->getName());
|
mPhysics->removeObject (node->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if ((*iter)->mCell->isExterior())
|
if ((*iter)->mCell->isExterior())
|
||||||
{
|
{
|
||||||
|
@ -150,7 +150,7 @@ namespace MWWorld
|
||||||
|
|
||||||
// ... then references. This is important for adjustPosition to work correctly.
|
// ... then references. This is important for adjustPosition to work correctly.
|
||||||
/// \todo rescale depending on the state of a new GMST
|
/// \todo rescale depending on the state of a new GMST
|
||||||
//insertCell (*cell, true);
|
insertCell (*cell, true);
|
||||||
|
|
||||||
mRendering.cellAdded (cell);
|
mRendering.cellAdded (cell);
|
||||||
|
|
||||||
|
|
|
@ -835,7 +835,7 @@ namespace MWWorld
|
||||||
bool isPlayer = ptr == mPlayer->getPlayer();
|
bool isPlayer = ptr == mPlayer->getPlayer();
|
||||||
bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell);
|
bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell);
|
||||||
|
|
||||||
if (false ) //*currCell != newCell)
|
if (*currCell != newCell)
|
||||||
{
|
{
|
||||||
removeContainerScripts(ptr);
|
removeContainerScripts(ptr);
|
||||||
|
|
||||||
|
@ -1026,10 +1026,13 @@ namespace MWWorld
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float terrainHeight = -std::numeric_limits<float>().max();// mRendering->getTerrainHeightAt(pos);
|
if (ptr.getCell()->isExterior())
|
||||||
|
{
|
||||||
|
float terrainHeight = mRendering->getTerrainHeightAt(pos);
|
||||||
|
|
||||||
if (pos.z < terrainHeight)
|
if (pos.z < terrainHeight)
|
||||||
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
|
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;
|
const int cellSize = 8192;
|
||||||
|
|
||||||
cellX = static_cast<int> (x/cellSize);
|
cellX = std::floor(x/cellSize);
|
||||||
|
cellY = std::floor(y/cellSize);
|
||||||
if (x<0)
|
|
||||||
--cellX;
|
|
||||||
|
|
||||||
cellY = static_cast<int> (y/cellSize);
|
|
||||||
|
|
||||||
if (y<0)
|
|
||||||
--cellY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::doPhysics(const PtrMovementList &actors, float duration)
|
void World::doPhysics(const PtrMovementList &actors, float duration)
|
||||||
|
@ -1682,9 +1678,9 @@ namespace MWWorld
|
||||||
mRendering->stopVideo();
|
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)
|
void World::activateDoor(const MWWorld::Ptr& door)
|
||||||
|
|
|
@ -415,7 +415,7 @@ namespace MWWorld
|
||||||
/// \todo this does not belong here
|
/// \todo this does not belong here
|
||||||
virtual void playVideo(const std::string& name, bool allowSkipping);
|
virtual void playVideo(const std::string& name, bool allowSkipping);
|
||||||
virtual void stopVideo();
|
virtual void stopVideo();
|
||||||
virtual void frameStarted (float dt);
|
virtual void frameStarted (float dt, bool paused);
|
||||||
|
|
||||||
/// Find center of exterior cell above land surface
|
/// Find center of exterior cell above land surface
|
||||||
/// \return false if exterior with given name not exists, true otherwise
|
/// \return false if exterior with given name not exists, true otherwise
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
#include <OgreHardwareBufferManager.h>
|
#include <OgreHardwareBufferManager.h>
|
||||||
#include <OgreMaterialManager.h>
|
|
||||||
#include <OgreTechnique.h>
|
|
||||||
#include <OgrePass.h>
|
|
||||||
|
|
||||||
#include "quadtreenode.hpp"
|
#include "quadtreenode.hpp"
|
||||||
#include "terrain.hpp"
|
#include "terrain.hpp"
|
||||||
|
@ -53,7 +50,6 @@ namespace Terrain
|
||||||
mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
|
mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
|
||||||
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
|
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
|
||||||
|
|
||||||
|
|
||||||
mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(),
|
mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(),
|
||||||
mVertexBuffer, mNormalBuffer, mColourBuffer);
|
mVertexBuffer, mNormalBuffer, mColourBuffer);
|
||||||
|
|
||||||
|
@ -66,6 +62,8 @@ namespace Terrain
|
||||||
mIndexData->indexStart = 0;
|
mIndexData->indexStart = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Chunk::updateIndexBuffer()
|
void Chunk::updateIndexBuffer()
|
||||||
{
|
{
|
||||||
// Fetch a suitable index buffer (which may be shared)
|
// Fetch a suitable index buffer (which may be shared)
|
||||||
|
@ -75,7 +73,7 @@ namespace Terrain
|
||||||
|
|
||||||
for (int i=0; i<4; ++i)
|
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,
|
// If the neighbour isn't currently rendering itself,
|
||||||
// go up until we find one. NOTE: We don't need to go down,
|
// 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();
|
Ogre::Pass* pass = technique->createPass();
|
||||||
pass->setLightingEnabled(false);
|
pass->setLightingEnabled(false);
|
||||||
pass->setVertexColourTracking(Ogre::TVC_NONE);
|
pass->setVertexColourTracking(Ogre::TVC_NONE);
|
||||||
|
// TODO: How to handle fog?
|
||||||
|
pass->setFog(true, Ogre::FOG_NONE);
|
||||||
|
|
||||||
bool first = (layer == mLayerList.begin());
|
bool first = (layer == mLayerList.begin());
|
||||||
|
|
||||||
|
@ -127,9 +129,7 @@ namespace Terrain
|
||||||
tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
|
tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
|
||||||
|
|
||||||
float scale = (16/(16.f+1.f));
|
float scale = (16/(16.f+1.f));
|
||||||
float scroll = 1/16.f*0.5;
|
tus->setTextureScale(1.f/scale,1.f/scale);
|
||||||
tus->setTextureScale(scale,scale);
|
|
||||||
tus->setTextureScroll(-scroll,-scroll);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the actual layer texture on top of the alpha map.
|
// Add the actual layer texture on top of the alpha map.
|
||||||
|
@ -150,6 +150,7 @@ namespace Terrain
|
||||||
Ogre::Pass* lightingPass = technique->createPass();
|
Ogre::Pass* lightingPass = technique->createPass();
|
||||||
lightingPass->setSceneBlending(Ogre::SBT_MODULATE);
|
lightingPass->setSceneBlending(Ogre::SBT_MODULATE);
|
||||||
lightingPass->setVertexColourTracking(Ogre::TVC_AMBIENT|Ogre::TVC_DIFFUSE);
|
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; }
|
void setLayerList (const std::vector<std::string>& layerList) { mLayerList = layerList; }
|
||||||
bool hasLayers() { return mLayerList.size(); }
|
bool hasLayers() { return mLayerList.size(); }
|
||||||
void setBlendmapList (const std::vector<Ogre::TexturePtr>& blendmapList) { mBlendmapList = blendmapList; }
|
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; }
|
void setCompositeMap (const std::string& name) { mCompositeMap = name; }
|
||||||
|
|
||||||
/// Creates a material suitable for displaying a chunk of terrain using alpha-blending.
|
/// 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)
|
, mTerrain(terrain)
|
||||||
, mChunk(NULL)
|
, mChunk(NULL)
|
||||||
, mMaterialGenerator(NULL)
|
, mMaterialGenerator(NULL)
|
||||||
|
, mBounds(Ogre::AxisAlignedBox::BOX_NULL)
|
||||||
|
, mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL)
|
||||||
{
|
{
|
||||||
mBounds.setNull();
|
mBounds.setNull();
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mChildren[i] = NULL;
|
mChildren[i] = NULL;
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
mNeighbours[i] = NULL;
|
||||||
|
|
||||||
mSceneNode = mTerrain->getSceneManager()->getRootSceneNode()->createChildSceneNode(
|
mSceneNode = mTerrain->getSceneManager()->getRootSceneNode()->createChildSceneNode(
|
||||||
Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0));
|
Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0));
|
||||||
|
|
||||||
mLodLevel = log2(mSize);
|
mLodLevel = log2(mSize);
|
||||||
|
|
||||||
mMaterialGenerator = new MaterialGenerator(true);
|
mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er)
|
void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er)
|
||||||
|
@ -168,38 +172,40 @@ QuadTreeNode::~QuadTreeNode()
|
||||||
delete mMaterialGenerator;
|
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()
|
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;
|
return mBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,11 +215,19 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
if (bounds.isNull())
|
if (bounds.isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Ogre::AxisAlignedBox worldBounds (bounds.getMinimum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0),
|
float dist = distance(mWorldBounds, cameraPos);
|
||||||
bounds.getMaximum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0));
|
|
||||||
|
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
|
/// \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;
|
size_t wantedLod = 0;
|
||||||
if (dist > 8192*1)
|
if (dist > 8192*1)
|
||||||
wantedLod = 1;
|
wantedLod = 1;
|
||||||
|
@ -230,6 +244,7 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
|
|
||||||
if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod)
|
if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod)
|
||||||
{
|
{
|
||||||
|
bool hadChunk = hasChunk();
|
||||||
// Wanted LOD is small enough to render this node in one chunk
|
// Wanted LOD is small enough to render this node in one chunk
|
||||||
if (!mChunk)
|
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);
|
mChunk->setVisible(true);
|
||||||
|
|
||||||
if (hasChildren())
|
if (!hadChunk && hasChildren())
|
||||||
{
|
{
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mChildren[i]->removeChunks();
|
mChildren[i]->hideChunks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -272,15 +305,41 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::removeChunks()
|
void QuadTreeNode::hideChunks()
|
||||||
{
|
{
|
||||||
if (mChunk)
|
if (mChunk)
|
||||||
mChunk->setVisible(false);
|
mChunk->setVisible(false);
|
||||||
if (hasChildren())
|
else if (hasChildren())
|
||||||
{
|
|
||||||
for (int i=0; i<4; ++i)
|
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()
|
void QuadTreeNode::updateIndexBuffers()
|
||||||
|
@ -312,7 +371,7 @@ void QuadTreeNode::ensureLayerInfo()
|
||||||
|
|
||||||
std::vector<Ogre::TexturePtr> blendmaps;
|
std::vector<Ogre::TexturePtr> blendmaps;
|
||||||
std::vector<std::string> layerList;
|
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->setLayerList(layerList);
|
||||||
mMaterialGenerator->setBlendmapList(blendmaps);
|
mMaterialGenerator->setBlendmapList(blendmaps);
|
||||||
|
@ -324,7 +383,9 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
||||||
|
|
||||||
if (mIsDummy)
|
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;
|
std::vector<std::string> layer;
|
||||||
layer.push_back("_land_default.dds");
|
layer.push_back("_land_default.dds");
|
||||||
matGen.setLayerList(layer);
|
matGen.setLayerList(layer);
|
||||||
|
@ -357,12 +418,6 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool QuadTreeNode::hasCompositeMap()
|
|
||||||
{
|
|
||||||
return !mCompositeMap.isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadTreeNode::ensureCompositeMap()
|
void QuadTreeNode::ensureCompositeMap()
|
||||||
{
|
{
|
||||||
if (!mCompositeMap.isNull())
|
if (!mCompositeMap.isNull())
|
||||||
|
|
|
@ -49,6 +49,13 @@ namespace Terrain
|
||||||
QuadTreeNode (Terrain* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
QuadTreeNode (Terrain* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
||||||
~QuadTreeNode();
|
~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
|
/// @note takes ownership of \a child
|
||||||
void createChild (ChildDirection id, float size, const Ogre::Vector2& center);
|
void createChild (ChildDirection id, float size, const Ogre::Vector2& center);
|
||||||
|
|
||||||
|
@ -66,15 +73,15 @@ namespace Terrain
|
||||||
bool hasChildren() { return mChildren[0] != 0; }
|
bool hasChildren() { return mChildren[0] != 0; }
|
||||||
QuadTreeNode* getChild(ChildDirection dir) { return mChildren[dir]; }
|
QuadTreeNode* getChild(ChildDirection dir) { return mChildren[dir]; }
|
||||||
|
|
||||||
/// Search for a neighbour node in this direction
|
/// Get neighbour node in this direction
|
||||||
QuadTreeNode* searchNeighbour (Direction dir);
|
QuadTreeNode* getNeighbour (Direction dir);
|
||||||
|
|
||||||
/// Returns our direction relative to the parent node, or Root if we are the root node.
|
/// Returns our direction relative to the parent node, or Root if we are the root node.
|
||||||
ChildDirection getDirection() { return mDirection; }
|
ChildDirection getDirection() { return mDirection; }
|
||||||
|
|
||||||
/// Set bounding box in local coordinates. Should be done at load time for leaf nodes.
|
/// Set bounding box in local coordinates. Should be done at load time for leaf nodes.
|
||||||
/// Other nodes can merge AABB of child 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
|
/// Get bounding box in local coordinates
|
||||||
const Ogre::AxisAlignedBox& getBoundingBox();
|
const Ogre::AxisAlignedBox& getBoundingBox();
|
||||||
|
@ -88,8 +95,11 @@ namespace Terrain
|
||||||
/// Call after QuadTreeNode::update!
|
/// Call after QuadTreeNode::update!
|
||||||
void updateIndexBuffers();
|
void updateIndexBuffers();
|
||||||
|
|
||||||
/// Remove chunks rendered by this node and all its children
|
/// Hide chunks rendered by this node and all its children
|
||||||
void removeChunks();
|
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
|
/// Get the effective LOD level if this node was rendered in one chunk
|
||||||
/// with ESM::Land::LAND_SIZE^2 vertices
|
/// with ESM::Land::LAND_SIZE^2 vertices
|
||||||
|
@ -101,8 +111,6 @@ namespace Terrain
|
||||||
/// Is this node currently configured to render itself?
|
/// Is this node currently configured to render itself?
|
||||||
bool hasChunk();
|
bool hasChunk();
|
||||||
|
|
||||||
bool hasCompositeMap();
|
|
||||||
|
|
||||||
/// Add a textured quad to a specific 2d area in the composite map scenemanager.
|
/// 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
|
/// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply
|
||||||
/// call this method on their children.
|
/// call this method on their children.
|
||||||
|
@ -118,6 +126,7 @@ namespace Terrain
|
||||||
float mSize;
|
float mSize;
|
||||||
size_t mLodLevel; // LOD if we were to render this node in one chunk
|
size_t mLodLevel; // LOD if we were to render this node in one chunk
|
||||||
Ogre::AxisAlignedBox mBounds;
|
Ogre::AxisAlignedBox mBounds;
|
||||||
|
Ogre::AxisAlignedBox mWorldBounds;
|
||||||
ChildDirection mDirection;
|
ChildDirection mDirection;
|
||||||
Ogre::Vector2 mCenter;
|
Ogre::Vector2 mCenter;
|
||||||
|
|
||||||
|
@ -125,6 +134,7 @@ namespace Terrain
|
||||||
|
|
||||||
QuadTreeNode* mParent;
|
QuadTreeNode* mParent;
|
||||||
QuadTreeNode* mChildren[4];
|
QuadTreeNode* mChildren[4];
|
||||||
|
QuadTreeNode* mNeighbours[4];
|
||||||
|
|
||||||
Chunk* mChunk;
|
Chunk* mChunk;
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,51 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row)
|
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)
|
if (col == ESM::Land::LAND_SIZE-1)
|
||||||
{
|
{
|
||||||
|
@ -65,12 +110,19 @@ namespace Terrain
|
||||||
row = 0;
|
row = 0;
|
||||||
}
|
}
|
||||||
ESM::Land* land = getLand(cellX, cellY);
|
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];
|
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
||||||
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
||||||
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
|
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,
|
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.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.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.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();
|
normal.normalise();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
normal = Ogre::Vector3(0,0,1);
|
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] = normal.x;
|
||||||
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
|
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
|
||||||
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
|
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
|
||||||
|
@ -168,6 +224,11 @@ namespace Terrain
|
||||||
color.g = 1;
|
color.g = 1;
|
||||||
color.b = 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;
|
color.a = 1;
|
||||||
Ogre::uint32 rsColor;
|
Ogre::uint32 rsColor;
|
||||||
Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
|
Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
|
||||||
|
@ -193,18 +254,20 @@ namespace Terrain
|
||||||
Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY,
|
Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY,
|
||||||
int x, int y)
|
int x, int y)
|
||||||
{
|
{
|
||||||
// If we're at the last row (or last column), we need to get the texture from the neighbour cell
|
// For the first/last row/column, we need to get the texture from the neighbour cell
|
||||||
// to get consistent blending at the border
|
// to get consistent blending at the borders
|
||||||
if (x >= ESM::Land::LAND_TEXTURE_SIZE)
|
--x;
|
||||||
|
if (x < 0)
|
||||||
{
|
{
|
||||||
cellX++;
|
--cellX;
|
||||||
x -= ESM::Land::LAND_TEXTURE_SIZE;
|
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;
|
y -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(x<ESM::Land::LAND_TEXTURE_SIZE);
|
assert(x<ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
|
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
|
||||||
|
@ -226,7 +289,7 @@ namespace Terrain
|
||||||
std::string Storage::getTextureName(UniqueTextureId id)
|
std::string Storage::getTextureName(UniqueTextureId id)
|
||||||
{
|
{
|
||||||
if (id.first == 0)
|
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
|
// NB: All vtex ids are +1 compared to the ltex ids
|
||||||
const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second);
|
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,
|
void Storage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
|
||||||
bool pack, std::vector<Ogre::TexturePtr> &blendmaps, std::vector<std::string> &layerList)
|
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);
|
Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f);
|
||||||
int cellX = origin.x;
|
int cellX = origin.x;
|
||||||
int cellY = origin.y;
|
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.
|
// 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.
|
// So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell.
|
||||||
textureIndices.insert(std::make_pair(0,0));
|
textureIndices.insert(std::make_pair(0,0));
|
||||||
// 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 y=0; y<ESM::Land::LAND_TEXTURE_SIZE+1; ++y)
|
||||||
for (int x=0; x<ESM::Land::LAND_TEXTURE_SIZE+1; ++x)
|
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<Ogre::TexturePtr>& blendmaps,
|
||||||
std::vector<std::string>& layerList);
|
std::vector<std::string>& layerList);
|
||||||
|
|
||||||
|
float getHeightAt (const Ogre::Vector3& worldPos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
|
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
|
// Since plugins can define new texture palettes, we need to know the plugin index too
|
||||||
// in order to retrieve the correct texture name.
|
// in order to retrieve the correct texture name.
|
||||||
|
|
|
@ -51,12 +51,14 @@ namespace
|
||||||
namespace Terrain
|
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)
|
: mStorage(storage)
|
||||||
, mMinBatchSize(1)
|
, mMinBatchSize(1)
|
||||||
, mMaxBatchSize(64)
|
, mMaxBatchSize(64)
|
||||||
, mSceneMgr(sceneMgr)
|
, mSceneMgr(sceneMgr)
|
||||||
, mVisibilityFlags(visibilityFlags)
|
, mVisibilityFlags(visibilityFlags)
|
||||||
|
, mDistantLand(distantLand)
|
||||||
|
, mShaders(shaders)
|
||||||
{
|
{
|
||||||
mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
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);
|
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL);
|
||||||
buildQuadTree(mRootNode);
|
buildQuadTree(mRootNode);
|
||||||
|
mRootNode->initAabb();
|
||||||
|
mRootNode->initNeighbours();
|
||||||
}
|
}
|
||||||
|
|
||||||
Terrain::~Terrain()
|
Terrain::~Terrain()
|
||||||
|
@ -136,14 +140,19 @@ namespace Terrain
|
||||||
node->markAsDummy();
|
node->markAsDummy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terrain::update(Ogre::Camera *camera)
|
void Terrain::update(const Ogre::Vector3& cameraPos)
|
||||||
{
|
{
|
||||||
mRootNode->update(camera->getRealPosition());
|
mRootNode->update(cameraPos);
|
||||||
mRootNode->updateIndexBuffers();
|
mRootNode->updateIndexBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ogre::AxisAlignedBox Terrain::getWorldBoundingBox (const Ogre::Vector2& center)
|
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);
|
QuadTreeNode* node = findNode(center, mRootNode);
|
||||||
Ogre::AxisAlignedBox box = node->getBoundingBox();
|
Ogre::AxisAlignedBox box = node->getBoundingBox();
|
||||||
box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * 8192,
|
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)
|
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+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+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);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment);
|
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+row);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row);
|
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row);
|
||||||
// Make sure not to touch the left edge
|
// Make sure not to touch the right edge
|
||||||
if (col == 0)
|
if (col+outerStep == ESM::Land::LAND_SIZE-1)
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+innerStep);
|
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row+innerStep);
|
||||||
else
|
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)
|
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||||
{
|
{
|
||||||
// Make sure not to touch the left or right edges
|
// Make sure not to touch the left or right edges
|
||||||
if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep)
|
if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep)
|
||||||
continue;
|
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+innerStep)+row+innerStep);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+i)+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+outerStep)+row);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||||
// Make sure not to touch the right edge
|
// Make sure not to touch the left edge
|
||||||
if (col+outerStep == ESM::Land::LAND_SIZE-1)
|
if (col == 0)
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row-innerStep);
|
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row-innerStep);
|
||||||
else
|
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)
|
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||||
{
|
{
|
||||||
|
@ -278,7 +288,7 @@ namespace Terrain
|
||||||
continue;
|
continue;
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row-innerStep);
|
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+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+outerStep);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
indices.push_back(ESM::Land::LAND_SIZE*col+row);
|
||||||
// Make sure not to touch the bottom edge
|
// Make sure not to touch the top edge
|
||||||
if (row == 0)
|
if (row+outerStep == ESM::Land::LAND_SIZE-1)
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+innerStep);
|
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep-innerStep);
|
||||||
else
|
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)
|
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||||
{
|
{
|
||||||
// Make sure not to touch the top or bottom edges
|
// Make sure not to touch the top or bottom edges
|
||||||
if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep)
|
if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep)
|
||||||
continue;
|
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);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i+innerStep);
|
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);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep);
|
indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep);
|
||||||
// Make sure not to touch the top edge
|
// Make sure not to touch the bottom edge
|
||||||
if (row+outerStep == ESM::Land::LAND_SIZE-1)
|
if (row == 0)
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+outerStep-innerStep);
|
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+innerStep);
|
||||||
else
|
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)
|
for (size_t i = 0; i < outerStep; i += innerStep)
|
||||||
{
|
{
|
||||||
// Make sure not to touch the top or bottom edges
|
// Make sure not to touch the top or bottom edges
|
||||||
if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep)
|
if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep)
|
||||||
continue;
|
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+innerStep);
|
||||||
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i);
|
indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i);
|
||||||
}
|
}
|
||||||
|
@ -355,5 +365,10 @@ namespace Terrain
|
||||||
mCompositeMapSceneMgr->clearScene();
|
mCompositeMapSceneMgr->clearScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Terrain::getHeightAt(const Ogre::Vector3 &worldPos)
|
||||||
|
{
|
||||||
|
return mStorage->getHeightAt(worldPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,16 +28,25 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @note takes ownership of \a storage
|
/// @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();
|
~Terrain();
|
||||||
|
|
||||||
|
bool getDistantLandEnabled() { return mDistantLand; }
|
||||||
|
bool getShadersEnabled() { return mShaders; }
|
||||||
|
|
||||||
|
float getHeightAt (const Ogre::Vector3& worldPos);
|
||||||
|
|
||||||
/// Update chunk LODs according to this camera position
|
/// Update chunk LODs according to this camera position
|
||||||
/// @note Calling this method might lead to composite textures being rendered, so it is best
|
/// @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.
|
/// not to call it when render commands are still queued, since that would cause a flush.
|
||||||
void update (Ogre::Camera* camera);
|
void update (const Ogre::Vector3& cameraPos);
|
||||||
|
|
||||||
/// \todo
|
|
||||||
float getHeightAt (const Ogre::Vector3& worldPos) { return 0; }
|
|
||||||
|
|
||||||
/// Get the world bounding box of a chunk of terrain centered at \a center
|
/// Get the world bounding box of a chunk of terrain centered at \a center
|
||||||
Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center);
|
Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center);
|
||||||
|
@ -63,6 +72,9 @@ namespace Terrain
|
||||||
void enableSplattingShader(bool enabled);
|
void enableSplattingShader(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool mDistantLand;
|
||||||
|
bool mShaders;
|
||||||
|
|
||||||
QuadTreeNode* mRootNode;
|
QuadTreeNode* mRootNode;
|
||||||
Storage* mStorage;
|
Storage* mStorage;
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,7 @@
|
||||||
@shEndForeach
|
@shEndForeach
|
||||||
lightResult.xyz += lightAmbient.xyz;
|
lightResult.xyz += lightAmbient.xyz;
|
||||||
lightResult.xyz *= colour.xyz;
|
lightResult.xyz *= colour.xyz;
|
||||||
|
directionalResult.xyz *= colour.xyz;
|
||||||
|
|
||||||
@shPassthroughAssign(lightResult, lightResult);
|
@shPassthroughAssign(lightResult, lightResult);
|
||||||
@shPassthroughAssign(directionalResult, directionalResult);
|
@shPassthroughAssign(directionalResult, directionalResult);
|
||||||
|
|
|
@ -335,12 +335,12 @@
|
||||||
</Widget>
|
</Widget>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<Widget type="HBox" position="4 172 350 24">
|
<Widget type="HBox" position="4 140 350 24">
|
||||||
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="ShadowsDebug"/>
|
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="TerrainShadows"/>
|
||||||
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
|
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
|
||||||
<Property key="Caption" value="Debug overlay"/>
|
<Property key="Caption" value="Terrain shadows"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,7 @@ texture size = 1024
|
||||||
actor shadows = true
|
actor shadows = true
|
||||||
misc shadows = true
|
misc shadows = true
|
||||||
statics shadows = true
|
statics shadows = true
|
||||||
|
terrain shadows = true
|
||||||
|
|
||||||
# Fraction of the total shadow distance after which the shadow starts to fade out
|
# Fraction of the total shadow distance after which the shadow starts to fade out
|
||||||
fade start = 0.8
|
fade start = 0.8
|
||||||
|
@ -125,8 +126,9 @@ fog start factor = 0.5
|
||||||
fog end factor = 1.0
|
fog end factor = 1.0
|
||||||
|
|
||||||
[Terrain]
|
[Terrain]
|
||||||
# Max. number of lights that affect the terrain. Setting to 1 will only reflect sunlight
|
distant land = false
|
||||||
num lights = 8
|
|
||||||
|
shader = true
|
||||||
|
|
||||||
[Water]
|
[Water]
|
||||||
shader = true
|
shader = true
|
||||||
|
|
Loading…
Reference in a new issue