Skip to content

Instantly share code, notes, and snippets.

@jtsagata
Created September 18, 2013 01:24
Show Gist options
  • Save jtsagata/6603244 to your computer and use it in GitHub Desktop.
Save jtsagata/6603244 to your computer and use it in GitHub Desktop.
#include "terrain.hpp"
TerrainEngine::TerrainEngine( Ogre::String seed, Ogre::Root *root, Ogre::SceneManager *scenemgr, Ogre::Camera *cam, Ogre::Light* light, Ogre::String file_prefix, Ogre::String file_suffix )
: mRoot(root)
, mTerrainGroup(0)
, mTerrainPos(1000,0,5000)
, mLayerEdit(1)
, mTerrainsImported(false)
, mSceneMgr(scenemgr)
, mCamera(cam)
, mSlotX(0)
, mSlotY(0)
, mUpdateTerrains(true)
, mLockTerrains(false)
, mTerrainPageProvider( hashString(seed) )
{
terrainSelectClear();
mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();
mTerrainGroup = OGRE_NEW Ogre::TerrainGroup( mSceneMgr, Ogre::Terrain::ALIGN_X_Z, TERRAIN_SIZE, TERRAIN_WORLD_SIZE );
std::stringstream fpx;
mTerrainGroup->setFilenameConvention( file_prefix, file_suffix );
mTerrainGroup->setOrigin( mTerrainPos );
configureTerrainDefaults( light );
//mTerrainGroup->convertWorldPositionToTerrainSlot( cam->getPosition(), &mSlotX, &mSlotY );
mPageManager = OGRE_NEW Ogre::PageManager();
mPageManager->setPageProvider(&mTerrainPageProvider);
mPageManager->addCamera(mCamera);
mTerrainPaging = OGRE_NEW Ogre::TerrainPaging(mPageManager);
Ogre::PagedWorld* world = mPageManager->createWorld();
mTerrainPaging->createWorldSection(world, mTerrainGroup, 2000, 3000,
TERRAIN_PAGE_MIN_X, TERRAIN_PAGE_MIN_Y,
TERRAIN_PAGE_MAX_X, TERRAIN_PAGE_MAX_Y);
}
TerrainEngine::~TerrainEngine( )
{
mTerrainGroup->saveAllTerrains( true );
terrainSelectClear();
OGRE_DELETE mTerrainGroup;
OGRE_DELETE mTerrainGlobals;
OGRE_DELETE mTerrainPaging;
OGRE_DELETE mPageManager;
}
void TerrainEngine::terrainSelect( Ogre::Terrain *terrain, Ogre::Vector3 position, Ogre::Real radius )
{
mSelected.terrain = terrain;
mSelected.position = position;
mSelected.radius = radius;
}
TerrainEngine::TerrainSelect *TerrainEngine::terrainSelect( void ) {
return &mSelected;
}
bool TerrainEngine::terrainSelected( void )
{
return ( mSelected.terrain == NULL ? false : true );
}
void TerrainEngine::terrainSelectClear( void )
{
mSelected.terrain = NULL;
mSelected.position = Ogre::Vector3(0,0,0);
mSelected.radius = 0.01;
}
void TerrainEngine::changeTerrainHeight( Ogre::Real scale )
{
if( mSelected.terrain == NULL ) {
// Log something in the console?
return; // Get the hell out of dodge if we don't have a terrain selected
}
Ogre::Real terrainSize = ( mSelected.terrain->getSize() - 1 );
long startx = ( mSelected.position.x - mSelected.radius ) * terrainSize;
long starty = ( mSelected.position.y - mSelected.radius ) * terrainSize;
long endx = ( mSelected.position.x + mSelected.radius ) * terrainSize;
long endy= ( mSelected.position.y + mSelected.radius ) * terrainSize;
startx = std::max(startx, 0L);
starty = std::max(starty, 0L);
endx = std::min(endx, (long)terrainSize);
endy = std::min(endy, (long)terrainSize);
for (long y = starty; y <= endy; ++y)
{
for (long x = startx; x <= endx; ++x)
{
Ogre::Real tsXdist = (x / terrainSize) - mSelected.position.x;
Ogre::Real tsYdist = (y / terrainSize) - mSelected.position.y;
Ogre::Real weight = std::min((Ogre::Real)1.0, std::sqrt(tsYdist * tsYdist + tsXdist * tsXdist) / Ogre::Real(0.5 * mSelected.radius));
weight = 1.0 - (weight * weight);
float addedHeight = weight * 250.0 * scale; // * timeElapsed;
float newheight = mSelected.terrain->getHeightAtPoint(x, y) + addedHeight;
mSelected.terrain->setHeightAtPoint(x, y, newheight);
}
}
}
void TerrainEngine::changeTerrainTexture( Ogre::Real rate )
{
if( mSelected.terrain == NULL ) {
// Log something in the console?
return; // Get the hell out of dodge if we don't have a terrain selected
}
Ogre::TerrainLayerBlendMap *layer = mSelected.terrain->getLayerBlendMap( mLayerEdit );
Ogre::Real imgSize = mSelected.terrain->getLayerBlendMapSize();
long startx = ( mSelected.position.x - mSelected.radius ) * imgSize;
long starty = ( mSelected.position.y - mSelected.radius ) * imgSize;
long endx = ( mSelected.position.x + mSelected.radius ) * imgSize;
long endy= ( mSelected.position.y + mSelected.radius ) * imgSize;
startx = std::max(startx, 0L);
starty = std::max(starty, 0L);
endx = std::min(endx, (long)imgSize);
endy = std::min(endy, (long)imgSize);
for (long y = starty; y <= endy; ++y)
{
for (long x = startx; x <= endx; ++x)
{
Ogre::Real tsXdist = (x / imgSize) - mSelected.position.x;
Ogre::Real tsYdist = (y / imgSize) - mSelected.position.y;
Ogre::Real weight = std::min( (Ogre::Real)1.0, std::sqrt( tsYdist * tsYdist + tsXdist * tsXdist ) / Ogre::Real( 0.5 * mSelected.radius ) );
weight = 1.0 - (weight * weight);
float paint = weight * rate; // * timeElapsed;
size_t imgY = imgSize - y;
float val = layer->getBlendValue( x, imgY ) + paint;
//val = Math::Clamp(val, 0.0f, 1.0f);
val = std::max( std::min( 1.0f, val ), 0.0f );
layer->setBlendValue( x, imgY, val );
}
}
layer->update();
}
void TerrainEngine::changeTextureLayer( Ogre::uint8 layer )
{
if( mSelected.terrain == NULL ) {
// Log something in the console?
return; // Get the hell out of dodge if we don't have a terrain selected
}
mLayerEdit = ( ( layer < 1 ) ? 1 : layer );
}
void TerrainEngine::onFrameRenderingQueued( void )
{
#if 0
if ( !mTerrainGroup->isDerivedDataUpdateInProgress() ) {
// Load/unload the next piece
for( int i = 0; i < 3; i++ ) {
TerrainQueue *tq = terrainQueueNext();
if( tq ) {
loadTerrain( tq->x, tq->y, !tq->load );
terrainQueuePop();
std::stringstream liq;
liq << "Terrain chunks left in queue: " << terrainQueue.size();
Ogre::LogManager::getSingletonPtr()->logMessage(liq.str());
} else { break; }
}
}
long int csx, csy;
mTerrainGroup->convertWorldPositionToTerrainSlot( mCamera->getPosition(), &csx, &csy );
if( ( ( ( csx != mSlotX ) || ( csy != mSlotY ) ) || mUpdateTerrains ) && !mLockTerrains ) {
std::stringstream tus;
tus << "Updating terrain load/unload list; Camera Position=" << mCamera->getPosition() << "; mSlotX,Y=(" << mSlotX << ", " << mSlotY << "); CameraSlotX,Y=("<< csx <<","<< csy <<")";
Ogre::LogManager::getSingletonPtr()->logMessage(tus.str());
mSlotX = csx;
mSlotY = csy;
mUpdateTerrains = false;
// Load Center first
/*TerrainQueue tq;
tq.terrain = mTerrainGroup->getTerrain( mSlotX, mSlotY );
tq.x = mSlotX;
tq.y = mSlotY;
tq.load = true;
terrainQueuePush( tq, true ); // Set as next priority!
*/
loadTerrain( mSlotX, mSlotY );
// Loop through distances
for( long int dist = 0; dist < TERRAIN_DIST; dist++ ) {
terrainQueueAtDistance( dist, true );
}
//terrainQueueAtDistance( TERRAIN_DIST, false );
}
#endif
}
bool TerrainEngine::getTerrainLocked( void )
{
return mLockTerrains;
}
void TerrainEngine::setTerrainLocked( bool lock )
{
mLockTerrains = lock;
}
/*void TerrainEngine::fixCameraTerrain( Ogre::Camera *cam, float height )
{
Ogre::Terrain *activeTerrain = mTerrainGroup->getTerrain( mSlotX, mSlotY );
if( !activeTerrain ) return;
Ogre::Vector3 tpos, cpos;
cpos = mCamera->getPosition();
activeTerrain->getTerrainPosition( cpos, &tpos );
cpos.y = height + activeTerrain->getHeightAtWorldPosition( cpos );
mCamera->setPosition( cpos );
}*/
bool TerrainEngine::defineTerrain( long x, long y )
{
bool needsWork = false;
Ogre::String filename = mTerrainGroup->generateFilename(x, y);
if( Ogre::ResourceGroupManager::getSingleton().resourceExists(mTerrainGroup->getResourceGroup(), filename) ) {
mTerrainGroup->defineTerrain(x, y);
} else {
mTerrainGroup->defineTerrain( x, y, 0.0f );
needsWork = true;
}
return needsWork;
}
void TerrainEngine::initBlendMaps( Ogre::Terrain *terrain )
{
Ogre::TerrainLayerBlendMap* blendMap0 = terrain->getLayerBlendMap( 1 );
Ogre::TerrainLayerBlendMap* blendMap1 = terrain->getLayerBlendMap( 2 );
Ogre::Real minHeight0 = 70;
Ogre::Real fadeDist0 = 40;
Ogre::Real minHeight1 = 70;
Ogre::Real fadeDist1 = 15;
float* pBlend1 = blendMap0->getBlendPointer();
for (Ogre::uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y)
{
for (Ogre::uint16 x = 0; x < terrain->getLayerBlendMapSize(); ++x)
{
Ogre::Real tx, ty;
blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);
Ogre::Real height = terrain->getHeightAtTerrainPosition(tx, ty);
Ogre::Real val = (height - minHeight0) / fadeDist0;
val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
val = (height - minHeight1) / fadeDist1;
val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
*pBlend1++ = val;
}
}
blendMap0->dirty();
blendMap1->dirty();
blendMap0->update();
blendMap1->update();
}
void TerrainEngine::configureTerrainDefaults( Ogre::Light *light )
{
mTerrainGlobals->setMaxPixelError(8);
mTerrainGlobals->setCompositeMapDistance(3000);
mTerrainGlobals->setLightMapDirection(light->getDerivedDirection());
mTerrainGlobals->setCompositeMapAmbient(mSceneMgr->getAmbientLight());
mTerrainGlobals->setCompositeMapDiffuse(light->getDiffuseColour());
Ogre::Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
defaultimp.terrainSize = TERRAIN_SIZE;
defaultimp.worldSize = TERRAIN_WORLD_SIZE;
defaultimp.inputScale = 600;
defaultimp.minBatchSize = 65;
defaultimp.maxBatchSize = 129;
defaultimp.layerList.resize(3);
defaultimp.layerList[0].worldSize = 200;
defaultimp.layerList[0].textureNames.push_back("terrain-grass-ds");
defaultimp.layerList[0].textureNames.push_back("terrain-grass-nh");
defaultimp.layerList[1].worldSize = 300;
defaultimp.layerList[1].textureNames.push_back("terrain-dirt-ds");
defaultimp.layerList[1].textureNames.push_back("terrain-dirt-nh");
defaultimp.layerList[2].worldSize = 50;
defaultimp.layerList[2].textureNames.push_back("terrain-rock-ds");
defaultimp.layerList[2].textureNames.push_back("terrain-rock-nh");
}
/*void TerrainEngine::loadTerrain( long int x, long int y, bool unload )
{
std::stringstream _log;
Ogre::Terrain *terrain = mTerrainGroup->getTerrain( x, y );
if( !unload ) {
_log << "Loading terrain (" << x << ", " << y << ")";
Ogre::LogManager::getSingletonPtr()->logMessage( _log.str() );
if( !terrain ) {
Ogre::LogManager::getSingletonPtr()->logMessage(" - Needs definition");
if( defineTerrain( x, y ) ) {
mTerrainGroup->loadTerrain( x, y, true );
terrain = mTerrainGroup->getTerrain( x, y );
int sz = terrain->getSize();
for( long tx = 0; tx < sz; tx++ ) {
for( long tz = 0; tz < sz; tz++ ) {
Ogre::Vector3 worldpos;
terrain->getPoint( tx, tz, &worldpos );
terrain->setHeightAtPoint( tx, tz, smoothNoise( worldpos.x, worldpos.z, 0.01f ) );
}
}
terrain->update();
terrain->dirty();
mTerrainGroup->update();
} else {
mTerrainGroup->loadTerrain( x, y, true ); // May need to be true?
}
}
} else {
if( terrain ) {
_log << "Unloading terrain (" << x << ", " << y << ")";
Ogre::LogManager::getSingletonPtr()->logMessage( _log.str() );
if( terrain->isModified() ) {
Ogre::LogManager::getSingletonPtr()->logMessage(" - Saving modifications");
terrain->save( mTerrainGroup->generateFilename( x, y ) );
}
Ogre::LogManager::getSingletonPtr()->logMessage(" - Unloading from TerrainGroup");
mTerrainGroup->unloadTerrain( x, y );
}
}
Ogre::LogManager::getSingletonPtr()->logMessage(" - Done");
}*/
float smoothNoise( Perlin *mPerlin, float x, float y, float scale )
{
float corners = ( mPerlin->Get(x-scale, y-scale) + mPerlin->Get(x+scale, y-scale) + mPerlin->Get(x-scale, y+scale) + mPerlin->Get(x+scale, y+scale) ) / 16;
float sides = ( mPerlin->Get(x-scale, y) + mPerlin->Get(x+scale, y) + mPerlin->Get(x, y-scale) + mPerlin->Get(x, y+scale) ) / 8;
float center = mPerlin->Get(x, y) / 4;
return corners + sides + center;
}
#include <OgreRoot.h>
#include <OgreConfigFile.h>
#include <OgreResourceGroupManager.h>
#include <OgreCamera.h>
#include <Paging/OgrePage.h>
#include <Paging/OgrePageManager.h>
#include <Terrain/OgreTerrain.h>
#include <Terrain/OgreTerrainGroup.h>
#include <Terrain/OgreTerrainPagedWorldSection.h>
#include <Terrain/OgreTerrainPaging.h>
#include <Terrain/OgreTerrainQuadTreeNode.h>
#include <Terrain/OgreTerrainMaterialGeneratorA.h>
#include <cmath>
#include <algorithm>
#include <deque>
#include "perlin.h"
#include "main.hpp"
#ifndef __terrain_hpp__
#define __terrain_hpp__ 1
#define TERRAIN_PAGE_MIN_X -90000000.0f
#define TERRAIN_PAGE_MIN_Y -90000000.0f
#define TERRAIN_PAGE_MAX_X 90000000.0f
#define TERRAIN_PAGE_MAX_Y 90000000.0f
float smoothNoise( Perlin *mPerlin, float x, float y, float scale );
class TerrainPP : public Ogre::PageProvider {
public:
TerrainPP( int seed )
: mPerlin(16, 0.0001, 900, seed)
{ }
bool prepareProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) {
Ogre::TerrainGroup* pGroup = ((Ogre::TerrainPagedWorldSection*)section)->getTerrainGroup();
long x, y;
pGroup->unpackIndex(page->getID(), &x, &y);
Ogre::String filename = pGroup->generateFilename(x, y);
//if( Ogre::ResourceGroupManager::getSingleton().resourceExists(pGroup->getResourceGroup(), filename) ) {
// pGroup->defineTerrain(x, y);
//} else {
pGroup->defineTerrain( x, y, 0.0f );
//}
return true;
}
bool loadProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) {
const Ogre::AxisAlignedBox bb = section->getBoundingBox( );
Ogre::TerrainGroup* pGroup = ((Ogre::TerrainPagedWorldSection*)section)->getTerrainGroup();
long x, y;
pGroup->unpackIndex(page->getID(), &x, &y);
//Ogre::String filename = pGroup->generateFilename(x, y);
//if( !Ogre::ResourceGroupManager::getSingleton().resourceExists(pGroup->getResourceGroup(), filename) ) {
pGroup->loadTerrain( x, y, true );
/*Ogre::Terrain *terrain = pGroup->getTerrain( x, y );
for( long tx = bb.getMinimum().x; tx < bb.getMaximum().x; tx++ ) {
for( long tz = bb.getMinimum().z; tz < bb.getMaximum().z; tz++ ) {
terrain->setHeightAtPoint( tx, tz, smoothNoise( &mPerlin, tx, tz, 0.01f ) );
}
}
terrain->update();
terrain->dirty();*/
pGroup->update();
//}
return true;
}
bool unloadProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) { return true; }
bool unprepareProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) { return true; }
Perlin mPerlin;
};
class TerrainEngine
{
public:
TerrainEngine( Ogre::String seed, Ogre::Root *root, Ogre::SceneManager *scenemgr, Ogre::Camera *cam, Ogre::Light* light, Ogre::String file_prefix = "undeadland", Ogre::String file_suffix = "dat" );
~TerrainEngine( );
typedef struct TerrainSelect {
Ogre::Terrain *terrain;
Ogre::Vector3 position;
Ogre::Real radius;
} TerrainSelect;
void terrainSelect( Ogre::Terrain *terrain, Ogre::Vector3 position, Ogre::Real radius = 10.0f );
TerrainSelect *terrainSelect( void );
bool terrainSelected( void );
void terrainSelectClear( void );
void changeTerrainHeight( Ogre::Real scale = 1.0f );
void changeTerrainTexture( Ogre::Real rate = 1.0f );
void changeTextureLayer( Ogre::uint8 layer );
void onFrameRenderingQueued( );
bool getTerrainLocked( void );
void setTerrainLocked( bool lock );
//void fixCameraTerrain( Ogre::Camera *cam, float height = 35.0f );
protected:
bool defineTerrain( long x, long y );
void initBlendMaps( Ogre::Terrain *terrain );
void configureTerrainDefaults( Ogre::Light *light );
//void loadTerrain( long int x, long int y, bool unload = false );
private:
//float smoothNoise( float _x, float _y, float scale = 1.0f );
Ogre::Root *mRoot;
Ogre::Camera *mCamera;
Ogre::TerrainGlobalOptions* mTerrainGlobals;
Ogre::TerrainGroup* mTerrainGroup;
Ogre::Vector3 mTerrainPos;
Ogre::SceneNode* mEditNode;
Ogre::Entity* mEditMarker;
TerrainSelect mSelected;
Ogre::SceneManager *mSceneMgr;
Ogre::uint8 mLayerEdit;
//Ogre::SceneNode* mEditNode;
bool mTerrainsImported;
long int mSlotX, mSlotY;
bool mUpdateTerrains;
bool mLockTerrains;
TerrainPP mTerrainPageProvider;
Ogre::TerrainPaging *mTerrainPaging;
Ogre::PageManager *mPageManager;
//typedef std::list<Ogre::Entity*> EntityList;
};
#endif /* __terrain_hpp__ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment