- started code cleanup/rewrite

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@113 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 16 years ago
parent a46804dae3
commit 2ac9503854

@ -33,8 +33,8 @@ mygui_cpp=mygui console
# Ditto for the landscape engine, in terrain/cpp_X.cpp # Ditto for the landscape engine, in terrain/cpp_X.cpp
terrain_cpp=baseland esm framelistener generator index landdata\ terrain_cpp=baseland esm framelistener generator index landdata\
materialgen meshinterface mwheightmap mwquadmatgen palette point2\ materialgen mwheightmap palette point2\
quad quaddata quadsegment terraincls terrain terrainmesh quad quaddata terraincls terrain terrainmesh
# FFmpeg files, in the form sound/cpp_X.cpp. # FFmpeg files, in the form sound/cpp_X.cpp.
avcodec_cpp=avcodec avcodec_cpp=avcodec

@ -76,6 +76,7 @@ void main(char[][] args)
bool noSound = false; bool noSound = false;
bool debugOut = false; bool debugOut = false;
bool extTest = false; bool extTest = false;
bool doGen = false;
// Some examples to try: // Some examples to try:
// //
@ -96,6 +97,7 @@ void main(char[][] args)
foreach(char[] a; args[1..$]) foreach(char[] a; args[1..$])
if(a == "-n") render = false; if(a == "-n") render = false;
else if(a == "-ex") extTest = true; else if(a == "-ex") extTest = true;
else if(a == "-gen") doGen = true;
else if(a == "-h") help=true; else if(a == "-h") help=true;
else if(a == "-rk") resetKeys = true; else if(a == "-rk") resetKeys = true;
else if(a == "-oc") showOgreFlag = true; else if(a == "-oc") showOgreFlag = true;
@ -122,6 +124,7 @@ void main(char[][] args)
writefln(" Options:"); writefln(" Options:");
writefln(" -n Only load, do not render"); writefln(" -n Only load, do not render");
writefln(" -ex Test the terrain system"); writefln(" -ex Test the terrain system");
writefln(" -gen Generate landscape cache");
writefln(" -rk Reset key bindings to default"); writefln(" -rk Reset key bindings to default");
writefln(" -oc Show the Ogre config dialogue"); writefln(" -oc Show the Ogre config dialogue");
writefln(" -ns Completely disable sound"); writefln(" -ns Completely disable sound");
@ -257,7 +260,7 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
ogre_makeSky(); ogre_makeSky();
*/ */
initTerrain(); initTerrain(doGen);
} }
else else
{ {

@ -1,15 +1,12 @@
class TerrainFrameListener : public FrameListener class TerrainFrameListener : public FrameListener
{ {
protected: protected:
Terrain* mTerrain;
MWHeightmap* mHeights;
/** /**
* Updates the quad tree * Updates the quad tree
*/ */
bool frameEnded(const FrameEvent& evt) bool frameEnded(const FrameEvent& evt)
{ {
mTerrain->update(evt.timeSinceLastFrame); g_Terrain->update(evt.timeSinceLastFrame);
return true; return true;
} }
@ -20,19 +17,18 @@ public:
mRoot->addFrameListener(this); mRoot->addFrameListener(this);
//our derived heightmap //our derived heightmap
mHeights = new MWHeightmap(); g_heightMap = new MWHeightmap();
mHeights->load(TERRAIN_OUTPUT); g_heightMap->load(TERRAIN_OUTPUT);
//setup terrain //setup terrain
mTerrain = new Terrain( mHeights, //heightmap g_Terrain = new Terrain(mSceneMgr->getRootSceneNode()->createChildSceneNode("TERRAIN_ROOT")); //root scene node
mSceneMgr->getRootSceneNode()->createChildSceneNode("TERRAIN_ROOT")); //root scene node
//fix settings //fix settings
mTerrain->setMorphingEnabled(false); g_Terrain->setMorphingEnabled(false);
mTerrain->setTextureFadingEnabled(false); g_Terrain->setTextureFadingEnabled(false);
//create the quad node //create the quad node
mTerrain->create(); g_Terrain->create();
} }
/* KILLME /* KILLME

@ -87,7 +87,7 @@ public:
int cellDist = pow((float)2, mIndex.getMaxDepth()); int cellDist = pow((float)2, mIndex.getMaxDepth());
// Temporary storage // Temporary storage
MWQuadData qd(0); QuadData qd;
qd.setVertexSeperation(128*halfLevel); //dist between two verts qd.setVertexSeperation(128*halfLevel); //dist between two verts
std::vector<float>& gh = qd.getHeightsRef(); //ref to the data storage in the quad std::vector<float>& gh = qd.getHeightsRef(); //ref to the data storage in the quad
@ -207,6 +207,8 @@ public:
boost::archive::binary_oarchive oap(ofp); boost::archive::binary_oarchive oap(ofp);
oai << mIndex; oai << mIndex;
oap << mPalette; oap << mPalette;
mDataO.close();
} }
private: private:
@ -333,13 +335,14 @@ private:
assert(Ogre::Math::Sqrt(ltex.size())==alphaSize); assert(Ogre::Math::Sqrt(ltex.size())==alphaSize);
std::list<Ogre::ResourcePtr> createdResources; std::list<Ogre::ResourcePtr> createdResources;
// FIXME: Move out of this function?
MaterialGenerator mg; MaterialGenerator mg;
mg.setTexturePaths(mPalette.getPalette()); mg.setTexturePaths(mPalette.getPalette());
const int scaleDiv = alphaSize/LAND_LTEX_WIDTH; const int scaleDiv = alphaSize/LAND_LTEX_WIDTH;
//genetate material/aplahas //genetate material/aplahas
Ogre::MaterialPtr mp = mg.getAlphaMat("Rtt_Alpha1", ltex, alphaSize, 0, scaleDiv,createdResources); Ogre::MaterialPtr mp = mg.getAlphaMat(ltex, alphaSize, 0, scaleDiv,createdResources);
Ogre::TexturePtr tex1 = getRenderedTexture(mp, "RTT_TEX_1",texSize, Ogre::PF_R8G8B8); Ogre::TexturePtr tex1 = getRenderedTexture(mp, "RTT_TEX_1",texSize, Ogre::PF_R8G8B8);
tex1->getBuffer()->getRenderTarget()->writeContentsToFile(outputName); tex1->getBuffer()->getRenderTarget()->writeContentsToFile(outputName);
Ogre::MaterialManager::getSingleton().remove(mp->getHandle()); Ogre::MaterialManager::getSingleton().remove(mp->getHandle());

@ -1,90 +1,90 @@
/** /**
* @brief holds data about positions of data and general header info * Holds index and other data describing the landscape.data file.
*/ */
class Index { class Index
{
public: public:
///saves my fingers :P typedef std::map<long, std::map<long, long> >::iterator OffsetItr;
typedef std::map<long, std::map<long, long> >::iterator OffsetItr; typedef std::map<long, std::map<long, long> >::const_iterator OffsetConstItr;
typedef std::map<long, std::map<long, long> >::const_iterator OffsetConstItr;
/** /**
* @brief sets the root quads side length in gu * @brief sets the root quads side length in gu
* @param l the side length * @param l the side length
* *
* This is used for working out the locations of quad children. * This is used for working out the locations of quad children.
* I am assuming a long is enough... * I am assuming a long is enough...
*/ */
inline void setRootSideLength(long l) { inline void setRootSideLength(long l) {
mRootSideLength = l; mRootSideLength = l;
} }
/** /**
* @return the side length of the root quad. * @return the side length of the root quad.
*/ */
inline long getRootSideLength() const { inline long getRootSideLength() const {
return mRootSideLength; return mRootSideLength;
} }
inline void setMaxDepth(int d) { inline void setMaxDepth(int d) {
mMaxDepth = d; mMaxDepth = d;
} }
inline int getMaxDepth() const { inline int getMaxDepth() const {
return mMaxDepth; return mMaxDepth;
} }
/** /**
* @return -1 is returned if there is no offset * @return -1 is returned if there is no offset
* @param x, y the position of the quad in gu * @param x, y the position of the quad in gu
* *
* Slightly faster using hasOffset to check if it exists * Slightly faster using hasOffset to check if it exists
* Shouldn't be noticable diffrence. * Shouldn't be noticable diffrence.
*/ */
inline long getOffset(long x, long y) const { //inline? inline long getOffset(long x, long y) const { //inline?
OffsetConstItr itr1 = mQuadOffsets.find(x); OffsetConstItr itr1 = mQuadOffsets.find(x);
if ( itr1 == mQuadOffsets.end() ) return -1; if ( itr1 == mQuadOffsets.end() ) return -1;
std::map<long, long>::const_iterator itr2 = itr1->second.find(y); std::map<long, long>::const_iterator itr2 = itr1->second.find(y);
if ( itr2 == itr1->second.end() ) return -1; if ( itr2 == itr1->second.end() ) return -1;
return itr2->second; return itr2->second;
} }
/** /**
* @brief checks if a quad for the given position exists * @brief checks if a quad for the given position exists
* @return true/false * @return true/false
* @param x, y the position of the quad in gu * @param x, y the position of the quad in gu
* *
* @todo Would it be worth merging this with getOffset? * @todo Would it be worth merging this with getOffset?
*/ */
inline bool hasOffset(long x, long y) const { inline bool hasOffset(long x, long y) const {
OffsetConstItr itr = mQuadOffsets.find(x); OffsetConstItr itr = mQuadOffsets.find(x);
if ( itr == mQuadOffsets.end() ) return false; if ( itr == mQuadOffsets.end() ) return false;
return (itr->second.find(y) != itr->second.end()); return (itr->second.find(y) != itr->second.end());
} }
/** /**
* @brief sets an offset of a quad * @brief sets an offset of a quad
* @param x, y the position in gu of the quad * @param x, y the position in gu of the quad
* @param o the offset within the file of the records for this quad * @param o the offset within the file of the records for this quad
*/ */
inline void setOffset(long x, long y, long o) { inline void setOffset(long x, long y, long o) {
mQuadOffsets[x][y] = o; mQuadOffsets[x][y] = o;
} }
protected: protected:
std::map<long, std::map<long, long> > mQuadOffsets; std::map<long, std::map<long, long> > mQuadOffsets;
long mRootSideLength; ///length in gu of the root quad long mRootSideLength; ///length in gu of the root quad
int mMaxDepth; ///maximum depth assuming root quad depth = 0 int mMaxDepth; ///maximum depth assuming root quad depth = 0
friend class boost::serialization::access; friend class boost::serialization::access;
/** /**
* Saves the data for the max depth, the root side legnth, and the quad offsets * Saves the data for the max depth, the root side legnth, and the quad offsets
*/ */
template<class Archive> template<class Archive>
inline void serialize(Archive& ar, const unsigned int version){ inline void serialize(Archive& ar, const unsigned int version){
ar &mMaxDepth; ar &mMaxDepth;
ar &mRootSideLength; ar &mRootSideLength;
ar &mQuadOffsets; ar &mQuadOffsets;
} }
}; };

@ -1,35 +1,30 @@
/** class TextureSplatter
* Handles the runtime generation of materials
*
*/
class MaterialGenerator
{ {
class TextureSplatter public:
{ inline TextureSplatter(const std::vector<int>& ltex,
public: int texSize,
inline TextureSplatter(const std::vector<int>& ltex, int ltexsize,
int texSize, int border) :
int ltexsize, mLTEX(ltex),
int border) : mTexSize(texSize), //the root of the size of the texture
mLTEX(ltex), mLTEXSize(ltexsize), //the root of the ltex array
mTexSize(texSize), //the root of the size of the texture mBorder(border) { //the size of the border of the ltex
mLTEXSize(ltexsize), //the root of the ltex array mSizeDiff = texSize/(ltexsize-border*2);
mBorder(border) { //the size of the border of the ltex }
mSizeDiff = texSize/(ltexsize-border*2);
}
int getAlphaAtPixel(int x, int y, int tid) { int getAlphaAtPixel(int x, int y, int tid) {
//offset for border //offset for border
x += (mBorder*mSizeDiff); x += (mBorder*mSizeDiff);
y += (mBorder*mSizeDiff); y += (mBorder*mSizeDiff);
if ( getTextureAtPixel(x,y) == tid ) { if ( getTextureAtPixel(x,y) == tid ) {
return 255; return 255;
} else if ( mBorder > 0 ) { //hacky remove fix. perofrmance issues. skips if not ingame gen. } else if ( mBorder > 0 )
{
//hacky remove fix. perofrmance issues. skips if not ingame gen.
float amount = 0; float amount = 0;
for ( int ty = y-1; ty <= y+1; ++ty ) { for ( int ty = y-1; ty <= y+1; ++ty ) {
for ( int tx = x-1; tx <= x+1; ++tx ) { for ( int tx = x-1; tx <= x+1; ++tx ) {
if ( ty < -mBorder*mSizeDiff || if ( ty < -mBorder*mSizeDiff ||
tx < -mBorder*mSizeDiff || tx < -mBorder*mSizeDiff ||
ty >= mTexSize+mBorder*mSizeDiff || ty >= mTexSize+mBorder*mSizeDiff ||
@ -44,35 +39,43 @@ class MaterialGenerator
assert(amount>=0&&amount<=1); assert(amount>=0&&amount<=1);
return amount*255; return amount*255;
} }
return 0; return 0;
}
}
private: private:
int getTextureAtPixel(int x, int y) { int getTextureAtPixel(int x, int y) {
x = (x - x%mSizeDiff)/mSizeDiff; x = (x - x%mSizeDiff)/mSizeDiff;
y = (y - y%mSizeDiff)/mSizeDiff; y = (y - y%mSizeDiff)/mSizeDiff;
//y = floor(float(y)/float(mSizeDiff));
//x = floor(float(x)/float(mSizeDiff));
return mLTEX[(y)*mLTEXSize+(x)]; return mLTEX[(y)*mLTEXSize+(x)];
} }
const std::vector<int>& mLTEX; const std::vector<int>& mLTEX;
const int mTexSize; const int mTexSize;
const int mLTEXSize; const int mLTEXSize;
const int mBorder; const int mBorder;
int mSizeDiff; int mSizeDiff;
}; };
/**
* Handles the runtime generation of materials
*
*/
class MaterialGenerator
{
public: public:
/** /**
* @brief generates a material for a quad using a single texture * @brief generates a material for a quad using a single
* texture. Only used at runtime, not while generating.
*/ */
Ogre::MaterialPtr generateSingleTexture(const std::string& matname, const std::string& texName, std::list<Ogre::ResourcePtr> createdResources) Ogre::MaterialPtr generateSingleTexture
(const std::string& texName,
std::list<Ogre::ResourcePtr> createdResources)
{ {
const std::string matname("MAT" + Ogre::StringConverter::toString(mCount++));
if ( !Ogre::MaterialManager::getSingleton().resourceExists(matname) ) if ( !Ogre::MaterialManager::getSingleton().resourceExists(matname) )
{ {
Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(matname,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(matname,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
@ -87,159 +90,17 @@ public:
return Ogre::MaterialPtr(); return Ogre::MaterialPtr();
} }
/**
* Currently doesn't work
*/
Ogre::MaterialPtr getShaderAlpha(const std::string& materialName, std::vector<int>& ltex, int size, std::list<Ogre::TexturePtr>& generatedAlphas)
{
Ogre::TexturePtr currentAlphaTexture; //ptr to the texture we are currnly writing to
Ogre::uint8* currentAlphaPtr = 0; //pointer to the data
Ogre::HardwarePixelBufferSharedPtr currentPixelBuffer; //pointer to the buffer for ctrling locks/unlicjks
int currentColour = 0; //current colour index. ARGB.
std::vector<std::string> texturesWritten; //a list of "done" textures. Order important
std::vector<std::string> alphasGenerated; //alphas generated
std::set<short> tidDone; //holds the list of done textures
const int rootSideLength = Ogre::Math::Sqrt(ltex.size()); //used for looping
//loop over every splat possition
for ( int y1 = 0; y1 < rootSideLength; y1++ ) {
for ( int x1 = 0; x1 < rootSideLength; x1++ ) {
const short tid = ltex[y1*rootSideLength+x1];
//if already done.
if ( tidDone.find(tid) != tidDone.end()) continue;
//insert it into the done list
tidDone.insert(tid);
//get the textuyre path. If it is default we don't need to do anything, as it
//is done in the first pass. CHANGE? We end up using a whole pass for it??
const std::string tn(mTexturePaths[tid]);
if ( tn == "_land_default.dds" ) continue;
texturesWritten.push_back(tn);
//unqiue alpha name
const std::string alphaName(materialName + "_A_" + tn);
if ( Ogre::TextureManager::getSingleton().resourceExists(alphaName) )
OGRE_EXCEPT(0, "ALPHA Already Exists", ""); //shouldn't happen.
//check if we need to create a new apla
if ( currentColour == 4 || //we only have 4 colours per tex ofc
currentAlphaTexture.isNull() ) { //no texture assigned yet
//unlock old buffer, if we have one
if ( !currentAlphaTexture.isNull() )
currentPixelBuffer->unlock();
//new texture
currentAlphaTexture = Ogre::TextureManager::getSingleton().
createManual(
alphaName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
size,size, //size ofc
1, 0, //depth, mipmaps
Ogre::PF_A8R8G8B8, //4 channesl for 4 splats
Ogre::TU_STATIC_WRITE_ONLY //we only need to write data
);
generatedAlphas.push_back(currentAlphaTexture); //record
alphasGenerated.push_back(alphaName);
currentPixelBuffer = currentAlphaTexture->getBuffer();
currentPixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
const Ogre::PixelBox& pixelBox = currentPixelBuffer->getCurrentLock();
currentAlphaPtr = static_cast<Ogre::uint8*>(pixelBox.data);
//zero out all data
for ( int i = 0; i < size*size*4; i++ )
currentAlphaPtr[i] = 0;
currentColour = 0;
} else {
currentColour++; //increase the colour we are working with by one
}
//for every splat that the texture is splatted,
//check if it is the same as the one in the current splat
for ( int y2 = 0; y2 < rootSideLength; y2++ ) {
for ( int x2 = 0; x2 < rootSideLength; x2++ ) {
if ( ltex[y2*rootSideLength+x2] == tid ) {
//add splat to current alpha map
const int splatSize = size/rootSideLength;
for ( int ys = 0; ys < splatSize ; ys++ ) {
for ( int xs = 0; xs < splatSize; xs++ ) {
//calc position on main texture
const int pxpos = splatSize*x2+xs;
const int pypos = splatSize*y2+ys;
//write splat to buffer
const int index = (pypos*size*4)+(pxpos*4)+currentColour;
currentAlphaPtr[index] = 255;
}
}
}
}
}//for ( int y2 = 0; y2 < rootSideLength; y2++ ){
}
}//for ( int y1 = 0; y1 < rootSideLength; y1++ ){
//check to see if we need to unlock a buff again
if ( !currentAlphaTexture.isNull() )
currentPixelBuffer->unlock();
//sort material
assert(Ogre::MaterialManager::getSingleton().getByName("AlphaSplatTerrain").getPointer());
Ogre::MaterialPtr material = ((Ogre::Material*)Ogre::MaterialManager::getSingleton().getByName("AlphaSplatTerrain").getPointer())->clone(materialName);
Ogre::Pass* pass = material->getTechnique(0)->getPass(0);
//write alphas
if ( alphasGenerated.size() > 0 )
pass->getTextureUnitState(0)->setTextureName(alphasGenerated[0]);
if ( alphasGenerated.size() > 1 )
pass->getTextureUnitState(1)->setTextureName(alphasGenerated[1]);
//write 8 textures
int c = 1;
for ( std::vector<std::string>::iterator itr = texturesWritten.begin();
itr != texturesWritten.end();
++itr ) {
if ( ++c > 8 ) break;
pass->getTextureUnitState(1+c)->setTextureName(*itr);
//ta["Splat" + Ogre::StringConverter::toString(c)] = *itr;
}
//pass->applyTextureAliases(ta, true);
return material;
}
/** /**
* gets the material for the alpha textures * gets the material for the alpha textures
*/ */
Ogre::MaterialPtr getAlphaMat(const std::string& materialName, Ogre::MaterialPtr getAlphaMat(std::vector<int>& ltex,
std::vector<int>& ltex,
int size, int size,
int border, int border,
float scaleDiv, float scaleDiv,
std::list<Ogre::ResourcePtr>& createdResources) std::list<Ogre::ResourcePtr>& createdResources)
{ {
const std::string materialName("MAT" + Ogre::StringConverter::toString(mCount++));
const int sizeDiff = 4; const int sizeDiff = 4;
size *= sizeDiff; size *= sizeDiff;
@ -337,25 +198,6 @@ public:
return material; return material;
} }
std::string generateAlphaMap(const std::string& name,
std::vector<int>& ltex,
int size,
int border,
float scaleDiv,
std::list<Ogre::ResourcePtr>& createdResources)
{
//std::string materialName = "TEX_" + Ogre::StringConverter::toString(cp.x) + "_" + Ogre::StringConverter::toString(cp.y);
if ( Ogre::MaterialManager::getSingleton().resourceExists(name) )
assert(0);
//return materialName;
getAlphaMat(name, ltex, size, border, scaleDiv, createdResources);
//getShaderAlpha(materialName, ltex, size, newTextures);
return name;
}
inline void setTexturePaths( std::map<int, std::string> r) { inline void setTexturePaths( std::map<int, std::string> r) {
mTexturePaths = r; mTexturePaths = r;
} }
@ -364,4 +206,6 @@ private:
* Merged records accross all mods for LTEX data * Merged records accross all mods for LTEX data
*/ */
std::map<int, std::string> mTexturePaths; std::map<int, std::string> mTexturePaths;
unsigned int mCount;
}; };

@ -1,110 +0,0 @@
MeshInterface::MeshInterface(Quad* p, Terrain* t) :
mParentQuad(p),
mTerrain(t),
mMax(0),
mMin(0),
mSplitState(SS_NONE) {
mQuadData = t->getTerrainData()->getData(mParentQuad);
//the mesh is created a zero, so an offset is applied
const Ogre::Vector3 pos(mParentQuad->getPosition().x - mParentQuad->getSideLength()/2,
0,
mParentQuad->getPosition().y - mParentQuad->getSideLength()/2);
mSceneNode = mTerrain->getTerrainSceneNode()->createChildSceneNode(pos);
}
MeshInterface::~MeshInterface() {
for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin();
itr != mTerrainObjects.end();
++itr )
delete *itr;
mSceneNode->removeAndDestroyAllChildren();
mSceneMgr->destroySceneNode(mSceneNode);
mTerrain->getTerrainSceneNode()->detachAllObjects();
mTerrain->_quadDestroyed(mQuadData);
delete mQuadData;
}
void MeshInterface::create() {
//LOG("Creating");
if ( mParentQuad->getDepth() == mTerrain->getMaxDepth() ) {
for ( int y = 0; y < 4; ++y ) {
for ( int x = 0; x < 4; ++x ) {
addNewObject(
Ogre::Vector3(x*16*128, 0, y*16*128), //pos
17, //size
false, //skirts
0.25f, float(x)/4.0f, float(y)/4.0f); //quad seg location
}
}
} else {
addNewObject(Ogre::Vector3(0,0,0), 65);
}
getBounds();
mTerrain->_quadCreated(mQuadData);
}
void MeshInterface::addNewObject(const Ogre::Vector3& pos,
int terrainSize,
bool skirts /*= true*/,
float segmentSize /*= 1*/,
float startX /*= 0*/,
float startY /*= 0*/ ) {
TerrainObjectGroup* to = new TerrainObjectGroup();
to->segment = new QuadSegment(mQuadData, segmentSize, startX, startY);
to->node = mSceneNode->createChildSceneNode(pos);
to->terrain = new TerrainRenderable(mTerrain, to->segment, terrainSize, mParentQuad->getDepth(), skirts);
to->terrain->create(to->node);
mMax = std::max(to->terrain->getMax(), mMax);
mMin = std::max(to->terrain->getMin(), mMin);
mTerrainObjects.push_back(to);
}
void MeshInterface::update(Ogre::Real time) {
const Ogre::Vector3 cpos = mCamera->getDerivedPosition();
Ogre::Vector3 diff(0, 0, 0);
//copy?
Ogre::AxisAlignedBox worldBounds = mBounds;
worldBounds.transformAffine(mSceneNode->_getFullTransform());
diff.makeFloor(cpos - worldBounds.getMinimum() );
diff.makeCeil(cpos - worldBounds.getMaximum() );
const Ogre::Real camDist = diff.squaredLength();
mSplitState = SS_NONE;
if ( camDist < mSplitDistance ) mSplitState = SS_SPLIT;
else if ( camDist > mUnsplitDistance ) mSplitState = SS_UNSPLIT;
for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin();
itr != mTerrainObjects.end();
++itr )
(*itr)->terrain->update(time, camDist, mUnsplitDistance, mMorphDistance);
}
void MeshInterface::getBounds() {
mBounds.setExtents( 0,
mMin,
0,
(65 - 1) * mQuadData->getVertexSeperation(),
mMax,
(65 - 1) * mQuadData->getVertexSeperation());
mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2;
mSplitDistance = pow(mBoundingRadius * 0.5, 2);
mUnsplitDistance = pow(mBoundingRadius * 2.0, 2);
mMorphDistance = pow(mBoundingRadius * 1.5, 2);
}

@ -1,117 +0,0 @@
/**
* Interface between the quad and the terrain renderble classes, to the
* quad it looks like this rendereds a single mesh for the quad. This
* may not be the case.
*
* It also could allow several optomizations (e.g. multiple splits)
*/
class MeshInterface
{
/**
*
* @brief Holds a group of objects and destorys them in the
* destructor. Avoids the needs for 100s of vectors
*/
struct TerrainObjectGroup {
/**
* @brief inits all ptrs at 0
*/
inline TerrainObjectGroup() : segment(0), terrain(0), node(0) {}
/**
* @brief destorys all objects
*/
inline ~TerrainObjectGroup() {
if ( node ) {
node->detachAllObjects();
node->getCreator()->destroySceneNode(node);
}
delete terrain;
delete segment;
}
QuadSegment* segment;
TerrainRenderable* terrain;
Ogre::SceneNode* node;
};
public:
enum SplitState { SS_NONE, SS_SPLIT, SS_UNSPLIT };
MeshInterface(Quad* p, Terrain* t);
~MeshInterface() ;
/**
* @brief creates all required meshes. If it is at the max depth, it creates 16, otherwise just one
*/
void create();
/**
* @brief updates all meshes.
* @remarks the camera distance is calculated here so that all terrain has the correct morph levels etc
*/
void update(Ogre::Real time);
inline SplitState getSplitState() {
return mSplitState;
}
/**
* @brief propergates the just split through all terrain
*/
inline void justSplit() {
for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin();
itr != mTerrainObjects.end();
++itr )
(*itr)->terrain->justSplit();
}
/**
* @brief propergates the just unsplit through all terrain
*/
inline void justUnsplit() {
for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin();
itr != mTerrainObjects.end();
++itr )
(*itr)->terrain->justUnsplit();
}
private:
Quad* mParentQuad;
Terrain* mTerrain;
///Must be a ptr, else it destorys before we are ready
std::vector<TerrainObjectGroup*> mTerrainObjects;
Ogre::SceneNode* mSceneNode;
///use for split distances
Ogre::Real mBoundingRadius;
Ogre::AxisAlignedBox mBounds;
///max and min heights
float mMax, mMin;
Ogre::Real mSplitDistance,mUnsplitDistance,mMorphDistance;
SplitState mSplitState;
QuadData* mQuadData;
/**
* @brief sets the bounds and split radius of the object
*/
void getBounds();
/**
* @brief Adds a new mesh
* @param pos the position in relation to mSceneNode
* @param terrainSize the size of the terrain in verts. Should be n^2+1
* @param skirts true if the terrain should have skirts
* @param segmentSize the size of the segment. So if splitting terrain into 4*4, it should be 0.25
* @param startX, startY the start position of this segment (0 <= startX < 1)
*/
void addNewObject(const Ogre::Vector3& pos, int terrainSize,
bool skirts = true, float segmentSize = 1,
float startX = 0, float startY = 0 );
};

@ -3,23 +3,30 @@
* major improvment would be to store the data as a quad tree. It might * major improvment would be to store the data as a quad tree. It might
* improve lookup times. Then again. Might not * improve lookup times. Then again. Might not
*/ */
class MWHeightmap : public TerrainHeightmap class MWHeightmap
{ {
public: public:
QuadData* getData(Quad* q) /**
* loads the quad data from the disk
*/
QuadData* getData(long x, long y)
{ {
MWQuadData* data = loadQuad(q->getPosition().x,q->getPosition().y); long offset = mIndex.getOffset(x,y);
if ( offset == -1 ) //check we have xy
if ( !data )
assert(0); assert(0);
return data; mDataIFS.seekg(offset);
}
inline bool hasData(Quad* q) { //load the quad from the file
return hasQuad(q->getPosition().x,q->getPosition().y); QuadData* q = new QuadData();
boost::archive::binary_iarchive oa(mDataIFS);
oa >> *q;
return q;
} }
inline bool hasData(long x, long y)
{ return (mIndex.getOffset(x,y) != -1 ); }
inline long getRootSideLength() { inline long getRootSideLength() {
return mIndex.getRootSideLength(); return mIndex.getRootSideLength();
} }
@ -42,9 +49,7 @@ public:
boost::archive::binary_iarchive oa(ifs); boost::archive::binary_iarchive oa(ifs);
oa >> mPalette; oa >> mPalette;
} }
mMaterialGen.setTexturePaths(mPalette.getPalette()); g_materialGen->setTexturePaths(mPalette.getPalette());
mMaterialGenerator = new MWQuadMaterialGen(&mMaterialGen);
mDataIFS.open(std::string(fn + ".data").c_str(), std::ios::binary); mDataIFS.open(std::string(fn + ".data").c_str(), std::ios::binary);
return true; return true;
@ -52,33 +57,10 @@ public:
private: private:
inline long hasQuad(long x, long y) {
return (mIndex.getOffset(x,y) != -1 ) ;
}
/**
* @brief loads the quad data from the disk
*/
MWQuadData* loadQuad(long x, long y)
{
long offset = mIndex.getOffset(x,y);
if ( offset == -1 ) //check we have xy
return 0;
mDataIFS.seekg(offset);
//load the quad from the file
MWQuadData* q = new MWQuadData(this);
boost::archive::binary_iarchive oa(mDataIFS);
oa >> *q;
return q;
}
///ifs for the data file. Opned on load ///ifs for the data file. Opned on load
std::ifstream mDataIFS; std::ifstream mDataIFS;
///holds the offsets of the quads ///holds the offsets of the quads
Index mIndex; Index mIndex;
TexturePalette mPalette; TexturePalette mPalette;
///material generator. gens a ogre::material from quad data
MaterialGenerator mMaterialGen;
}; };

@ -1,84 +0,0 @@
/**
* @todo mergre with matgen class
*/
class MWQuadMaterialGen : public QuadMaterialGenerator
{
public:
MWQuadMaterialGen(MaterialGenerator* mg) : mMatGen(mg), mCount(0){}
Ogre::MaterialPtr getMaterial(QuadData* qd){
return _getMaterial((MWQuadData*)qd);
}
Ogre::MaterialPtr getMaterialSegment(QuadData* qd, QuadSegment* qs){
return _getMaterialSegment((MWQuadData*)qd,qs);
}
private:
Ogre::MaterialPtr _getMaterial(MWQuadData* qd){
assert(qd);
const std::string name("MAT" + Ogre::StringConverter::toString(mCount++));
if ( qd->getTexture().length() ){
return mMatGen->generateSingleTexture(name, qd->getTexture(), qd->getUsedResourcesRef());
}else{
assert(0);
}
return Ogre::MaterialPtr();
}
Ogre::MaterialPtr _getMaterialSegment(MWQuadData* qd, QuadSegment* qs){
const std::string name("MAT" + Ogre::StringConverter::toString(mCount++));
if ( qd->getTexture().length() )
assert(0&&"NOT IMPLEMENTED");
const std::vector<int>& tref = qd->getTextureIndexRef();
const int indexSize = sqrt(tref.size());
const int cellIndexSize = indexSize - 2;
//plus 1 to take into account border
const int xoff = float(cellIndexSize) * qs->getStartX();
const int yoff = float(cellIndexSize) * qs->getStartY();
const int size = float(cellIndexSize) * qs->getSegmentSize();
std::vector<int> ti;
ti.resize((size+2)*(size+2), -1);
for ( int y = 0; y < size+2; ++y ){
for ( int x = 0; x < size+2; ++x ){
ti[(y)*(size+2)+(x)] = tref.at((y+yoff)*(indexSize)+(x+xoff));
}
}
/*
ti.resize((size)*(size));
for ( int y = 1; y < size+1; ++y ){
for ( int x = 1; x < size+1; ++x ){
if ( y+yoff >= indexSize ) assert(0);
if ( x+xoff >= indexSize ) assert(0);
ti.at((y-1)*(size)+(x-1)) = tref.at((y+yoff)*(indexSize)+(x+xoff));
}
}
*/
Ogre::MaterialPtr t = Ogre::MaterialManager::getSingleton().
getByName(mMatGen->generateAlphaMap
(name,
ti,size,
1, 1.0f/size,
qd->getUsedResourcesRef()));
return t;
}
MaterialGenerator* mMatGen;
unsigned int mCount;
};

@ -1,16 +1,25 @@
/** /**
* @brief defines an area of Landscape * defines an area of Landscape
* *
* A quad can either hold a mesh, or 4 other sub quads * A quad can either hold a mesh, or 4 other sub quads The functions
* The functions split and unslip either break the current quad into smaller quads, or * split and unsplit either break the current quad into smaller quads,
* alternatively remove the lower quads and create the terrain mesh on the the current (now the lowest) level * or alternatively remove the lower quads and create the terrain mesh
* on the the current (now the lowest) level
* *
* needUnsplit and needSplit query the state of the meshes to see if it needs spliting or unspliting * needUnsplit and needSplit query the state of the meshes to see if
* it needs spliting or unspliting
* *
*/ */
/* Previously for MeshInterface:
* Interface between the quad and the terrain renderble classes, to the
* quad it looks like this rendereds a single mesh for the quad. This
* may not be the case.
*
* It also could allow several optimizations (e.g. multiple splits)
*/
class Quad class Quad
{ {
public: enum SplitState { SS_NONE, SS_SPLIT, SS_UNSPLIT };
/** /**
* when each quad is split, the children can be one of 4 places, * when each quad is split, the children can be one of 4 places,
@ -19,9 +28,12 @@ public:
* and should always have 4 children. * and should always have 4 children.
*/ */
typedef std::list<TerrainMesh*> MeshList;
public:
// FIXME: There's probably a better way to do this // FIXME: There's probably a better way to do this
enum QuadLocation { QL_NW, QL_NE, QL_SW, QL_SE, QL_ROOT }; enum QuadLocation { QL_NW, QL_NE, QL_SW, QL_SE, QL_ROOT };
/** /**
* @param l the location of the quad * @param l the location of the quad
* @param p the parent quad. Leave 0 if it is root * @param p the parent quad. Leave 0 if it is root
@ -29,9 +41,10 @@ public:
* *
* Constructor mainly sets up the position variables/depth etc * Constructor mainly sets up the position variables/depth etc
*/ */
Quad(QuadLocation l, Quad* p, Terrain* t) Quad(QuadLocation l, Quad* p)
: mParent(p), mTerrain(t), mTerrainMesh(0), mLocation(l) : mParent(p), mLocation(l), mQuadData(NULL)
{ {
TRACE("Quad");
//as mentioned elsewhere, the children should all be null. //as mentioned elsewhere, the children should all be null.
memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN); memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN);
@ -44,46 +57,45 @@ public:
mSideLength = p->getSideLength()/2; mSideLength = p->getSideLength()/2;
//horrible bit of code //horrible bit of code
// FIXME
switch (l) { switch (l) {
case Quad::QL_NE: case Quad::QL_NE:
mPosition.x += mSideLength/2; mPosition.x += mSideLength/2;
mPosition.y += mSideLength/2; mPosition.y += mSideLength/2;
break; break;
case Quad::QL_NW: case Quad::QL_NW:
mPosition.x -= mSideLength/2; mPosition.x -= mSideLength/2;
mPosition.y += mSideLength/2; mPosition.y += mSideLength/2;
break; break;
case Quad::QL_SE: case Quad::QL_SE:
mPosition.x += mSideLength/2; mPosition.x += mSideLength/2;
mPosition.y -= mSideLength/2; mPosition.y -= mSideLength/2;
break; break;
case Quad::QL_SW: case Quad::QL_SW:
mPosition.x -= mSideLength/2; mPosition.x -= mSideLength/2;
mPosition.y -= mSideLength/2; mPosition.y -= mSideLength/2;
break; break;
default: default:
break;//get rid of warning break;
} }
//set after positions have been retrived //set after positions have been retrived
TerrainHeightmap* d = mTerrain->getTerrainData(); mMaxDepth = g_heightMap->getMaxDepth();
mMaxDepth = d->getMaxDepth(); mHasData = g_heightMap->hasData(mPosition.x, mPosition.y);
mHasData = d->hasData(this);
if ( needSplit() ) //need to "semi" build terrain if ( needSplit() ) //need to "semi" build terrain
split(); split();
else if ( mHasData ) else if ( mHasData )
{ {
buildTerrain(); buildTerrain();
getMesh()->justSplit(); justSplit();
} }
} }
else else
{ //assume it is root node, get data and possition { //assume it is root node, get data and position
mDepth = 0; //root mDepth = 0; //root
mSideLength = mTerrain->getTerrainData()->getRootSideLength(); mSideLength = g_heightMap->getRootSideLength();
mPosition = Point2<long>(0,0); //see Quad::getPosition as to why this is always 0 mPosition = Point2<long>(0,0);
mHasData = false; mHasData = false;
@ -100,36 +112,12 @@ public:
*/ */
~Quad() ~Quad()
{ {
TRACE("~Quad");
destroyTerrain(); destroyTerrain();
for (size_t i = 0; i < NUM_CHILDREN; i++) for (size_t i = 0; i < NUM_CHILDREN; i++)
delete mChildren[i]; delete mChildren[i];
} }
void update(Ogre::Real t)
{
if ( needSplit() )
{
split();
return;
}
else if ( needUnsplit() )
{
unsplit();
return;
}
//deal with updating the mesh.
if ( mTerrainMesh )
mTerrainMesh->update(t);
else if ( hasChildren() )
{
for (size_t i = 0; i < NUM_CHILDREN; ++i) {
assert( mChildren[i] );
mChildren[i]->update(t);
}
}
}
/** /**
* @return true if the node needs to be split. * @return true if the node needs to be split.
* *
@ -139,34 +127,26 @@ public:
*/ */
bool needSplit() bool needSplit()
{ {
TRACE("needSplit");
if ( hasChildren() || if ( hasChildren() ||
getDepth() == mMaxDepth || getDepth() == mMaxDepth ||
!hasData() ) !hasData() )
return false; return false;
return ( mTerrainMesh && mTerrainMesh->getSplitState() return ( mQuadData && (mSplitState == SS_SPLIT) );
== MeshInterface::SS_SPLIT );
} }
/**
* The functions preforms work on the quad tree state. There is no
* requirement for this to be called every frame, but it is not
* thread safe due to interacting with OGRE The function checks if a
* split or unsplit is needed. It also calls update on all children
* @param t the time since the last frame
*/
/** /**
* Deletes the landscape, if there is any * Deletes the landscape, if there is any
* Creates children, and either splits them, or creates landscape for them * Creates children, and either splits them, or creates landscape for them
*/ */
void split() void split()
{ {
TRACE("split");
destroyTerrain(); destroyTerrain();
//create a new terrain //create a new terrain
for ( size_t i = 0; i < NUM_CHILDREN; ++i ) for ( size_t i = 0; i < NUM_CHILDREN; ++i )
mChildren[i] = new Quad((QuadLocation)i, this, mTerrain); mChildren[i] = new Quad((QuadLocation)i, this);
assert(!needUnsplit()); assert(!needUnsplit());
} }
@ -176,17 +156,20 @@ public:
*/ */
void unsplit() void unsplit()
{ {
TRACE("unsplit");
//shouldn't unsplit 0 depth //shouldn't unsplit 0 depth
assert(getDepth()); assert(getDepth());
for ( size_t i = 0; i < NUM_CHILDREN; i++ )//{ for ( size_t i = 0; i < NUM_CHILDREN; i++ )
delete mChildren[i]; delete mChildren[i];
memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN); memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN);
if ( mHasData ) { if ( mHasData )
buildTerrain(); {
getMesh()->justUnsplit(); buildTerrain();
} justUnsplit();
}
assert(!needSplit()); assert(!needSplit());
} }
@ -196,17 +179,21 @@ public:
*/ */
bool needUnsplit() bool needUnsplit()
{ {
if ( hasChildren() && getDepth() ) { TRACE("needUnsplit");
for (size_t i=0;i< NUM_CHILDREN;i++) { if ( hasChildren() && getDepth() )
if ( mChildren[i]->hasData() ) { {
if ( !mChildren[i]->hasMesh() ) for (size_t i=0;i< NUM_CHILDREN;i++)
return false; {
else if ( mChildren[i]->getMesh()->getSplitState() != MeshInterface::SS_UNSPLIT) if ( mChildren[i]->hasData() )
return false; {
} if ( !mChildren[i]->hasMesh() )
return false;
else if ( mChildren[i]->getSplitState() != SS_UNSPLIT)
return false;
}
}
return true;
} }
return true;
}
//get depth ensures the root doesn't try and unsplit //get depth ensures the root doesn't try and unsplit
if ( getDepth() && !hasData() ) if ( getDepth() && !hasData() )
@ -221,10 +208,41 @@ public:
*/ */
void buildTerrain() void buildTerrain()
{ {
TRACE("buildTerrain");
assert(!mQuadData);
assert(hasData()); assert(hasData());
assert(getMesh() == NULL); //the terrain sould not exist
mTerrainMesh = new MeshInterface(this, mTerrain); // This was in MeshInterface().
mTerrainMesh->create();
mMax = 0;
mMin = 0;
mSplitState = SS_NONE;
long qx = mPosition.x;
long qy = mPosition.y;
mQuadData = g_heightMap->getData(qx, qy);
//the mesh is created at zero, so an offset is applied
const Ogre::Vector3 pos(qx - mSideLength/2,
0,qy - mSideLength/2);
mSceneNode = g_Terrain->getTerrainSceneNode()->createChildSceneNode(pos);
// This was in create()
if ( mDepth == g_Terrain->getMaxDepth() )
for ( int y = 0; y < 4; ++y )
for ( int x = 0; x < 4; ++x )
{
addNewObject(Ogre::Vector3(x*16*128, 0, y*16*128), //pos
17, //size
false, //skirts
0.25f, float(x)/4.0f, float(y)/4.0f);//quad seg location
}
else
addNewObject(Ogre::Vector3(0,0,0), 65);
getBounds();
} }
/** /**
@ -232,8 +250,25 @@ public:
*/ */
void destroyTerrain() void destroyTerrain()
{ {
delete mTerrainMesh; TRACE("destroyTerrain");
mTerrainMesh = NULL; if(!mQuadData)
return;
// From ~MeshInterface()
for ( MeshList::iterator itr =
mMeshList.begin();
itr != mMeshList.end();
++itr )
delete *itr;
mMeshList.clear();
mSceneNode->removeAndDestroyAllChildren();
mSceneMgr->destroySceneNode(mSceneNode);
g_Terrain->getTerrainSceneNode()->detachAllObjects();
delete mQuadData;
mQuadData = NULL;
} }
/** /**
@ -286,7 +321,7 @@ public:
/** /**
* @return true if their is a terrain mesh alocated * @return true if their is a terrain mesh alocated
*/ */
inline bool hasMesh() const{ return mTerrainMesh != 0; } inline bool hasMesh() const{ return mQuadData; }
/** /**
* @return true if there are any children * @return true if there are any children
@ -294,27 +329,161 @@ public:
inline bool hasChildren() const { return mChildren[0] != 0; } inline bool hasChildren() const { return mChildren[0] != 0; }
/** /**
* @return the mesh. 0 if there is no mesh * @brief checks if the quad has any data (i.e. a mesh avaible for rendering
*/ */
MeshInterface* getMesh() inline bool hasData() const{ return mHasData; }
/**
* @brief updates all meshes.
* @remarks the camera distance is calculated here so that all terrain has the correct morph levels etc
*/
void update(Ogre::Real time)
{ {
if ( mTerrainMesh == 0 ) return 0; TRACE("Quad::update");
return mTerrainMesh; if ( needSplit() )
{
split();
return;
}
else if ( needUnsplit() )
{
unsplit();
return;
}
//deal with updating the mesh.
if ( !mQuadData )
{
// We don't have a mesh
if ( hasChildren() )
{
for (size_t i = 0; i < NUM_CHILDREN; ++i) {
assert( mChildren[i] );
mChildren[i]->update(time);
}
}
return;
}
// We have a mesh. Update it.
const Ogre::Vector3 cpos = mCamera->getDerivedPosition();
Ogre::Vector3 diff(0, 0, 0);
//copy?
Ogre::AxisAlignedBox worldBounds = mBounds;
worldBounds.transformAffine(mSceneNode->_getFullTransform());
diff.makeFloor(cpos - worldBounds.getMinimum() );
diff.makeCeil(cpos - worldBounds.getMaximum() );
const Ogre::Real camDist = diff.squaredLength();
mSplitState = SS_NONE;
if ( camDist < mSplitDistance ) mSplitState = SS_SPLIT;
else if ( camDist > mUnsplitDistance ) mSplitState = SS_UNSPLIT;
for ( MeshList::iterator itr = mMeshList.begin();
itr != mMeshList.end();
++itr )
{
assert(*itr);
(*itr)->update(time, camDist, mUnsplitDistance, mMorphDistance);
}
}
inline SplitState getSplitState() {
return mSplitState;
} }
/** /**
* @brief checks if the quad has any data (i.e. a mesh avaible for rendering * @brief propergates the just split through all terrain
*/ */
inline bool hasData() const{ return mHasData; } inline void justSplit() {
for ( MeshList::iterator itr = mMeshList.begin();
itr != mMeshList.end();
++itr )
(*itr)->justSplit();
}
/**
* @brief propergates the just unsplit through all terrain
*/
inline void justUnsplit() {
for ( MeshList::iterator itr = mMeshList.begin();
itr != mMeshList.end();
++itr )
(*itr)->justUnsplit();
}
private: private:
///Must be a ptr, else it destorys before we are ready
MeshList mMeshList;
Ogre::SceneNode* mSceneNode;
///use for split distances
Ogre::Real mBoundingRadius;
Ogre::AxisAlignedBox mBounds;
///max and min heights
float mMax, mMin;
Ogre::Real mSplitDistance,mUnsplitDistance,mMorphDistance;
SplitState mSplitState;
QuadData* mQuadData;
/**
* @brief sets the bounds and split radius of the object
*/
void getBounds()
{
mBounds.setExtents( 0,
mMin,
0,
(65 - 1) * mQuadData->getVertexSeperation(),
mMax,
(65 - 1) * mQuadData->getVertexSeperation());
mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2;
mSplitDistance = pow(mBoundingRadius * SPLIT_FACTOR, 2);
mUnsplitDistance = pow(mBoundingRadius * UNSPLIT_FACTOR, 2);
mMorphDistance = pow(mBoundingRadius * 1.5, 2);
}
/**
* @brief Adds a new mesh
* @param pos the position in relation to mSceneNode
* @param terrainSize the size of the terrain in verts. Should be n^2+1
* @param skirts true if the terrain should have skirts
* @param segmentSize the size of the segment. So if splitting terrain into 4*4, it should be 0.25
* @param startX, startY the start position of this segment (0 <= startX < 1)
*/
void addNewObject(const Ogre::Vector3& pos, int terrainSize,
bool skirts = true, float segmentSize = 1,
float startX = 0, float startY = 0 )
{
assert(mQuadData);
TerrainMesh *tm = new TerrainMesh(mQuadData, segmentSize,
startX, startY, pos,
terrainSize, mDepth, skirts,
mSceneNode);
mMax = std::max(tm->getMax(), mMax);
mMin = std::max(tm->getMin(), mMin);
mMeshList.push_back(tm);
}
static const size_t NUM_CHILDREN = 4; static const size_t NUM_CHILDREN = 4;
Quad* mParent; /// this is the node above this. 0 if this is root Quad* mParent; /// this is the node above this. 0 if this is root
Quad* mChildren[4]; ///optionaly the children. Should be 0 if not exist Quad* mChildren[NUM_CHILDREN]; ///optionaly the children. Should be
///0 if not exist
Terrain* mTerrain; ///the pointer to the root terrain
MeshInterface* mTerrainMesh; ///the terrain mesh, only used if this is the bottom node
Quad::QuadLocation mLocation; ///the location within the quad (ne, se, nw, sw). See Quad::QuadLocation Quad::QuadLocation mLocation; ///the location within the quad (ne, se, nw, sw). See Quad::QuadLocation
Point2<long> mPosition; ///the center of the mesh. this is a long so can be used as comparison. See Quad::getPosition Point2<long> mPosition; ///the center of the mesh. this is a long so can be used as comparison. See Quad::getPosition
long mSideLength; ///the length in units of one side of the quad. See Quad::getSideLength long mSideLength; ///the length in units of one side of the quad. See Quad::getSideLength
@ -323,5 +492,3 @@ private:
bool mHasData; ///holds if there is terrain data about this quad bool mHasData; ///holds if there is terrain data about this quad
int mMaxDepth; ///the maxmium depth. Cached. This is not valid is mDepth == 0 int mMaxDepth; ///the maxmium depth. Cached. This is not valid is mDepth == 0
}; };
//const size_t Quad::NUM_CHILDREN = 4;

@ -1,76 +1,22 @@
/** /**
* @brief gets a material for the quad data * holds data that is passed to the mesh renderer. heights normals etc
*/
class QuadMaterialGenerator
{
public:
virtual Ogre::MaterialPtr getMaterial(QuadData* qd) = 0;
/**
* @brief can't overload :(
*/
virtual Ogre::MaterialPtr getMaterialSegment(QuadData* qd,QuadSegment* qs) = 0;
private:
/**
* @brief fix for gcc not putting the vtable anywhere unless there is a none inline, none virtual function
* @remarks does absolutly nothing ofc
*/
void _vtablefix();
};
/**
* @brief abstract class used for getting LOD data.
*
* This class enables storing of data in whatever form is wanted
*/
class TerrainHeightmap {
public:
TerrainHeightmap() : mMaterialGenerator(0){
}
virtual ~TerrainHeightmap() {
delete mMaterialGenerator;
}
/**
* @brief called to load some data for the given quad
* @return NULL if the data doesn't exist
*
* The deleting of the memory is handled by TerrainMesh
*/
virtual QuadData* getData(Quad* q) = 0;
/**
* @brief check if any data exists for this level
* @param q the quad that is asking for the data
*/
virtual bool hasData(Quad* q) = 0;
/**
* @brief get the distance from one end of the map to the other
*/
virtual long getRootSideLength() = 0;
virtual int getMaxDepth() = 0;
inline QuadMaterialGenerator* getMaterialGenerator(){
assert(mMaterialGenerator);
return mMaterialGenerator;
}
protected:
QuadMaterialGenerator* mMaterialGenerator;
};
/**
* @brief holds data that is passed to the mesh renderer. heights normals etc
* *
* This needs a rework, as really the mesh renderer should accept just a set of verts * This needs a rework, as really the mesh renderer should accept just
* Normals and indicies to allow us to pass optoizied meshes * a set of verts Normals and indicies to allow us to pass optimized
* meshes
*/ */
class QuadData class QuadData
{ {
public: typedef std::list<Ogre::ResourcePtr> ResourceList;
QuadData(TerrainHeightmap* p) typedef std::list<Ogre::ResourcePtr>::const_iterator ResourceListCItr;
: mHeightmap(p) {}
virtual ~QuadData() {} public:
virtual ~QuadData()
{
const ResourceListCItr end = mResources.end();
for ( ResourceListCItr itr = mResources.begin(); itr != end; ++itr )
(*itr)->getCreator()->remove((*itr)->getHandle());
}
/** /**
* How many vertes wide the qd is. Usally 65. * How many vertes wide the qd is. Usally 65.
@ -93,8 +39,20 @@ public:
return getNormalsRef().at(offset); return getNormalsRef().at(offset);
} }
inline ResourceList& getUsedResourcesRef()
{ return mResources; }
inline void setTexture(const std::string& t)
{ mTexture = t; }
inline std::string& getTexture()
{ return mTexture; }
inline std::vector<int>& getTextureIndexRef()
{ return mTextureIndex; }
/** /**
* @brief should be removed when we get around to developing optomized meshes * @brief should be removed when we get around to developing optimized meshes
*/ */
inline int getVertexSeperation() { inline int getVertexSeperation() {
return mVertexSeperation; return mVertexSeperation;
@ -104,7 +62,7 @@ public:
} }
/** /**
* @brief lazy get function. Avoids creating materail until requested * @brief lazy get function. Avoids creating material until requested
*/ */
inline Ogre::MaterialPtr getMaterial() { inline Ogre::MaterialPtr getMaterial() {
if ( mMaterial.isNull() ) if ( mMaterial.isNull() )
@ -112,10 +70,6 @@ public:
assert(!mMaterial.isNull()); assert(!mMaterial.isNull());
return mMaterial; return mMaterial;
} }
QuadMaterialGenerator* getMaterialGenerator()
{
return mHeightmap->getMaterialGenerator();
}
/** /**
* @brief gets the texture for the above quad * @brief gets the texture for the above quad
@ -130,12 +84,14 @@ public:
mParentTexture = c; mParentTexture = c;
} }
protected: private:
void createMaterial() void createMaterial()
{ {
mMaterial = getMaterialGenerator()->getMaterial(this); assert(mTexture.length());
mMaterial = g_materialGen->generateSingleTexture(mTexture, mResources);
} }
TerrainHeightmap* mHeightmap;
Ogre::MaterialPtr mMaterial; Ogre::MaterialPtr mMaterial;
std::string mParentTexture; std::string mParentTexture;
@ -143,43 +99,11 @@ protected:
int mVertexSeperation; int mVertexSeperation;
std::vector<float> mHeights; std::vector<float> mHeights;
std::vector<char> mNormals; std::vector<char> mNormals;
};
class MWQuadData : public QuadData
{
public:
typedef std::list<Ogre::ResourcePtr> ResourceList;
typedef std::list<Ogre::ResourcePtr>::const_iterator ResourceListCItr;
MWQuadData(TerrainHeightmap* thm) : QuadData(thm) {}
~MWQuadData ()
{
const ResourceListCItr end = mResources.end();
for ( ResourceListCItr itr = mResources.begin(); itr != end; ++itr )
(*itr)->getCreator()->remove((*itr)->getHandle());
}
/**
* @return a ref to the list of used resourcs
*/
inline ResourceList& getUsedResourcesRef()
{ return mResources; }
inline void setTexture(const std::string& t)
{ mTexture = t; }
inline std::string& getTexture()
{ return mTexture; }
inline std::vector<int>& getTextureIndexRef()
{ return mTextureIndex; }
private:
///Holds the resources used by the quad ///Holds the resources used by the quad
ResourceList mResources; ResourceList mResources;
std::vector<int> mTextureIndex; ///holds index that corespond the the palette std::vector<int> mTextureIndex; ///holds index that correspond to the palette
std::string mTexture; ///The land texture. Mutally exclusive with the above std::string mTexture; ///The land texture. Mutally exclusive with the above
friend class boost::serialization::access; friend class boost::serialization::access;
@ -200,4 +124,4 @@ private:
} }
}; };
BOOST_CLASS_TRACKING(MWQuadData, boost::serialization::track_never); BOOST_CLASS_TRACKING(QuadData, boost::serialization::track_never);

@ -1,145 +0,0 @@
/* Represents a segment of a quad. It can also represent a large segment
*
* this is needed for experimenting with splitting the lowest
* quad level into small segments for better culling. Due to the
* intented design of using optomized meshes, this should not work on
* optomized meshes, as there is no easy way to segment the data
*
* This uses floating point numbers to define the areas of the
* quads. There may be issues due to accuracy if we go to small but it
* should be caught in debug mode
*/
#define QSDEBUG
class QuadSegment
{
public:
/**
* @param qd the parent quad data
* @param size the proportion of the total size that the segment is. Must be greater than 0, but less than or equal to 1
* @param x,y the start positions of the segment
*/
QuadSegment(QuadData* qd, float size = 1, float x = 0, float y = 0)
: mQuadData(qd),
mSegmentSize(size),
mX(x),
mY(y)
{
assert(qd);
assert(size>0&&size<=1);
assert(mY>=0&&mY<=1);
assert(mX>=0&&mY<=1);
#ifdef QSDEBUG
{
//check sizes
const float qw = mQuadData->getVertexWidth()-1;
const float fsw = qw*size;
const int isw = (int)fsw;
assert(fsw==isw);
}
#endif
//precalc offsets, as getVertex/getNormal get called a lot (1000s of times)
computeOffsets();
}
/**
* Gets how many vertexes wide this quad segment is. Should always be 2^n+1
* @return the vertex width of this quad segment
*/
int getVertexWidth()
{
return (mQuadData->getVertexWidth()-1)*mSegmentSize+1;
}
/**
* @brief gets a vertex assuming that x = 0, y = 0 addresses the start of the quad
*/
float getVertex(int x, int y)
{
#ifdef QSDEBUG
{
const int vw = getVertexWidth();
assert(x<vw);
}
#endif
return mQuadData->getVertex((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x));
}
float getNormal(int x, int y, int z)
{
assert(z>=0&&z<3);
return mQuadData->getNormal(((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x))*3+z);
}
/**
* @brief lazy function for getting a material
*/
Ogre::MaterialPtr getMaterial()
{
if ( mMaterial.isNull() )
createMaterial();
assert(!mMaterial.isNull());
return mMaterial;
}
void createMaterial()
{
assert(mSegmentSize>0);
if ( mSegmentSize == 1 ) //we can use the top level material
mMaterial = mQuadData->getMaterial();
else //generate a material spesificly for this
mMaterial = mQuadData->getMaterialGenerator()->
getMaterialSegment(mQuadData,this);
assert(!mMaterial.isNull());
}
inline bool hasParentTexture() const{
return mQuadData->hasParentTexture();
}
inline const std::string& getParentTexture() const{
return mQuadData->getParentTexture();
}
inline int getVertexSeperation(){
return mQuadData->getVertexSeperation();
}
inline float getSegmentSize(){
return mSegmentSize;
}
inline float getStartX(){
return mX;
}
inline float getStartY(){
return mY;
}
private:
int computeOffset(float x)
{
#ifdef QSDEBUG
{
//check it is a valid position
const int start = (mQuadData->getVertexWidth()-1)*x;
const int vw = getVertexWidth()-1;
assert(vw>0);
assert((start%vw)==0);
}
#endif
return float((mQuadData->getVertexWidth()-1))*x;
}
void computeOffsets()
{
mXOffset = computeOffset(mX);
mYOffset = computeOffset(mY);
}
QuadData* mQuadData;
float mSegmentSize;
float mX, mY;
Ogre::MaterialPtr mMaterial;
int mXOffset, mYOffset;
};

@ -43,13 +43,27 @@ const int LAND_NUM_VERTS = LAND_VERT_WIDTH*LAND_VERT_WIDTH;
const int LAND_LTEX_WIDTH = 16; const int LAND_LTEX_WIDTH = 16;
const int LAND_NUM_LTEX = LAND_LTEX_WIDTH*LAND_LTEX_WIDTH; const int LAND_NUM_LTEX = LAND_LTEX_WIDTH*LAND_LTEX_WIDTH;
// Multiplied with the size of the quad. If these are too close, a
// quad might end up splitting/unsplitting continuously, since the
// quad size changes when we split.
const float SPLIT_FACTOR = 0.5;
const float UNSPLIT_FACTOR = 2.0;
//stops it crashing, now it leaks. //stops it crashing, now it leaks.
#define ENABLED_CRASHING 0 #define ENABLED_CRASHING 0
class Quad; class Quad;
class QuadData; class QuadData;
class QuadSegment;
class Terrain; class Terrain;
class MaterialGenerator;
class MWHeightmap;
MWHeightmap *g_heightMap;
MaterialGenerator *g_materialGen;
Terrain *g_Terrain;
#undef TRACE
#define TRACE(x)
// Prerequisites // Prerequisites
#include <vector> #include <vector>
@ -66,33 +80,29 @@ class Terrain;
#include <boost/serialization/map.hpp> #include <boost/serialization/map.hpp>
// For generation // For generation
#include "cpp_materialgen.cpp"
#include "cpp_esm.cpp" #include "cpp_esm.cpp"
#include "cpp_landdata.cpp" #include "cpp_landdata.cpp"
#include "cpp_quaddata.cpp" #include "cpp_quaddata.cpp"
#include "cpp_point2.cpp"
#include "cpp_materialgen.cpp"
#include "cpp_index.cpp" #include "cpp_index.cpp"
#include "cpp_palette.cpp" #include "cpp_palette.cpp"
#include "cpp_point2.cpp"
#include "cpp_generator.cpp" #include "cpp_generator.cpp"
// For rendering // For rendering
#include "cpp_quadsegment.cpp"
#include "cpp_baseland.cpp" #include "cpp_baseland.cpp"
#include "cpp_mwquadmatgen.cpp" #include "cpp_mwheightmap.cpp"
// These depend on each other, so our usual hackery won't work. We // These depend on each other, so our usual hackery won't work. We
// need the header files first. // need the header files first.
#include "cpp_terrainmesh.h" #include "cpp_terrainmesh.h"
#include "cpp_meshinterface.h"
#include "cpp_terraincls.h" #include "cpp_terraincls.h"
#include "cpp_quad.cpp" #include "cpp_quad.cpp"
#include "cpp_terraincls.cpp" #include "cpp_terraincls.cpp"
#include "cpp_terrainmesh.cpp" #include "cpp_terrainmesh.cpp"
#include "cpp_meshinterface.cpp"
#include "cpp_mwheightmap.cpp"
#include "cpp_framelistener.cpp" #include "cpp_framelistener.cpp"
TerrainFrameListener terrainListener; TerrainFrameListener terrainListener;
@ -102,6 +112,9 @@ extern "C" void d_superman();
// Set up the rendering system // Set up the rendering system
extern "C" void terr_setupRendering() extern "C" void terr_setupRendering()
{ {
if(!g_materialGen)
g_materialGen = new MaterialGenerator;
// Add the terrain directory // Add the terrain directory
ResourceGroupManager::getSingleton(). ResourceGroupManager::getSingleton().
addResourceLocation(TEXTURE_OUTPUT, "FileSystem", "General"); addResourceLocation(TEXTURE_OUTPUT, "FileSystem", "General");
@ -116,7 +129,10 @@ extern "C" void terr_setupRendering()
// Generate all cached data. // Generate all cached data.
extern "C" void terr_genData() extern "C" void terr_genData()
{ {
if(!g_materialGen)
g_materialGen = new MaterialGenerator;
Ogre::Root::getSingleton().renderOneFrame(); Ogre::Root::getSingleton().renderOneFrame();
Generator mhm(TERRAIN_OUTPUT); Generator mhm(TERRAIN_OUTPUT);

@ -1,10 +1,6 @@
Terrain::Terrain(TerrainHeightmap* d, Terrain::Terrain(Ogre::SceneNode* r)
Ogre::SceneNode* r) : mTerrainSceneNode(r),
: mTerrainData(d),
mTerrainSceneNode(r),
mQuadRoot(0), mQuadRoot(0),
mQuadCreateFunction(0),
mQuadDestroyFunction(0),
mMorphingEnabled(true), mMorphingEnabled(true),
mTextureFadingEnabled(true), mTextureFadingEnabled(true),
mBaseLand(r) mBaseLand(r)
@ -17,7 +13,7 @@ Terrain::~Terrain(){
} }
//---------------------------------------------- //----------------------------------------------
void Terrain::create(){ void Terrain::create(){
mQuadRoot = new Quad(Quad::QL_ROOT, 0, this); //cleaned in Terrain::~Terrain mQuadRoot = new Quad(Quad::QL_ROOT, 0);
} }
//---------------------------------------------- //----------------------------------------------
void Terrain::update(Ogre::Real t){ void Terrain::update(Ogre::Real t){
@ -26,20 +22,14 @@ void Terrain::update(Ogre::Real t){
mBaseLand.update(); mBaseLand.update();
} }
//---------------------------------------------- //----------------------------------------------
void Terrain::_quadCreated(QuadData* qd){
if ( mQuadCreateFunction ) (*mQuadCreateFunction)(qd);
}
//----------------------------------------------
void Terrain::_quadDestroyed(QuadData* qd){
if ( mQuadDestroyFunction ) (*mQuadDestroyFunction)(qd);
}
//----------------------------------------------
int Terrain::getMaxDepth(){ int Terrain::getMaxDepth(){
return mTerrainData->getMaxDepth(); return g_heightMap->getMaxDepth();
} }
/*
//---------------------------------------------- //----------------------------------------------
void Terrain::reload(){ void Terrain::reload(){
delete mQuadRoot; delete mQuadRoot;
mQuadRoot = new Quad(Quad::QL_ROOT, 0, this); mQuadRoot = new Quad(Quad::QL_ROOT, 0);
} }
//---------------------------------------------- //----------------------------------------------
*/

@ -11,7 +11,7 @@ public:
* The terrain is create in the constructor. * The terrain is create in the constructor.
* @todo change quad root creation to create/destroy funcs? * @todo change quad root creation to create/destroy funcs?
*/ */
explicit Terrain(TerrainHeightmap* d, Ogre::SceneNode* r); explicit Terrain(Ogre::SceneNode* r);
/** /**
* @brief deletes the quad tree * @brief deletes the quad tree
*/ */
@ -29,7 +29,7 @@ public:
* @remarks this is very slow, as it drops all created alpha maps and meshes. * @remarks this is very slow, as it drops all created alpha maps and meshes.
* @todo check this works * @todo check this works
*/ */
void reload(); //void reload();
/** /**
* @brief sets the scene node that all of the terrain nodes are based off * @brief sets the scene node that all of the terrain nodes are based off
@ -51,37 +51,30 @@ public:
*/ */
void update(Ogre::Real t); void update(Ogre::Real t);
/**
* @return the heightmap data
*/
inline TerrainHeightmap* getTerrainData(){ return mTerrainData; }
/** /**
* @brief handles the actions to take on the creation of a terrain mesh * @brief handles the actions to take on the creation of a terrain mesh
* @param qd the quad data. It is valid until the same variable is passed to _quadDestroyed * @param qd the quad data. It is valid until the same variable is passed to _quadDestroyed
*/
void _quadCreated(QuadData* qd); void _quadCreated(QuadData* qd);
/** /**
* @brief The quad as defined by the quad data qd has been destroyed * @brief The quad as defined by the quad data qd has been destroyed
* @param qd the quad that has been destroyed. This is only valid for this function. Is is deleted just after * @param qd the quad that has been destroyed. This is only valid for this function. Is is deleted just after
*/
void _quadDestroyed(QuadData* qd); void _quadDestroyed(QuadData* qd);
/** /**
* @brief sets the function to be used in the callback when a quad is created * @brief sets the function to be used in the callback when a quad is created
* @param f the function to use. Set to null to disable callbacks * @param f the function to use. Set to null to disable callbacks
*/
inline void setQuadCreateFunction(void (*f)(QuadData*)){ inline void setQuadCreateFunction(void (*f)(QuadData*)){
mQuadCreateFunction = f; mQuadCreateFunction = f;
} }
/** /**
* @brief sets the function to be used in the callback when a quad is destroyed * @brief sets the function to be used in the callback when a quad is destroyed
* @param f the function to use. Set to null to disable callbacks * @param f the function to use. Set to null to disable callbacks
*/
inline void setQuadDestroyFunction(void (*f)(QuadData*)){ inline void setQuadDestroyFunction(void (*f)(QuadData*)){
mQuadDestroyFunction = f; mQuadDestroyFunction = f;
} }
*/
/** /**
* @brief time in seconds to morph to full detail after an unsplit. * @brief time in seconds to morph to full detail after an unsplit.
@ -131,8 +124,6 @@ public:
return mTextureFadingEnabled; return mTextureFadingEnabled;
} }
protected: protected:
TerrainHeightmap* mTerrainData;
/// the scenenode that every other node is decended from. This /// the scenenode that every other node is decended from. This
/// should be surplied by the user /// should be surplied by the user
Ogre::SceneNode* mTerrainSceneNode; Ogre::SceneNode* mTerrainSceneNode;
@ -140,10 +131,12 @@ protected:
///the root node for all the quads. ///the root node for all the quads.
Quad* mQuadRoot; Quad* mQuadRoot;
/*
///quad callback function ///quad callback function
void (*mQuadCreateFunction)(QuadData*); void (*mQuadCreateFunction)(QuadData*);
///quad callback function ///quad callback function
void (*mQuadDestroyFunction)(QuadData*); void (*mQuadDestroyFunction)(QuadData*);
*/
bool mMorphingEnabled; bool mMorphingEnabled;
bool mTextureFadingEnabled; bool mTextureFadingEnabled;

@ -1,359 +1,376 @@
//---------------------------------------------- TerrainMesh::TerrainMesh(QuadData* qd, float segSize, float startX, float startY,
TerrainRenderable::TerrainRenderable(Terrain* t, QuadSegment* qs,int width, int depth, bool skirts) : const Ogre::Vector3 &pos,
Ogre::Renderable(), int width, int depth, bool skirts,
Ogre::MovableObject(), Ogre::SceneNode *parent)
mWidth(width), : Ogre::Renderable(),
mUseSkirts(skirts), Ogre::MovableObject(),
mBuilt(false), mWidth(width),
mDepth(depth), mUseSkirts(skirts),
mSegment(qs), mBuilt(false),
mTerrain(t), mDepth(depth),
mVertexes(0), mVertexes(0),
mIndices(0), mIndices(0),
mLODMorphFactor(0), mLODMorphFactor(0),
mTextureFadeFactor(0), mTextureFadeFactor(0),
mMin(30000), mMin(30000),
mMax(-30000), mMax(-30000),
mExtraMorphAmount(0), mExtraMorphAmount(0),
mHasFadePass(false) {} mHasFadePass(false),
//---------------------------------------------- mQuadData(qd),
TerrainRenderable::~TerrainRenderable() { mSegmentSize(segSize),
destroy(); mX(startX),
} mY(startY)
//---------------------------------------------- {
void TerrainRenderable::create(Ogre::SceneNode* sn) { // From QuadSegment()
using namespace Ogre; assert(qd);
assert(segSize>0&&segSize<=1);
if ( mBuilt ) return; assert(mY>=0&&mY<=1);
assert(mX>=0&&mY<=1);
createVertexBuffer();
calculateVetexValues(); #ifdef QSDEBUG
calculateIndexValues(); {
setBounds(); //check sizes
const float qw = mQuadData->getVertexWidth()-1;
sn->attachObject(this); const float fsw = qw*segSize;
const int isw = (int)fsw;
mMaterial = mSegment->getMaterial(); assert(fsw==isw);
}
#endif
if ( mTerrain->isMorhpingEnabled() && mDepth != mTerrain->getMaxDepth() ) { //precalc offsets, as getVertex/getNormal get called a lot (1000s of times)
Ogre::Technique* tech = getMaterial()->getTechnique(0); computeOffsets();
for ( size_t i = 0; i < tech->getNumPasses(); ++i ) {
assert(mTerrain->isMorhpingEnabled()); // From Quad
tech->getPass(i)->setVertexProgram(MORPH_VERTEX_PROGRAM); node = parent->createChildSceneNode(pos);
}
// From create()
createVertexBuffer();
calculateVetexValues();
calculateIndexValues();
setBounds();
node->attachObject(this);
createMaterial();
if ( g_Terrain->isMorhpingEnabled() && mDepth != g_Terrain->getMaxDepth() ) {
Ogre::Technique* tech = getMaterial()->getTechnique(0);
for ( size_t i = 0; i < tech->getNumPasses(); ++i ) {
assert(g_Terrain->isMorhpingEnabled());
tech->getPass(i)->setVertexProgram(MORPH_VERTEX_PROGRAM);
} }
}
if ( mTerrain->isMorhpingEnabled() ) if ( g_Terrain->isMorhpingEnabled() )
calculateDeltaValues(); calculateDeltaValues();
mBuilt = true; mBuilt = true;
} }
//----------------------------------------------
void TerrainRenderable::destroy() {
if ( !mBuilt ) return;
//deleting null values is fine iirc void TerrainMesh::destroy() {
delete mIndices; if ( !mBuilt ) return;
//deleting null values is fine iirc
delete mIndices;
# if ENABLED_CRASHING == 1 # if ENABLED_CRASHING == 1
{ {
delete mVertexes; delete mVertexes;
} }
# else # else
{ {
if ( mDepth != mTerrain->getMaxDepth() ){ if ( mDepth != g_Terrain->getMaxDepth() ){
delete mVertexes; delete mVertexes;
}
} }
}
# endif # endif
mBuilt = false; mBuilt = false;
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist) { void TerrainMesh::update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist)
//if ( USE_MORPH ){ {
TRACE("TerrainMesh::update");
//as aprocesh mUnsplitDistance, lower detail //if ( USE_MORPH ){
if ( camDist > morphDist && mDepth > 1 ) {
mLODMorphFactor = 1 - (usplitDist - camDist)/(usplitDist-morphDist); //as aprocesh mUnsplitDistance, lower detail
} else if ( camDist > morphDist && mDepth > 1 ) {
mLODMorphFactor = 0; mLODMorphFactor = 1 - (usplitDist - camDist)/(usplitDist-morphDist);
mTextureFadeFactor = mLODMorphFactor; } else
mLODMorphFactor = 0;
mTextureFadeFactor = mLODMorphFactor;
//on an split, it sets the extra morph amount to 1, we then ensure this ends up at 0... slowly
if ( mExtraMorphAmount > 0 ) {
mLODMorphFactor += mExtraMorphAmount; //on an split, it sets the extra morph amount to 1, we then ensure this ends up at 0... slowly
mExtraMorphAmount -= (time/mTerrain->getMorphSpeed()); //decrease slowly if ( mExtraMorphAmount > 0 ) {
} mLODMorphFactor += mExtraMorphAmount;
if ( mExtraFadeAmount > 0 ) { mExtraMorphAmount -= (time/g_Terrain->getMorphSpeed()); //decrease slowly
mTextureFadeFactor += mExtraFadeAmount; }
mExtraFadeAmount -= (time/mTerrain->getTextureFadeSpeed()); if ( mExtraFadeAmount > 0 ) {
} mTextureFadeFactor += mExtraFadeAmount;
mExtraFadeAmount -= (time/g_Terrain->getTextureFadeSpeed());
//Ensure within valid bounds }
if ( mLODMorphFactor > 1 )
mLODMorphFactor = 1; //Ensure within valid bounds
else if ( mLODMorphFactor < 0 ) if ( mLODMorphFactor > 1 )
mLODMorphFactor = 0; mLODMorphFactor = 1;
else if ( mLODMorphFactor < 0 )
if ( mTextureFadeFactor > 1 ) mLODMorphFactor = 0;
mTextureFadeFactor = 1;
else if ( mTextureFadeFactor < 0 )
mTextureFadeFactor = 0;
//} if ( mTextureFadeFactor > 1 )
mTextureFadeFactor = 1;
else if ( mTextureFadeFactor < 0 )
mTextureFadeFactor = 0;
//}
//remove pass. Keep outside in case terrain fading is removed while it is active //remove pass. Keep outside in case terrain fading is removed while it is active
if ( mHasFadePass && mTextureFadeFactor == 0 ) { if ( mHasFadePass && mTextureFadeFactor == 0 ) {
removeFadePass(); removeFadePass();
} else if ( mTerrain->isTextureFadingEnabled() && } else if ( g_Terrain->isTextureFadingEnabled() &&
!mHasFadePass && !mHasFadePass &&
mTextureFadeFactor > 0 && mTextureFadeFactor > 0 &&
mSegment->hasParentTexture() ) { hasParentTexture() ) {
addFadePass(); addFadePass();
} }
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::addFadePass() { void TerrainMesh::addFadePass() {
assert(mHasFadePass==false); assert(mHasFadePass==false);
if ( mDepth == mTerrain->getMaxDepth() ) return; if ( mDepth == g_Terrain->getMaxDepth() ) return;
mHasFadePass = true; mHasFadePass = true;
Ogre::MaterialPtr mat = getMaterial(); Ogre::MaterialPtr mat = getMaterial();
Ogre::Pass* newPass = mat->getTechnique(0)->createPass(); Ogre::Pass* newPass = mat->getTechnique(0)->createPass();
newPass->setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA); newPass->setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA);
//set fragment program //set fragment program
assert(mTerrain->isTextureFadingEnabled()); assert(g_Terrain->isTextureFadingEnabled());
newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM); newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM);
if ( mTerrain->isMorhpingEnabled() && mDepth != mTerrain->getMaxDepth() ) { if ( g_Terrain->isMorhpingEnabled() && mDepth != g_Terrain->getMaxDepth() ) {
assert(mTerrain->isMorhpingEnabled()); assert(g_Terrain->isMorhpingEnabled());
newPass->setVertexProgram(MORPH_VERTEX_PROGRAM); newPass->setVertexProgram(MORPH_VERTEX_PROGRAM);
} }
//set texture to be used //set texture to be used
newPass->createTextureUnitState(mSegment->getParentTexture(), 1); newPass->createTextureUnitState(getParentTexture(), 1);
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::removeFadePass() { void TerrainMesh::removeFadePass() {
assert(mHasFadePass==true); assert(mHasFadePass==true);
mHasFadePass = false; mHasFadePass = false;
Ogre::MaterialPtr mat = getMaterial(); Ogre::MaterialPtr mat = getMaterial();
Ogre::Technique* tech = mat->getTechnique(0); Ogre::Technique* tech = mat->getTechnique(0);
tech->removePass(tech->getNumPasses()-1); tech->removePass(tech->getNumPasses()-1);
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::justSplit() { void TerrainMesh::justSplit() {
mExtraMorphAmount = 1; mExtraMorphAmount = 1;
mLODMorphFactor = 1; mLODMorphFactor = 1;
mTextureFadeFactor = 1; mTextureFadeFactor = 1;
mExtraFadeAmount = 1; mExtraFadeAmount = 1;
if ( mTerrain->isTextureFadingEnabled() && mSegment->hasParentTexture() ) if ( g_Terrain->isTextureFadingEnabled() && hasParentTexture() )
addFadePass(); addFadePass();
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::_updateCustomGpuParameter( void TerrainMesh::_updateCustomGpuParameter(
const GpuProgramParameters::AutoConstantEntry& constantEntry, const GpuProgramParameters::AutoConstantEntry& constantEntry,
GpuProgramParameters* params) const { GpuProgramParameters* params) const {
using namespace Ogre; using namespace Ogre;
if (constantEntry.data == MORPH_CUSTOM_PARAM_ID) if (constantEntry.data == MORPH_CUSTOM_PARAM_ID)
params->_writeRawConstant(constantEntry.physicalIndex, mLODMorphFactor); params->_writeRawConstant(constantEntry.physicalIndex, mLODMorphFactor);
else if ( constantEntry.data == FADE_CUSTOM_PARAM_ID ) else if ( constantEntry.data == FADE_CUSTOM_PARAM_ID )
params->_writeRawConstant(constantEntry.physicalIndex, mTextureFadeFactor); params->_writeRawConstant(constantEntry.physicalIndex, mTextureFadeFactor);
else else
Renderable::_updateCustomGpuParameter(constantEntry, params); Renderable::_updateCustomGpuParameter(constantEntry, params);
} }
//---------------------------------------------- //----------------------------------------------
float TerrainRenderable::getVertexHeight(int x, int y) { float TerrainMesh::getVertexHeight(int x, int y) {
return mSegment->getVertex(x,y); return getVertex(x,y);
} }
//---------------------------------------------- //----------------------------------------------
Ogre::Vector3 TerrainRenderable::getVertexPosition(int x, int y) { Ogre::Vector3 TerrainMesh::getVertexPosition(int x, int y) {
return Ogre::Vector3(x*mSegment->getVertexSeperation(), getVertexHeight(x,y) , y*mSegment->getVertexSeperation()); return Ogre::Vector3(x*getVertexSeperation(), getVertexHeight(x,y) , y*getVertexSeperation());
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::calculateVetexValues() { void TerrainMesh::calculateVetexValues() {
using namespace Ogre; using namespace Ogre;
//get the texture offsets for the higher uv //get the texture offsets for the higher uv
float xUVOffset = 0; float xUVOffset = 0;
float yUVOffset = 0; float yUVOffset = 0;
if ( mTerrain->isTextureFadingEnabled() ) { if ( g_Terrain->isTextureFadingEnabled() ) {
assert(0); assert(0);
}
/*
switch (mInterface->getLocation()) {
case Quad::QL_NW :
yUVOffset = 32.0f/64.0f;
break;
case Quad::QL_NE:
yUVOffset = 32.0f/64.0f;
xUVOffset = 32.0f/64.0f;
break;
case Quad::QL_SE:
xUVOffset = 32.0f/64.0f;
break;
default:
break;
} }
/* */
switch (mInterface->getLocation()) {
case Quad::QL_NW :
yUVOffset = 32.0f/64.0f;
break;
case Quad::QL_NE:
yUVOffset = 32.0f/64.0f;
xUVOffset = 32.0f/64.0f;
break;
case Quad::QL_SE:
xUVOffset = 32.0f/64.0f;
break;
default:
break;
}
*/
int start = 0; int start = 0;
int end = mWidth; int end = mWidth;
if ( mUseSkirts ) { if ( mUseSkirts ) {
--start; --start;
++end; ++end;
} }
float* verts = static_cast<float*>(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); float* verts = static_cast<float*>(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD));
for ( int y = start; y < end; y++ ) { for ( int y = start; y < end; y++ ) {
for ( int x = start; x < end; x++ ) { for ( int x = start; x < end; x++ ) {
//the skirts //the skirts
if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) { if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) {
if ( x < 0 ) *verts++ = 0; if ( x < 0 ) *verts++ = 0;
else if ( x > (mWidth-1) ) *verts++ = (mWidth-1)*mSegment->getVertexSeperation(); else if ( x > (mWidth-1) ) *verts++ = (mWidth-1)*getVertexSeperation();
else *verts++ = x*mSegment->getVertexSeperation(); else *verts++ = x*getVertexSeperation();
*verts++ = -4096; //2048 below base sea floor height *verts++ = -4096; //2048 below base sea floor height
if ( y < 0 ) *verts++ = 0; if ( y < 0 ) *verts++ = 0;
else if ( y > (mWidth-1) ) *verts++ = (mWidth-1)*mSegment->getVertexSeperation(); else if ( y > (mWidth-1) ) *verts++ = (mWidth-1)*getVertexSeperation();
else *verts++ = y*mSegment->getVertexSeperation(); else *verts++ = y*getVertexSeperation();
for ( Ogre::uint i = 0; i < 3; i++ ) for ( Ogre::uint i = 0; i < 3; i++ )
*verts++ = 0; *verts++ = 0;
float u = (float)(x) / (mWidth-1); float u = (float)(x) / (mWidth-1);
float v = (float)(y) / (mWidth-1); float v = (float)(y) / (mWidth-1);
//clamped, so shouldn't matter if > 1 //clamped, so shouldn't matter if > 1
*verts++ = u; *verts++ = u;
*verts++ = v; *verts++ = v;
if ( mTerrain->isTextureFadingEnabled() ) { if ( g_Terrain->isTextureFadingEnabled() ) {
*verts++ = u; *verts++ = u;
*verts++ = v; *verts++ = v;
} }
} else { } else {
assert(y*mWidth+x>=0&&y*mWidth+x<mWidth*mWidth); assert(y*mWidth+x>=0&&y*mWidth+x<mWidth*mWidth);
//verts //verts
*verts++ = x*mSegment->getVertexSeperation(); *verts++ = x*getVertexSeperation();
*verts++ = getVertexHeight(x,y); *verts++ = getVertexHeight(x,y);
*verts++ = y*mSegment->getVertexSeperation(); *verts++ = y*getVertexSeperation();
mMax = std::max(mMax, getVertexHeight(x,y)); mMax = std::max(mMax, getVertexHeight(x,y));
mMin = std::min(mMin, getVertexHeight(x,y)); mMin = std::min(mMin, getVertexHeight(x,y));
//normals //normals
for ( Ogre::uint i = 0; i < 3; i++ ) for ( Ogre::uint i = 0; i < 3; i++ )
*verts++ = mSegment->getNormal(x,y,i); *verts++ = getNormal(x,y,i);
//*verts++ = mInterface->getNormal((y*mWidth+x)*3+i); //*verts++ = mInterface->getNormal((y*mWidth+x)*3+i);
const float u = (float)(x) / (mWidth-1); const float u = (float)(x) / (mWidth-1);
const float v = (float)(y) / (mWidth-1); const float v = (float)(y) / (mWidth-1);
assert(u>=0&&v>=0); assert(u>=0&&v>=0);
assert(u<=1&&v<=1); assert(u<=1&&v<=1);
*verts++ = u; *verts++ = u;
*verts++ = v; *verts++ = v;
if ( mTerrain->isTextureFadingEnabled() ) { if ( g_Terrain->isTextureFadingEnabled() ) {
*verts++ = u/2.0f + xUVOffset; *verts++ = u/2.0f + xUVOffset;
*verts++ = v/2.0f + yUVOffset; *verts++ = v/2.0f + yUVOffset;
}
}
} }
}
} }
mMainBuffer->unlock(); }
mMainBuffer->unlock();
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::setBounds() { void TerrainMesh::setBounds() {
mBounds.setExtents(0,mMin,0, mBounds.setExtents(0,mMin,0,
(mWidth - 1) * mSegment->getVertexSeperation(), (mWidth - 1) * getVertexSeperation(),
mMax, mMax,
(mWidth - 1) * mSegment->getVertexSeperation()); (mWidth - 1) * getVertexSeperation());
mCenter = Ogre::Vector3( ( (mWidth - 1) * mSegment->getVertexSeperation() ) / 2, mCenter = Ogre::Vector3( ( (mWidth - 1) * getVertexSeperation() ) / 2,
( mMin + mMax ) / 2, ( mMin + mMax ) / 2,
( (mWidth - 1) * mSegment->getVertexSeperation() ) / 2); ( (mWidth - 1) * getVertexSeperation() ) / 2);
mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2;
} }
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::createVertexBuffer() { void TerrainMesh::createVertexBuffer() {
using namespace Ogre; using namespace Ogre;
size_t vw = mWidth; size_t vw = mWidth;
if ( mUseSkirts ) vw += 2; if ( mUseSkirts ) vw += 2;
mVertexes = new VertexData(); mVertexes = new VertexData();
mVertexes->vertexStart = 0; mVertexes->vertexStart = 0;
mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH; mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH;
VertexDeclaration* vertexDecl = mVertexes->vertexDeclaration; VertexDeclaration* vertexDecl = mVertexes->vertexDeclaration;
size_t currOffset = 0; size_t currOffset = 0;
vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_POSITION); vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_POSITION);
currOffset += VertexElement::getTypeSize(VET_FLOAT3); currOffset += VertexElement::getTypeSize(VET_FLOAT3);
vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_NORMAL); vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_NORMAL);
currOffset += VertexElement::getTypeSize(VET_FLOAT3); currOffset += VertexElement::getTypeSize(VET_FLOAT3);
vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
currOffset += VertexElement::getTypeSize(VET_FLOAT2); currOffset += VertexElement::getTypeSize(VET_FLOAT2);
if ( mTerrain->isTextureFadingEnabled() ) { if ( g_Terrain->isTextureFadingEnabled() ) {
vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 1); vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 1);
currOffset += VertexElement::getTypeSize(VET_FLOAT2); currOffset += VertexElement::getTypeSize(VET_FLOAT2);
} }
mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer(
vertexDecl->getVertexSize(0), // size of one whole vertex vertexDecl->getVertexSize(0), // size of one whole vertex
mVertexes->vertexCount, // number of vertices mVertexes->vertexCount, // number of vertices
HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage
false); // no shadow buffer false); // no shadow buffer
mVertexes->vertexBufferBinding->setBinding(MAIN_BINDING, mMainBuffer); //bind the data mVertexes->vertexBufferBinding->setBinding(MAIN_BINDING, mMainBuffer); //bind the data
if ( mTerrain->isMorhpingEnabled() ) if ( g_Terrain->isMorhpingEnabled() )
vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS);
} }
Ogre::HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer( ) { Ogre::HardwareVertexBufferSharedPtr TerrainMesh::createDeltaBuffer( ) {
size_t vw = mWidth; size_t vw = mWidth;
if ( mUseSkirts ) vw += 2; if ( mUseSkirts ) vw += 2;
const size_t totalVerts = (vw * vw); const size_t totalVerts = (vw * vw);
return Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( return Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT1), Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT1),
totalVerts, totalVerts,
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,
false); //no shadow buff false); //no shadow buff
} }
@ -364,115 +381,115 @@ Ogre::HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer( ) {
#define SET_DELTA_AT(x, y, v) \ #define SET_DELTA_AT(x, y, v) \
if ( mUseSkirts ) pDeltas[( y + 1)*vw+ x + 1] = v; \ if ( mUseSkirts ) pDeltas[( y + 1)*vw+ x + 1] = v; \
else pDeltas[( y)*vw+ x] = v; else pDeltas[( y)*vw+ x] = v;
void TerrainRenderable::calculateDeltaValues() { void TerrainMesh::calculateDeltaValues() {
using namespace Ogre; using namespace Ogre;
size_t vw = mWidth; size_t vw = mWidth;
if ( mUseSkirts ) vw += 2; if ( mUseSkirts ) vw += 2;
//must be using morphing to use this function //must be using morphing to use this function
assert(mTerrain->isMorhpingEnabled()); assert(g_Terrain->isMorhpingEnabled());
const size_t step = 2; const size_t step = 2;
// Create a set of delta values // Create a set of delta values
mDeltaBuffer = createDeltaBuffer(); mDeltaBuffer = createDeltaBuffer();
float* pDeltas = static_cast<float*>(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD)); float* pDeltas = static_cast<float*>(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD));
memset(pDeltas, 0, (vw)*(vw) * sizeof(float)); memset(pDeltas, 0, (vw)*(vw) * sizeof(float));
return; return;
bool flag=false; bool flag=false;
for ( size_t y = 0; y < mWidth-step; y += step ) { for ( size_t y = 0; y < mWidth-step; y += step ) {
for ( size_t x = 0; x < mWidth-step; x += step ) { for ( size_t x = 0; x < mWidth-step; x += step ) {
//create the diffrence between the full vertex if the vertex wasn't their //create the diffrence between the full vertex if the vertex wasn't their
float bottom_left = getVertexHeight(x,y); float bottom_left = getVertexHeight(x,y);
float bottom_right = getVertexHeight(x+step,y); float bottom_right = getVertexHeight(x+step,y);
float top_left = getVertexHeight(x,y+step); float top_left = getVertexHeight(x,y+step);
float top_right = getVertexHeight(x+step,y+step); float top_right = getVertexHeight(x+step,y+step);
//const int vw = mWidth+2; //const int vw = mWidth+2;
SET_DELTA_AT(x, y+1, (bottom_left+top_left)/2 - getVertexHeight(x, y+1)) //left SET_DELTA_AT(x, y+1, (bottom_left+top_left)/2 - getVertexHeight(x, y+1)) //left
SET_DELTA_AT(x+2, y+1, (bottom_right+top_right)/2 - getVertexHeight(x+2, y+1)) //right SET_DELTA_AT(x+2, y+1, (bottom_right+top_right)/2 - getVertexHeight(x+2, y+1)) //right
SET_DELTA_AT(x+1, y+2, (top_right+top_left)/2 - getVertexHeight(x+1, y+2)) //top SET_DELTA_AT(x+1, y+2, (top_right+top_left)/2 - getVertexHeight(x+1, y+2)) //top
SET_DELTA_AT(x+1, y, (bottom_right+bottom_left)/2 - getVertexHeight(x+1, y)) //bottom SET_DELTA_AT(x+1, y, (bottom_right+bottom_left)/2 - getVertexHeight(x+1, y)) //bottom
//this might not be correct //this might not be correct
if ( !flag ) if ( !flag )
SET_DELTA_AT(x+1, y+1, (bottom_left+top_right)/2 - getVertexHeight(x+1, y+1)) //center SET_DELTA_AT(x+1, y+1, (bottom_left+top_right)/2 - getVertexHeight(x+1, y+1)) //center
else else
SET_DELTA_AT(x+1, y+1, (bottom_right+top_left)/2 - getVertexHeight(x+1, y+1)) //center SET_DELTA_AT(x+1, y+1, (bottom_right+top_left)/2 - getVertexHeight(x+1, y+1)) //center
flag = !flag; flag = !flag;
}
flag = !flag; //flip tries for next row
} }
flag = !flag; //flip tries for next row
}
mDeltaBuffer->unlock(); mDeltaBuffer->unlock();
mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer); mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer);
} }
#undef SET_DELTA_AT #undef SET_DELTA_AT
//---------------------------------------------- //----------------------------------------------
void TerrainRenderable::calculateIndexValues() { void TerrainMesh::calculateIndexValues() {
using namespace Ogre; using namespace Ogre;
size_t vw = mWidth-1; size_t vw = mWidth-1;
if ( mUseSkirts ) vw += 2; if ( mUseSkirts ) vw += 2;
const size_t indexCount = (vw)*(vw)*6; const size_t indexCount = (vw)*(vw)*6;
//need to manage allocation if not null //need to manage allocation if not null
assert(mIndices==0); assert(mIndices==0);
mIndices = new IndexData(); mIndices = new IndexData();
mIndices->indexCount = indexCount; mIndices->indexCount = indexCount;
mIndices->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer( mIndices->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(
HardwareIndexBuffer::IT_16BIT, HardwareIndexBuffer::IT_16BIT,
indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
unsigned short* indices = static_cast<unsigned short*>(mIndices->indexBuffer->lock(0, unsigned short* indices = static_cast<unsigned short*>(mIndices->indexBuffer->lock(0,
mIndices->indexBuffer->getSizeInBytes(), mIndices->indexBuffer->getSizeInBytes(),
HardwareBuffer::HBL_DISCARD)); HardwareBuffer::HBL_DISCARD));
bool flag = false; bool flag = false;
Ogre::uint indNum = 0; Ogre::uint indNum = 0;
for ( Ogre::uint y = 0; y < (vw); y+=1 ) { for ( Ogre::uint y = 0; y < (vw); y+=1 ) {
for ( Ogre::uint x = 0; x < (vw); x+=1 ) { for ( Ogre::uint x = 0; x < (vw); x+=1 ) {
const int line1 = y * (vw+1) + x; const int line1 = y * (vw+1) + x;
const int line2 = (y + 1) * (vw+1) + x; const int line2 = (y + 1) * (vw+1) + x;
if ( flag ) { if ( flag ) {
*indices++ = line1; *indices++ = line1;
*indices++ = line2; *indices++ = line2;
*indices++ = line1 + 1; *indices++ = line1 + 1;
*indices++ = line1 + 1; *indices++ = line1 + 1;
*indices++ = line2; *indices++ = line2;
*indices++ = line2 + 1; *indices++ = line2 + 1;
} else { } else {
*indices++ = line1; *indices++ = line1;
*indices++ = line2; *indices++ = line2;
*indices++ = line2 + 1; *indices++ = line2 + 1;
*indices++ = line1; *indices++ = line1;
*indices++ = line2 + 1; *indices++ = line2 + 1;
*indices++ = line1 + 1; *indices++ = line1 + 1;
} }
flag = !flag; //flip tris for next time flag = !flag; //flip tris for next time
indNum+=6; indNum+=6;
}
flag = !flag; //flip tries for next row
} }
assert(indNum==indexCount); flag = !flag; //flip tries for next row
mIndices->indexBuffer->unlock(); }
//return mIndices; assert(indNum==indexCount);
mIndices->indexBuffer->unlock();
//return mIndices;
} }

@ -1,229 +1,343 @@
/** /**
* @brief Terrain for one cell. Handles building, destroying and LOD morphing * @brief Terrain for one cell. Handles building, destroying and LOD morphing
*/ */
class TerrainRenderable : public Ogre::Renderable, public Ogre::MovableObject { #define QSDEBUG
public: class TerrainMesh : public Ogre::Renderable, public Ogre::MovableObject
{
explicit TerrainRenderable(Terrain* t, QuadSegment* qs, int width, int depth, bool skirts); public:
/** TerrainMesh(QuadData* qd, float segSize, float startX, float startY,
* Calls destroy(); const Ogre::Vector3 &pos,
*/ int width, int depth, bool skirts,
~TerrainRenderable(); Ogre::SceneNode *parent);
/** ~TerrainMesh()
* Creates the actual mesh, it has to be called manually, as it is not called in the constructor {
*/ destroy();
void create(Ogre::SceneNode* sn) ;
assert(node);
/**
* Can be called manually and can be called from the destuctor. This destroyes the mesh node->detachAllObjects();
*/ node->getCreator()->destroySceneNode(node);
void destroy();
/**
* @brief Checks if it needs to be split or unsplit and deals with the morph factor
* @brief time seconds since last frame
*/
void update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist);
/**
* @todo Needs to work out what this does (if it does what it is meant to)
*/
void visitRenderables(Renderable::Visitor* visitor,
bool debugRenderables = false) {
visitor->visit(this, 0, false);
} }
virtual const Ogre::MaterialPtr& getMaterial( void ) const { /**
return mMaterial; * Can be called manually and can be called from the destuctor. This
* destroyes the mesh
*/
// FIXME: We don't need this as a separate function.
void destroy();
/**
* @brief Checks if it needs to be split or unsplit and deals with
* the morph factor. time seconds since last frame
*/
void update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist);
/**
* @todo Needs to work out what this does (if it does what it is meant to)
*/
void visitRenderables(Renderable::Visitor* visitor,
bool debugRenderables = false) {
visitor->visit(this, 0, false);
}
virtual const Ogre::MaterialPtr& getMaterial( void ) const {
return mMaterial;
}
//-----------------------------------------------------------------------
void getRenderOperation( Ogre::RenderOperation& op ) {
op.useIndexes = true;
op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
op.vertexData = mVertexes;
op.indexData = mIndices;
}
//-----------------------------------------------------------------------
void getWorldTransforms( Ogre::Matrix4* xform ) const {
*xform = mParentNode->_getFullTransform();
}
//-----------------------------------------------------------------------
const Ogre::Quaternion& getWorldOrientation(void) const {
return mParentNode->_getDerivedOrientation();
}
//-----------------------------------------------------------------------
const Ogre::Vector3& getWorldPosition(void) const {
return mParentNode->_getDerivedPosition();
}
//-----------------------------------------------------------------------
Ogre::Real getSquaredViewDepth(const Ogre::Camera *cam) const {
Ogre::Vector3 diff = mCenter - cam->getDerivedPosition();
// Use squared length to avoid square root
return diff.squaredLength();
}
//-----------------------------------------------------------------------
const Ogre::LightList& getLights(void) const {
if (mLightListDirty) {
getParentSceneNode()->getCreator()->_populateLightList(
mCenter, this->getBoundingRadius(), mLightList);
mLightListDirty = false;
} }
//----------------------------------------------------------------------- return mLightList;
void getRenderOperation( Ogre::RenderOperation& op ) { }
op.useIndexes = true; //-----------------------------------------------------------------------
op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; virtual const Ogre::String& getMovableType( void ) const {
op.vertexData = mVertexes; static Ogre::String t = "MW_TERRAIN";
op.indexData = mIndices; return t;
}
//-----------------------------------------------------------------------
void _updateRenderQueue( Ogre::RenderQueue* queue ) {
mLightListDirty = true;
queue->addRenderable(this, mRenderQueueID);
}
const Ogre::AxisAlignedBox& getBoundingBox( void ) const {
return mBounds;
};
//-----------------------------------------------------------------------
Ogre::Real getBoundingRadius(void) const {
return mBoundingRadius;
}
//-----------------------------------------------------------------------
/**
* @brief passes the morph factor to the custom vertex program
*/
void _updateCustomGpuParameter(const Ogre::GpuProgramParameters::AutoConstantEntry& constantEntry,
Ogre::GpuProgramParameters* params) const;
/**
* @brief sets the mExtraMorphAmount so it slowly regains detail from the lowest morph factor
*/
void justSplit();
/**
* @brief Does nothing
*/
inline void justUnsplit(){
}
inline float getMax(){
return mMax;
}
inline float getMin(){
return mMin;
}
/**
* Gets how many vertexes wide this quad segment is. Should always be 2^n+1
* @return the vertex width of this quad segment
*/
int getVertexWidth()
{
return (mQuadData->getVertexWidth()-1)*mSegmentSize+1;
}
/**
* @brief gets a vertex assuming that x = 0, y = 0 addresses the start of the quad
*/
float getVertex(int x, int y)
{
#ifdef QSDEBUG
{
const int vw = getVertexWidth();
assert(x<vw);
} }
#endif
//----------------------------------------------------------------------- return mQuadData->getVertex((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x));
void getWorldTransforms( Ogre::Matrix4* xform ) const { }
*xform = mParentNode->_getFullTransform();
} float getNormal(int x, int y, int z)
//----------------------------------------------------------------------- {
const Ogre::Quaternion& getWorldOrientation(void) const { assert(z>=0&&z<3);
return mParentNode->_getDerivedOrientation(); return mQuadData->getNormal(((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x))*3+z);
} }
//-----------------------------------------------------------------------
const Ogre::Vector3& getWorldPosition(void) const { private:
return mParentNode->_getDerivedPosition();
} void createMaterial()
//----------------------------------------------------------------------- {
assert(mSegmentSize>0);
Ogre::Real getSquaredViewDepth(const Ogre::Camera *cam) const { if ( mSegmentSize == 1 ) //we can use the top level material
Ogre::Vector3 diff = mCenter - cam->getDerivedPosition(); {
// Use squared length to avoid square root mMaterial = mQuadData->getMaterial();
return diff.squaredLength(); return;
} }
//-----------------------------------------------------------------------
if ( mQuadData->getTexture().length() )
const Ogre::LightList& getLights(void) const { assert(0&&"NOT IMPLEMENTED");
if (mLightListDirty) {
getParentSceneNode()->getCreator()->_populateLightList( const std::vector<int>& tref = mQuadData->getTextureIndexRef();
mCenter, this->getBoundingRadius(), mLightList); const int indexSize = sqrt(tref.size());
mLightListDirty = false; const int cellIndexSize = indexSize - 2;
}
return mLightList; //plus 1 to take into account border
} const int xoff = float(cellIndexSize) * mX;
//----------------------------------------------------------------------- const int yoff = float(cellIndexSize) * mY;
virtual const Ogre::String& getMovableType( void ) const { const int size = float(cellIndexSize) * mSegmentSize;
static Ogre::String t = "MW_TERRAIN";
return t; std::vector<int> ti;
}
//----------------------------------------------------------------------- ti.resize((size+2)*(size+2), -1);
void _updateRenderQueue( Ogre::RenderQueue* queue ) {
mLightListDirty = true; for ( int y = 0; y < size+2; ++y ){
queue->addRenderable(this, mRenderQueueID); for ( int x = 0; x < size+2; ++x ){
ti[(y)*(size+2)+(x)] = tref.at((y+yoff)*(indexSize)+(x+xoff));
}
} }
const Ogre::AxisAlignedBox& getBoundingBox( void ) const { mMaterial = g_materialGen->getAlphaMat
return mBounds; (ti,size,
}; 1, 1.0f/size,
//----------------------------------------------------------------------- mQuadData->getUsedResourcesRef());
Ogre::Real getBoundingRadius(void) const { }
return mBoundingRadius;
inline bool hasParentTexture() const{
return mQuadData->hasParentTexture();
}
inline const std::string& getParentTexture() const{
return mQuadData->getParentTexture();
}
inline int getVertexSeperation(){
return mQuadData->getVertexSeperation();
}
inline float getSegmentSize(){
return mSegmentSize;
}
inline float getStartX(){
return mX;
}
inline float getStartY(){
return mY;
}
/**
* @brief Adds another pass to the material to fade in/out the material from a higher level
*/
void addFadePass();
/**
* @brief removes the last pass from the material. Assumed to be the fade pass
*/
void removeFadePass();
/**
* @return the height at the given vertex
*/
float getVertexHeight(int x, int y);
/**
* Inits the vertex stuff
*/
void createVertexBuffer();
/**
* @brief fills the vertex buffer with data
* @todo I don't think tex co-ords are right
*/
void calculateVetexValues();
/**
* @brief returns a a new Vertex Buffer ready for input
* @remarks Unlike other terrain libs, this isn't 0s when it is returend
*/
Ogre::HardwareVertexBufferSharedPtr createDeltaBuffer( );
/**
* @brief DOESN'T WORK FULLY
* @todo fix
*/
void calculateDeltaValues();
/**
* @brief gets the position of a vertex. It will not interpolate
*/
Ogre::Vector3 getVertexPosition(int x, int y);
/**
* @brief gets the indicies for the vertex data.
*/
void calculateIndexValues();
/*
* Sets the bounds on the renderable. This requires that mMin/mMax
* have been set. They are set in createVertexData. This may look
* as though it is done twice, as it is also done in MeshInterface,
* however, there is no guarentee that the mesh sizes are the same
*/
void setBounds();
Ogre::SceneNode* node;
const int mWidth;
const bool mUseSkirts;
///true if the land has been built
bool mBuilt;
int mDepth;
Ogre::MaterialPtr mMaterial;
Ogre::ManualObject* mObject;
Ogre::VertexData* mVertexes;
Ogre::IndexData* mIndices;
Ogre::HardwareVertexBufferSharedPtr mMainBuffer;
Ogre::HardwareVertexBufferSharedPtr mDeltaBuffer;
mutable bool mLightListDirty;
Ogre::Real mBoundingRadius;
Ogre::AxisAlignedBox mBounds;
Ogre::Vector3 mCenter;
Ogre::Real mLODMorphFactor, mTextureFadeFactor;
/** Max and min heights of the mesh */
float mMin, mMax;
int computeOffset(float x)
{
#ifdef QSDEBUG
{
//check it is a valid position
const int start = (mQuadData->getVertexWidth()-1)*x;
const int vw = getVertexWidth()-1;
assert(vw>0);
assert((start%vw)==0);
} }
//----------------------------------------------------------------------- #endif
return float((mQuadData->getVertexWidth()-1))*x;
/** }
* @brief passes the morph factor to the custom vertex program
*/ void computeOffsets()
void _updateCustomGpuParameter(const Ogre::GpuProgramParameters::AutoConstantEntry& constantEntry, {
Ogre::GpuProgramParameters* params) const; mXOffset = computeOffset(mX);
mYOffset = computeOffset(mY);
/** }
* @brief sets the mExtraMorphAmount so it slowly regains detail from the lowest morph factor
*/ QuadData* mQuadData;
void justSplit(); float mSegmentSize;
/** float mX, mY;
* @brief Does nothing int mXOffset, mYOffset;
*/
inline void justUnsplit(){ /**
} * @brief holds the amount to morph by, this decreases to 0 over a periord of time
* It is changed and used in the update() function
inline float getMax(){ */
return mMax; Ogre::Real mExtraMorphAmount, mExtraFadeAmount;
}
inline float getMin(){ /**
return mMin; * True if the fade pass has been added to the material.
} */
bool mHasFadePass;
private:
//Custom params used in terrian shaders.
/** static const size_t MORPH_CUSTOM_PARAM_ID = 77;
* @brief Adds another pass to the material to fade in/out the material from a higher level static const size_t FADE_CUSTOM_PARAM_ID = 78;
*/
void addFadePass(); //Terrain bindings
/** static const int MAIN_BINDING = 0;
* @brief removes the last pass from the material. Assumed to be the fade pass static const int DELTA_BINDING = 1;
*/
void removeFadePass();
/**
* @return the height at the given vertex
*/
float getVertexHeight(int x, int y);
/**
* Inits the vertex stuff
*/
void createVertexBuffer();
/**
* @brief fills the vertex buffer with data
* @todo I don't think tex co-ords are right
*/
void calculateVetexValues();
/**
* @brief returns a a new Vertex Buffer ready for input
* @remarks Unlike other terrain libs, this isn't 0s when it is returend
*/
Ogre::HardwareVertexBufferSharedPtr createDeltaBuffer( );
/**
* @brief DOESN'T WORK FULLY
* @todo fix
*/
void calculateDeltaValues();
/**
* @brief gets the position of a vertex. It will not interpolate
*/
Ogre::Vector3 getVertexPosition(int x, int y);
/**
* @brief gets the indicies for the vertex data.
*/
void calculateIndexValues();
/**
* @brief sets the bounds on the renderable. This requires that mMin/mMax have been set. They are set in createVertexData
* @remarks this may look as though it is done twice, as it is also done in MeshInterface, however, there is no guarentee that
* the mesh sizes are the same
*/
void setBounds();
const int mWidth;
const bool mUseSkirts;
///true if the land has been built
bool mBuilt;
int mDepth;
QuadSegment* mSegment;
Terrain* mTerrain;
Ogre::MaterialPtr mMaterial;
Ogre::ManualObject* mObject;
Ogre::SceneNode* mSceneNode;
Ogre::VertexData* mVertexes;
Ogre::IndexData* mIndices;
Ogre::HardwareVertexBufferSharedPtr mMainBuffer;
Ogre::HardwareVertexBufferSharedPtr mDeltaBuffer;
mutable bool mLightListDirty;
Ogre::Real mBoundingRadius;
Ogre::AxisAlignedBox mBounds;
Ogre::Vector3 mCenter;
Ogre::Real mLODMorphFactor, mTextureFadeFactor;
/** Max and min heights of the mesh */
float mMin, mMax;
/**
* @brief holds the amount to morph by, this decreases to 0 over a periord of time
* It is changed and used in the update() function
*/
Ogre::Real mExtraMorphAmount, mExtraFadeAmount;
/**
* True if the fade pass has been added to the material.
*/
bool mHasFadePass;
//Custom params used in terrian shaders.
static const size_t MORPH_CUSTOM_PARAM_ID = 77;
static const size_t FADE_CUSTOM_PARAM_ID = 78;
//Terrain bindings
static const int MAIN_BINDING = 0;
static const int DELTA_BINDING = 1;
}; };

@ -55,11 +55,13 @@ void makePath(char[] pt)
makeDir(pt); makeDir(pt);
} }
void initTerrain() void initTerrain(bool doGen)
{ {
makePath("cache/terrain"); makePath("cache/terrain");
//terr_genData(); if(doGen)
terr_genData();
terr_setupRendering(); terr_setupRendering();
} }

@ -13,4 +13,4 @@ class MTrace
{ dbg_untrace(); } { dbg_untrace(); }
}; };
#define TRACE(x) MTrace _trc(x); #define TRACE(x) MTrace __trc(x);

Loading…
Cancel
Save