diff --git a/Makefile b/Makefile index 44eafa483..51571c7c8 100644 --- a/Makefile +++ b/Makefile @@ -33,8 +33,8 @@ mygui_cpp=mygui console # Ditto for the landscape engine, in terrain/cpp_X.cpp terrain_cpp=baseland esm framelistener generator index landdata\ -materialgen meshinterface mwheightmap mwquadmatgen palette point2\ -quad quaddata quadsegment terraincls terrain terrainmesh +materialgen mwheightmap palette point2\ +quad quaddata terraincls terrain terrainmesh # FFmpeg files, in the form sound/cpp_X.cpp. avcodec_cpp=avcodec diff --git a/openmw.d b/openmw.d index 09ac44b94..7a5c17292 100644 --- a/openmw.d +++ b/openmw.d @@ -76,6 +76,7 @@ void main(char[][] args) bool noSound = false; bool debugOut = false; bool extTest = false; + bool doGen = false; // Some examples to try: // @@ -96,6 +97,7 @@ void main(char[][] args) foreach(char[] a; args[1..$]) if(a == "-n") render = false; else if(a == "-ex") extTest = true; + else if(a == "-gen") doGen = true; else if(a == "-h") help=true; else if(a == "-rk") resetKeys = true; else if(a == "-oc") showOgreFlag = true; @@ -122,6 +124,7 @@ void main(char[][] args) writefln(" Options:"); writefln(" -n Only load, do not render"); writefln(" -ex Test the terrain system"); + writefln(" -gen Generate landscape cache"); writefln(" -rk Reset key bindings to default"); writefln(" -oc Show the Ogre config dialogue"); writefln(" -ns Completely disable sound"); @@ -257,7 +260,7 @@ Try specifying another cell name on the command line, or edit openmw.ini."); ogre_makeSky(); */ - initTerrain(); + initTerrain(doGen); } else { diff --git a/terrain/cpp_framelistener.cpp b/terrain/cpp_framelistener.cpp index 3c7e899f7..75c417c4e 100644 --- a/terrain/cpp_framelistener.cpp +++ b/terrain/cpp_framelistener.cpp @@ -1,15 +1,12 @@ class TerrainFrameListener : public FrameListener { protected: - Terrain* mTerrain; - MWHeightmap* mHeights; - /** * Updates the quad tree */ bool frameEnded(const FrameEvent& evt) { - mTerrain->update(evt.timeSinceLastFrame); + g_Terrain->update(evt.timeSinceLastFrame); return true; } @@ -20,19 +17,18 @@ public: mRoot->addFrameListener(this); //our derived heightmap - mHeights = new MWHeightmap(); - mHeights->load(TERRAIN_OUTPUT); + g_heightMap = new MWHeightmap(); + g_heightMap->load(TERRAIN_OUTPUT); //setup terrain - mTerrain = new Terrain( mHeights, //heightmap - mSceneMgr->getRootSceneNode()->createChildSceneNode("TERRAIN_ROOT")); //root scene node + g_Terrain = new Terrain(mSceneMgr->getRootSceneNode()->createChildSceneNode("TERRAIN_ROOT")); //root scene node //fix settings - mTerrain->setMorphingEnabled(false); - mTerrain->setTextureFadingEnabled(false); + g_Terrain->setMorphingEnabled(false); + g_Terrain->setTextureFadingEnabled(false); //create the quad node - mTerrain->create(); + g_Terrain->create(); } /* KILLME diff --git a/terrain/cpp_generator.cpp b/terrain/cpp_generator.cpp index 678a6f65c..107b72e2c 100644 --- a/terrain/cpp_generator.cpp +++ b/terrain/cpp_generator.cpp @@ -87,7 +87,7 @@ public: int cellDist = pow((float)2, mIndex.getMaxDepth()); // Temporary storage - MWQuadData qd(0); + QuadData qd; qd.setVertexSeperation(128*halfLevel); //dist between two verts std::vector& gh = qd.getHeightsRef(); //ref to the data storage in the quad @@ -207,6 +207,8 @@ public: boost::archive::binary_oarchive oap(ofp); oai << mIndex; oap << mPalette; + + mDataO.close(); } private: @@ -333,13 +335,14 @@ private: assert(Ogre::Math::Sqrt(ltex.size())==alphaSize); std::list createdResources; + // FIXME: Move out of this function? MaterialGenerator mg; mg.setTexturePaths(mPalette.getPalette()); const int scaleDiv = alphaSize/LAND_LTEX_WIDTH; //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); tex1->getBuffer()->getRenderTarget()->writeContentsToFile(outputName); Ogre::MaterialManager::getSingleton().remove(mp->getHandle()); diff --git a/terrain/cpp_index.cpp b/terrain/cpp_index.cpp index 2497fa686..c35fd1673 100644 --- a/terrain/cpp_index.cpp +++ b/terrain/cpp_index.cpp @@ -1,90 +1,90 @@ /** -* @brief holds data about positions of data and general header info -*/ -class Index { + * Holds index and other data describing the landscape.data file. + */ +class Index +{ public: - ///saves my fingers :P - typedef std::map >::iterator OffsetItr; - typedef std::map >::const_iterator OffsetConstItr; + typedef std::map >::iterator OffsetItr; + typedef std::map >::const_iterator OffsetConstItr; - /** - * @brief sets the root quads side length in gu - * @param l the side length - * - * This is used for working out the locations of quad children. - * I am assuming a long is enough... - */ - inline void setRootSideLength(long l) { - mRootSideLength = l; - } - /** - * @return the side length of the root quad. - */ - inline long getRootSideLength() const { - return mRootSideLength; - } + /** + * @brief sets the root quads side length in gu + * @param l the side length + * + * This is used for working out the locations of quad children. + * I am assuming a long is enough... + */ + inline void setRootSideLength(long l) { + mRootSideLength = l; + } + /** + * @return the side length of the root quad. + */ + inline long getRootSideLength() const { + return mRootSideLength; + } - inline void setMaxDepth(int d) { - mMaxDepth = d; - } - inline int getMaxDepth() const { - return mMaxDepth; - } + inline void setMaxDepth(int d) { + mMaxDepth = d; + } + inline int getMaxDepth() const { + return mMaxDepth; + } - /** - * @return -1 is returned if there is no offset - * @param x, y the position of the quad in gu - * - * Slightly faster using hasOffset to check if it exists - * Shouldn't be noticable diffrence. - */ - inline long getOffset(long x, long y) const { //inline? - OffsetConstItr itr1 = mQuadOffsets.find(x); - if ( itr1 == mQuadOffsets.end() ) return -1; - std::map::const_iterator itr2 = itr1->second.find(y); - if ( itr2 == itr1->second.end() ) return -1; - return itr2->second; - } + /** + * @return -1 is returned if there is no offset + * @param x, y the position of the quad in gu + * + * Slightly faster using hasOffset to check if it exists + * Shouldn't be noticable diffrence. + */ + inline long getOffset(long x, long y) const { //inline? + OffsetConstItr itr1 = mQuadOffsets.find(x); + if ( itr1 == mQuadOffsets.end() ) return -1; + std::map::const_iterator itr2 = itr1->second.find(y); + if ( itr2 == itr1->second.end() ) return -1; + return itr2->second; + } - /** - * @brief checks if a quad for the given position exists - * @return true/false - * @param x, y the position of the quad in gu - * - * @todo Would it be worth merging this with getOffset? - */ - inline bool hasOffset(long x, long y) const { - OffsetConstItr itr = mQuadOffsets.find(x); - if ( itr == mQuadOffsets.end() ) return false; - return (itr->second.find(y) != itr->second.end()); - } + /** + * @brief checks if a quad for the given position exists + * @return true/false + * @param x, y the position of the quad in gu + * + * @todo Would it be worth merging this with getOffset? + */ + inline bool hasOffset(long x, long y) const { + OffsetConstItr itr = mQuadOffsets.find(x); + if ( itr == mQuadOffsets.end() ) return false; + return (itr->second.find(y) != itr->second.end()); + } - /** - * @brief sets an offset of a quad - * @param x, y the position in gu of the quad - * @param o the offset within the file of the records for this quad - */ - inline void setOffset(long x, long y, long o) { - mQuadOffsets[x][y] = o; - } + /** + * @brief sets an offset of a quad + * @param x, y the position in gu of the quad + * @param o the offset within the file of the records for this quad + */ + inline void setOffset(long x, long y, long o) { + mQuadOffsets[x][y] = o; + } protected: - std::map > mQuadOffsets; - long mRootSideLength; ///length in gu of the root quad - int mMaxDepth; ///maximum depth assuming root quad depth = 0 + std::map > mQuadOffsets; + long mRootSideLength; ///length in gu of the root quad + int mMaxDepth; ///maximum depth assuming root quad depth = 0 - friend class boost::serialization::access; - /** - * Saves the data for the max depth, the root side legnth, and the quad offsets - */ - template - inline void serialize(Archive& ar, const unsigned int version){ + friend class boost::serialization::access; + /** + * Saves the data for the max depth, the root side legnth, and the quad offsets + */ + template + inline void serialize(Archive& ar, const unsigned int version){ ar &mMaxDepth; ar &mRootSideLength; ar &mQuadOffsets; - } + } }; diff --git a/terrain/cpp_materialgen.cpp b/terrain/cpp_materialgen.cpp index 72f591c6d..3872139a8 100644 --- a/terrain/cpp_materialgen.cpp +++ b/terrain/cpp_materialgen.cpp @@ -1,35 +1,30 @@ -/** - * Handles the runtime generation of materials - * - */ -class MaterialGenerator +class TextureSplatter { - class TextureSplatter - { - public: - inline TextureSplatter(const std::vector& ltex, - int texSize, - int ltexsize, - int border) : - mLTEX(ltex), - mTexSize(texSize), //the root of the size of the texture - mLTEXSize(ltexsize), //the root of the ltex array - mBorder(border) { //the size of the border of the ltex - mSizeDiff = texSize/(ltexsize-border*2); - } +public: + inline TextureSplatter(const std::vector& ltex, + int texSize, + int ltexsize, + int border) : + mLTEX(ltex), + mTexSize(texSize), //the root of the size of the texture + mLTEXSize(ltexsize), //the root of the ltex array + mBorder(border) { //the size of the border of the ltex + mSizeDiff = texSize/(ltexsize-border*2); + } - int getAlphaAtPixel(int x, int y, int tid) { - //offset for border - x += (mBorder*mSizeDiff); - y += (mBorder*mSizeDiff); + int getAlphaAtPixel(int x, int y, int tid) { + //offset for border + x += (mBorder*mSizeDiff); + y += (mBorder*mSizeDiff); - if ( getTextureAtPixel(x,y) == tid ) { - return 255; - } else if ( mBorder > 0 ) { //hacky remove fix. perofrmance issues. skips if not ingame gen. + if ( getTextureAtPixel(x,y) == tid ) { + return 255; + } else if ( mBorder > 0 ) + { + //hacky remove fix. perofrmance issues. skips if not ingame gen. float amount = 0; for ( int ty = y-1; ty <= y+1; ++ty ) { for ( int tx = x-1; tx <= x+1; ++tx ) { - if ( ty < -mBorder*mSizeDiff || tx < -mBorder*mSizeDiff || ty >= mTexSize+mBorder*mSizeDiff || @@ -44,35 +39,43 @@ class MaterialGenerator assert(amount>=0&&amount<=1); return amount*255; } - return 0; - - } + return 0; + } - private: +private: - int getTextureAtPixel(int x, int y) { - x = (x - x%mSizeDiff)/mSizeDiff; - y = (y - y%mSizeDiff)/mSizeDiff; - //y = floor(float(y)/float(mSizeDiff)); - //x = floor(float(x)/float(mSizeDiff)); + int getTextureAtPixel(int x, int y) { + x = (x - x%mSizeDiff)/mSizeDiff; + y = (y - y%mSizeDiff)/mSizeDiff; - return mLTEX[(y)*mLTEXSize+(x)]; - } + return mLTEX[(y)*mLTEXSize+(x)]; + } - const std::vector& mLTEX; - const int mTexSize; - const int mLTEXSize; - const int mBorder; - int mSizeDiff; - }; + const std::vector& mLTEX; + const int mTexSize; + const int mLTEXSize; + const int mBorder; + int mSizeDiff; +}; +/** + * Handles the runtime generation of materials + * + */ +class MaterialGenerator +{ 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 createdResources) + Ogre::MaterialPtr generateSingleTexture + (const std::string& texName, + std::list createdResources) { + const std::string matname("MAT" + Ogre::StringConverter::toString(mCount++)); + if ( !Ogre::MaterialManager::getSingleton().resourceExists(matname) ) { Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(matname,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); @@ -87,159 +90,17 @@ public: return Ogre::MaterialPtr(); } - /** - * Currently doesn't work - */ - Ogre::MaterialPtr getShaderAlpha(const std::string& materialName, std::vector& ltex, int size, std::list& 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 texturesWritten; //a list of "done" textures. Order important - std::vector alphasGenerated; //alphas generated - - std::set 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(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::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 */ - Ogre::MaterialPtr getAlphaMat(const std::string& materialName, - std::vector& ltex, + Ogre::MaterialPtr getAlphaMat(std::vector& ltex, int size, int border, float scaleDiv, std::list& createdResources) { + const std::string materialName("MAT" + Ogre::StringConverter::toString(mCount++)); + const int sizeDiff = 4; size *= sizeDiff; @@ -337,25 +198,6 @@ public: return material; } - std::string generateAlphaMap(const std::string& name, - std::vector& ltex, - int size, - int border, - float scaleDiv, - std::list& 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 r) { mTexturePaths = r; } @@ -364,4 +206,6 @@ private: * Merged records accross all mods for LTEX data */ std::map mTexturePaths; + + unsigned int mCount; }; diff --git a/terrain/cpp_meshinterface.cpp b/terrain/cpp_meshinterface.cpp deleted file mode 100644 index 1dce015d5..000000000 --- a/terrain/cpp_meshinterface.cpp +++ /dev/null @@ -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::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::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); -} diff --git a/terrain/cpp_meshinterface.h b/terrain/cpp_meshinterface.h deleted file mode 100644 index 426510b39..000000000 --- a/terrain/cpp_meshinterface.h +++ /dev/null @@ -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::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::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 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 ); - - - -}; diff --git a/terrain/cpp_mwheightmap.cpp b/terrain/cpp_mwheightmap.cpp index a21ae7262..8b74488b9 100644 --- a/terrain/cpp_mwheightmap.cpp +++ b/terrain/cpp_mwheightmap.cpp @@ -3,23 +3,30 @@ * major improvment would be to store the data as a quad tree. It might * improve lookup times. Then again. Might not */ -class MWHeightmap : public TerrainHeightmap +class MWHeightmap { 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); - - if ( !data ) + long offset = mIndex.getOffset(x,y); + if ( offset == -1 ) //check we have xy assert(0); - return data; - } + mDataIFS.seekg(offset); - inline bool hasData(Quad* q) { - return hasQuad(q->getPosition().x,q->getPosition().y); + //load the quad from the file + 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() { return mIndex.getRootSideLength(); } @@ -42,9 +49,7 @@ public: boost::archive::binary_iarchive oa(ifs); oa >> mPalette; } - mMaterialGen.setTexturePaths(mPalette.getPalette()); - - mMaterialGenerator = new MWQuadMaterialGen(&mMaterialGen); + g_materialGen->setTexturePaths(mPalette.getPalette()); mDataIFS.open(std::string(fn + ".data").c_str(), std::ios::binary); return true; @@ -52,33 +57,10 @@ public: 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 std::ifstream mDataIFS; ///holds the offsets of the quads Index mIndex; TexturePalette mPalette; - ///material generator. gens a ogre::material from quad data - MaterialGenerator mMaterialGen; + }; diff --git a/terrain/cpp_mwquadmatgen.cpp b/terrain/cpp_mwquadmatgen.cpp deleted file mode 100644 index 0ce8ccd76..000000000 --- a/terrain/cpp_mwquadmatgen.cpp +++ /dev/null @@ -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& 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 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; - -}; diff --git a/terrain/cpp_quad.cpp b/terrain/cpp_quad.cpp index 5b7bf18a1..4f6ac1966 100644 --- a/terrain/cpp_quad.cpp +++ b/terrain/cpp_quad.cpp @@ -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 - * The functions split and unslip either break the current quad into smaller quads, or - * alternatively remove the lower quads and create the terrain mesh on the the current (now the lowest) level + * A quad can either hold a mesh, or 4 other sub quads The functions + * split and unsplit either break the current quad into smaller quads, + * 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 { -public: + enum SplitState { SS_NONE, SS_SPLIT, SS_UNSPLIT }; /** * when each quad is split, the children can be one of 4 places, @@ -19,9 +28,12 @@ public: * and should always have 4 children. */ + typedef std::list MeshList; + +public: + // FIXME: There's probably a better way to do this enum QuadLocation { QL_NW, QL_NE, QL_SW, QL_SE, QL_ROOT }; - /** * @param l the location of the quad * @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 */ - Quad(QuadLocation l, Quad* p, Terrain* t) - : mParent(p), mTerrain(t), mTerrainMesh(0), mLocation(l) + Quad(QuadLocation l, Quad* p) + : mParent(p), mLocation(l), mQuadData(NULL) { + TRACE("Quad"); //as mentioned elsewhere, the children should all be null. memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN); @@ -44,46 +57,45 @@ public: mSideLength = p->getSideLength()/2; //horrible bit of code + // FIXME switch (l) { case Quad::QL_NE: - mPosition.x += mSideLength/2; - mPosition.y += mSideLength/2; - break; + mPosition.x += mSideLength/2; + mPosition.y += mSideLength/2; + break; case Quad::QL_NW: - mPosition.x -= mSideLength/2; - mPosition.y += mSideLength/2; - break; + mPosition.x -= mSideLength/2; + mPosition.y += mSideLength/2; + break; case Quad::QL_SE: - mPosition.x += mSideLength/2; - mPosition.y -= mSideLength/2; - break; + mPosition.x += mSideLength/2; + mPosition.y -= mSideLength/2; + break; case Quad::QL_SW: - mPosition.x -= mSideLength/2; - mPosition.y -= mSideLength/2; - break; + mPosition.x -= mSideLength/2; + mPosition.y -= mSideLength/2; + break; default: - break;//get rid of warning + break; } //set after positions have been retrived - TerrainHeightmap* d = mTerrain->getTerrainData(); - mMaxDepth = d->getMaxDepth(); - mHasData = d->hasData(this); + mMaxDepth = g_heightMap->getMaxDepth(); + mHasData = g_heightMap->hasData(mPosition.x, mPosition.y); if ( needSplit() ) //need to "semi" build terrain - split(); + split(); else if ( mHasData ) { buildTerrain(); - getMesh()->justSplit(); + justSplit(); } - } else - { //assume it is root node, get data and possition + { //assume it is root node, get data and position mDepth = 0; //root - mSideLength = mTerrain->getTerrainData()->getRootSideLength(); - mPosition = Point2(0,0); //see Quad::getPosition as to why this is always 0 + mSideLength = g_heightMap->getRootSideLength(); + mPosition = Point2(0,0); mHasData = false; @@ -100,36 +112,12 @@ public: */ ~Quad() { + TRACE("~Quad"); destroyTerrain(); for (size_t i = 0; i < NUM_CHILDREN; 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. * @@ -139,34 +127,26 @@ public: */ bool needSplit() { + TRACE("needSplit"); if ( hasChildren() || getDepth() == mMaxDepth || !hasData() ) return false; - return ( mTerrainMesh && mTerrainMesh->getSplitState() - == MeshInterface::SS_SPLIT ); + return ( mQuadData && (mSplitState == 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 * Creates children, and either splits them, or creates landscape for them */ void split() { + TRACE("split"); destroyTerrain(); //create a new terrain 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()); } @@ -176,17 +156,20 @@ public: */ void unsplit() { + TRACE("unsplit"); //shouldn't unsplit 0 depth assert(getDepth()); - for ( size_t i = 0; i < NUM_CHILDREN; i++ )//{ + for ( size_t i = 0; i < NUM_CHILDREN; i++ ) delete mChildren[i]; memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN); - if ( mHasData ) { - buildTerrain(); - getMesh()->justUnsplit(); - } + if ( mHasData ) + { + buildTerrain(); + justUnsplit(); + } + assert(!needSplit()); } @@ -196,17 +179,21 @@ public: */ bool needUnsplit() { - if ( hasChildren() && getDepth() ) { - for (size_t i=0;i< NUM_CHILDREN;i++) { - if ( mChildren[i]->hasData() ) { - if ( !mChildren[i]->hasMesh() ) - return false; - else if ( mChildren[i]->getMesh()->getSplitState() != MeshInterface::SS_UNSPLIT) - return false; - } + TRACE("needUnsplit"); + if ( hasChildren() && getDepth() ) + { + for (size_t i=0;i< NUM_CHILDREN;i++) + { + if ( mChildren[i]->hasData() ) + { + 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 if ( getDepth() && !hasData() ) @@ -221,10 +208,41 @@ public: */ void buildTerrain() { + TRACE("buildTerrain"); + assert(!mQuadData); assert(hasData()); - assert(getMesh() == NULL); //the terrain sould not exist - mTerrainMesh = new MeshInterface(this, mTerrain); - mTerrainMesh->create(); + + // This was in MeshInterface(). + + 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() { - delete mTerrainMesh; - mTerrainMesh = NULL; + TRACE("destroyTerrain"); + 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 */ - inline bool hasMesh() const{ return mTerrainMesh != 0; } + inline bool hasMesh() const{ return mQuadData; } /** * @return true if there are any children @@ -294,27 +329,161 @@ public: 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; - return mTerrainMesh; + TRACE("Quad::update"); + 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: + + ///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; 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 Point2 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 @@ -323,5 +492,3 @@ private: bool mHasData; ///holds if there is terrain data about this quad int mMaxDepth; ///the maxmium depth. Cached. This is not valid is mDepth == 0 }; - -//const size_t Quad::NUM_CHILDREN = 4; diff --git a/terrain/cpp_quaddata.cpp b/terrain/cpp_quaddata.cpp index 20476e43f..18d37d906 100644 --- a/terrain/cpp_quaddata.cpp +++ b/terrain/cpp_quaddata.cpp @@ -1,76 +1,22 @@ /** - * @brief gets a material for the quad data - */ -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 + * 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 - * Normals and indicies to allow us to pass optoizied meshes + * This needs a rework, as really the mesh renderer should accept just + * a set of verts Normals and indicies to allow us to pass optimized + * meshes */ class QuadData { -public: - QuadData(TerrainHeightmap* p) - : mHeightmap(p) {} + typedef std::list ResourceList; + typedef std::list::const_iterator ResourceListCItr; - 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. @@ -93,8 +39,20 @@ public: 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& 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() { 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() { if ( mMaterial.isNull() ) @@ -112,10 +70,6 @@ public: assert(!mMaterial.isNull()); return mMaterial; } - QuadMaterialGenerator* getMaterialGenerator() - { - return mHeightmap->getMaterialGenerator(); - } /** * @brief gets the texture for the above quad @@ -130,12 +84,14 @@ public: mParentTexture = c; } -protected: +private: void createMaterial() { - mMaterial = getMaterialGenerator()->getMaterial(this); + assert(mTexture.length()); + + mMaterial = g_materialGen->generateSingleTexture(mTexture, mResources); } - TerrainHeightmap* mHeightmap; + Ogre::MaterialPtr mMaterial; std::string mParentTexture; @@ -143,43 +99,11 @@ protected: int mVertexSeperation; std::vector mHeights; std::vector mNormals; -}; - -class MWQuadData : public QuadData -{ -public: - typedef std::list ResourceList; - typedef std::list::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& getTextureIndexRef() - { return mTextureIndex; } - -private: ///Holds the resources used by the quad ResourceList mResources; - std::vector mTextureIndex; ///holds index that corespond the the palette + std::vector mTextureIndex; ///holds index that correspond to the palette std::string mTexture; ///The land texture. Mutally exclusive with the above 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); diff --git a/terrain/cpp_quadsegment.cpp b/terrain/cpp_quadsegment.cpp deleted file mode 100644 index e4c659456..000000000 --- a/terrain/cpp_quadsegment.cpp +++ /dev/null @@ -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(xgetVertex((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; -}; diff --git a/terrain/cpp_terrain.cpp b/terrain/cpp_terrain.cpp index 40d48211f..d0e3e9c33 100644 --- a/terrain/cpp_terrain.cpp +++ b/terrain/cpp_terrain.cpp @@ -43,13 +43,27 @@ const int LAND_NUM_VERTS = LAND_VERT_WIDTH*LAND_VERT_WIDTH; const int LAND_LTEX_WIDTH = 16; 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. #define ENABLED_CRASHING 0 class Quad; class QuadData; -class QuadSegment; class Terrain; +class MaterialGenerator; +class MWHeightmap; + +MWHeightmap *g_heightMap; +MaterialGenerator *g_materialGen; +Terrain *g_Terrain; + +#undef TRACE +#define TRACE(x) // Prerequisites #include @@ -66,33 +80,29 @@ class Terrain; #include // For generation +#include "cpp_materialgen.cpp" #include "cpp_esm.cpp" #include "cpp_landdata.cpp" #include "cpp_quaddata.cpp" -#include "cpp_point2.cpp" -#include "cpp_materialgen.cpp" #include "cpp_index.cpp" #include "cpp_palette.cpp" +#include "cpp_point2.cpp" #include "cpp_generator.cpp" // For rendering -#include "cpp_quadsegment.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 // need the header files first. #include "cpp_terrainmesh.h" -#include "cpp_meshinterface.h" #include "cpp_terraincls.h" #include "cpp_quad.cpp" #include "cpp_terraincls.cpp" #include "cpp_terrainmesh.cpp" -#include "cpp_meshinterface.cpp" -#include "cpp_mwheightmap.cpp" #include "cpp_framelistener.cpp" TerrainFrameListener terrainListener; @@ -102,6 +112,9 @@ extern "C" void d_superman(); // Set up the rendering system extern "C" void terr_setupRendering() { + if(!g_materialGen) + g_materialGen = new MaterialGenerator; + // Add the terrain directory ResourceGroupManager::getSingleton(). addResourceLocation(TEXTURE_OUTPUT, "FileSystem", "General"); @@ -116,7 +129,10 @@ extern "C" void terr_setupRendering() // Generate all cached data. extern "C" void terr_genData() -{ +{ + if(!g_materialGen) + g_materialGen = new MaterialGenerator; + Ogre::Root::getSingleton().renderOneFrame(); Generator mhm(TERRAIN_OUTPUT); diff --git a/terrain/cpp_terraincls.cpp b/terrain/cpp_terraincls.cpp index e8e35f5ed..ab033b3dd 100644 --- a/terrain/cpp_terraincls.cpp +++ b/terrain/cpp_terraincls.cpp @@ -1,10 +1,6 @@ -Terrain::Terrain(TerrainHeightmap* d, - Ogre::SceneNode* r) - : mTerrainData(d), - mTerrainSceneNode(r), +Terrain::Terrain(Ogre::SceneNode* r) + : mTerrainSceneNode(r), mQuadRoot(0), - mQuadCreateFunction(0), - mQuadDestroyFunction(0), mMorphingEnabled(true), mTextureFadingEnabled(true), mBaseLand(r) @@ -17,7 +13,7 @@ Terrain::~Terrain(){ } //---------------------------------------------- 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){ @@ -26,20 +22,14 @@ void Terrain::update(Ogre::Real t){ mBaseLand.update(); } //---------------------------------------------- -void Terrain::_quadCreated(QuadData* qd){ - if ( mQuadCreateFunction ) (*mQuadCreateFunction)(qd); -} -//---------------------------------------------- -void Terrain::_quadDestroyed(QuadData* qd){ - if ( mQuadDestroyFunction ) (*mQuadDestroyFunction)(qd); -} -//---------------------------------------------- int Terrain::getMaxDepth(){ - return mTerrainData->getMaxDepth(); + return g_heightMap->getMaxDepth(); } +/* //---------------------------------------------- void Terrain::reload(){ delete mQuadRoot; - mQuadRoot = new Quad(Quad::QL_ROOT, 0, this); + mQuadRoot = new Quad(Quad::QL_ROOT, 0); } //---------------------------------------------- +*/ diff --git a/terrain/cpp_terraincls.h b/terrain/cpp_terraincls.h index fd7f6bfdf..0a9df4f74 100644 --- a/terrain/cpp_terraincls.h +++ b/terrain/cpp_terraincls.h @@ -11,7 +11,7 @@ public: * The terrain is create in the constructor. * @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 */ @@ -29,7 +29,7 @@ public: * @remarks this is very slow, as it drops all created alpha maps and meshes. * @todo check this works */ - void reload(); + //void reload(); /** * @brief sets the scene node that all of the terrain nodes are based off @@ -51,37 +51,30 @@ public: */ 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 * @param qd the quad data. It is valid until the same variable is passed to _quadDestroyed - */ void _quadCreated(QuadData* qd); /** * @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 - */ void _quadDestroyed(QuadData* qd); /** * @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 - */ + inline void setQuadCreateFunction(void (*f)(QuadData*)){ mQuadCreateFunction = f; } /** * @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 - */ inline void setQuadDestroyFunction(void (*f)(QuadData*)){ mQuadDestroyFunction = f; } + */ /** * @brief time in seconds to morph to full detail after an unsplit. @@ -131,8 +124,6 @@ public: return mTextureFadingEnabled; } protected: - TerrainHeightmap* mTerrainData; - /// the scenenode that every other node is decended from. This /// should be surplied by the user Ogre::SceneNode* mTerrainSceneNode; @@ -140,10 +131,12 @@ protected: ///the root node for all the quads. Quad* mQuadRoot; + /* ///quad callback function void (*mQuadCreateFunction)(QuadData*); ///quad callback function void (*mQuadDestroyFunction)(QuadData*); + */ bool mMorphingEnabled; bool mTextureFadingEnabled; diff --git a/terrain/cpp_terrainmesh.cpp b/terrain/cpp_terrainmesh.cpp index 43ef218d0..1368228cc 100644 --- a/terrain/cpp_terrainmesh.cpp +++ b/terrain/cpp_terrainmesh.cpp @@ -1,359 +1,376 @@ -//---------------------------------------------- -TerrainRenderable::TerrainRenderable(Terrain* t, QuadSegment* qs,int width, int depth, bool skirts) : - Ogre::Renderable(), - Ogre::MovableObject(), - mWidth(width), - mUseSkirts(skirts), - mBuilt(false), - mDepth(depth), - mSegment(qs), - mTerrain(t), - mVertexes(0), - mIndices(0), - mLODMorphFactor(0), - mTextureFadeFactor(0), - mMin(30000), - mMax(-30000), - mExtraMorphAmount(0), - mHasFadePass(false) {} -//---------------------------------------------- -TerrainRenderable::~TerrainRenderable() { - destroy(); -} -//---------------------------------------------- -void TerrainRenderable::create(Ogre::SceneNode* sn) { - using namespace Ogre; - - if ( mBuilt ) return; - - createVertexBuffer(); - calculateVetexValues(); - calculateIndexValues(); - setBounds(); - - sn->attachObject(this); - - mMaterial = mSegment->getMaterial(); - - - - if ( mTerrain->isMorhpingEnabled() && mDepth != mTerrain->getMaxDepth() ) { - Ogre::Technique* tech = getMaterial()->getTechnique(0); - for ( size_t i = 0; i < tech->getNumPasses(); ++i ) { - assert(mTerrain->isMorhpingEnabled()); - tech->getPass(i)->setVertexProgram(MORPH_VERTEX_PROGRAM); - } +TerrainMesh::TerrainMesh(QuadData* qd, float segSize, float startX, float startY, + const Ogre::Vector3 &pos, + int width, int depth, bool skirts, + Ogre::SceneNode *parent) + : Ogre::Renderable(), + Ogre::MovableObject(), + mWidth(width), + mUseSkirts(skirts), + mBuilt(false), + mDepth(depth), + mVertexes(0), + mIndices(0), + mLODMorphFactor(0), + mTextureFadeFactor(0), + mMin(30000), + mMax(-30000), + mExtraMorphAmount(0), + mHasFadePass(false), + mQuadData(qd), + mSegmentSize(segSize), + mX(startX), + mY(startY) +{ + // From QuadSegment() + assert(qd); + assert(segSize>0&&segSize<=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*segSize; + const int isw = (int)fsw; + assert(fsw==isw); + } +#endif + + //precalc offsets, as getVertex/getNormal get called a lot (1000s of times) + computeOffsets(); + + // From Quad + 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() ) - calculateDeltaValues(); + if ( g_Terrain->isMorhpingEnabled() ) + calculateDeltaValues(); - mBuilt = true; + mBuilt = true; } -//---------------------------------------------- -void TerrainRenderable::destroy() { - if ( !mBuilt ) return; - //deleting null values is fine iirc - delete mIndices; +void TerrainMesh::destroy() { + if ( !mBuilt ) return; + + //deleting null values is fine iirc + delete mIndices; # if ENABLED_CRASHING == 1 - { - delete mVertexes; - } + { + delete mVertexes; + } # else - { - if ( mDepth != mTerrain->getMaxDepth() ){ - delete mVertexes; - } + { + if ( mDepth != g_Terrain->getMaxDepth() ){ + delete mVertexes; } + } # endif - mBuilt = false; + mBuilt = false; } //---------------------------------------------- -void TerrainRenderable::update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist) { - //if ( USE_MORPH ){ - - //as aprocesh mUnsplitDistance, lower detail - if ( camDist > morphDist && mDepth > 1 ) { - mLODMorphFactor = 1 - (usplitDist - camDist)/(usplitDist-morphDist); - } 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; - mExtraMorphAmount -= (time/mTerrain->getMorphSpeed()); //decrease slowly - } - if ( mExtraFadeAmount > 0 ) { - mTextureFadeFactor += mExtraFadeAmount; - mExtraFadeAmount -= (time/mTerrain->getTextureFadeSpeed()); - } - - //Ensure within valid bounds - if ( mLODMorphFactor > 1 ) - mLODMorphFactor = 1; - else if ( mLODMorphFactor < 0 ) - mLODMorphFactor = 0; - - if ( mTextureFadeFactor > 1 ) - mTextureFadeFactor = 1; - else if ( mTextureFadeFactor < 0 ) - mTextureFadeFactor = 0; +void TerrainMesh::update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist) +{ + TRACE("TerrainMesh::update"); + //if ( USE_MORPH ){ + + //as aprocesh mUnsplitDistance, lower detail + if ( camDist > morphDist && mDepth > 1 ) { + mLODMorphFactor = 1 - (usplitDist - camDist)/(usplitDist-morphDist); + } 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; + mExtraMorphAmount -= (time/g_Terrain->getMorphSpeed()); //decrease slowly + } + if ( mExtraFadeAmount > 0 ) { + mTextureFadeFactor += mExtraFadeAmount; + mExtraFadeAmount -= (time/g_Terrain->getTextureFadeSpeed()); + } + + //Ensure within valid bounds + if ( mLODMorphFactor > 1 ) + mLODMorphFactor = 1; + else if ( mLODMorphFactor < 0 ) + mLODMorphFactor = 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 - if ( mHasFadePass && mTextureFadeFactor == 0 ) { - removeFadePass(); - } else if ( mTerrain->isTextureFadingEnabled() && - !mHasFadePass && - mTextureFadeFactor > 0 && - mSegment->hasParentTexture() ) { - addFadePass(); - } + //remove pass. Keep outside in case terrain fading is removed while it is active + if ( mHasFadePass && mTextureFadeFactor == 0 ) { + removeFadePass(); + } else if ( g_Terrain->isTextureFadingEnabled() && + !mHasFadePass && + mTextureFadeFactor > 0 && + hasParentTexture() ) { + addFadePass(); + } } //---------------------------------------------- -void TerrainRenderable::addFadePass() { - assert(mHasFadePass==false); +void TerrainMesh::addFadePass() { + assert(mHasFadePass==false); - if ( mDepth == mTerrain->getMaxDepth() ) return; + if ( mDepth == g_Terrain->getMaxDepth() ) return; - mHasFadePass = true; - Ogre::MaterialPtr mat = getMaterial(); - Ogre::Pass* newPass = mat->getTechnique(0)->createPass(); - newPass->setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA); + mHasFadePass = true; + Ogre::MaterialPtr mat = getMaterial(); + Ogre::Pass* newPass = mat->getTechnique(0)->createPass(); + newPass->setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA); - //set fragment program - assert(mTerrain->isTextureFadingEnabled()); - newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM); + //set fragment program + assert(g_Terrain->isTextureFadingEnabled()); + newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM); - if ( mTerrain->isMorhpingEnabled() && mDepth != mTerrain->getMaxDepth() ) { - assert(mTerrain->isMorhpingEnabled()); - newPass->setVertexProgram(MORPH_VERTEX_PROGRAM); - } + if ( g_Terrain->isMorhpingEnabled() && mDepth != g_Terrain->getMaxDepth() ) { + assert(g_Terrain->isMorhpingEnabled()); + newPass->setVertexProgram(MORPH_VERTEX_PROGRAM); + } - //set texture to be used - newPass->createTextureUnitState(mSegment->getParentTexture(), 1); + //set texture to be used + newPass->createTextureUnitState(getParentTexture(), 1); } //---------------------------------------------- -void TerrainRenderable::removeFadePass() { - assert(mHasFadePass==true); - mHasFadePass = false; - Ogre::MaterialPtr mat = getMaterial(); - Ogre::Technique* tech = mat->getTechnique(0); +void TerrainMesh::removeFadePass() { + assert(mHasFadePass==true); + mHasFadePass = false; + Ogre::MaterialPtr mat = getMaterial(); + Ogre::Technique* tech = mat->getTechnique(0); - tech->removePass(tech->getNumPasses()-1); + tech->removePass(tech->getNumPasses()-1); } //---------------------------------------------- -void TerrainRenderable::justSplit() { - mExtraMorphAmount = 1; - mLODMorphFactor = 1; - mTextureFadeFactor = 1; - mExtraFadeAmount = 1; - - if ( mTerrain->isTextureFadingEnabled() && mSegment->hasParentTexture() ) - addFadePass(); +void TerrainMesh::justSplit() { + mExtraMorphAmount = 1; + mLODMorphFactor = 1; + mTextureFadeFactor = 1; + mExtraFadeAmount = 1; + + if ( g_Terrain->isTextureFadingEnabled() && hasParentTexture() ) + addFadePass(); } //---------------------------------------------- -void TerrainRenderable::_updateCustomGpuParameter( - const GpuProgramParameters::AutoConstantEntry& constantEntry, - GpuProgramParameters* params) const { - using namespace Ogre; - if (constantEntry.data == MORPH_CUSTOM_PARAM_ID) - params->_writeRawConstant(constantEntry.physicalIndex, mLODMorphFactor); - else if ( constantEntry.data == FADE_CUSTOM_PARAM_ID ) - params->_writeRawConstant(constantEntry.physicalIndex, mTextureFadeFactor); - else - Renderable::_updateCustomGpuParameter(constantEntry, params); +void TerrainMesh::_updateCustomGpuParameter( + const GpuProgramParameters::AutoConstantEntry& constantEntry, + GpuProgramParameters* params) const { + using namespace Ogre; + if (constantEntry.data == MORPH_CUSTOM_PARAM_ID) + params->_writeRawConstant(constantEntry.physicalIndex, mLODMorphFactor); + else if ( constantEntry.data == FADE_CUSTOM_PARAM_ID ) + params->_writeRawConstant(constantEntry.physicalIndex, mTextureFadeFactor); + else + Renderable::_updateCustomGpuParameter(constantEntry, params); } //---------------------------------------------- -float TerrainRenderable::getVertexHeight(int x, int y) { - return mSegment->getVertex(x,y); +float TerrainMesh::getVertexHeight(int x, int y) { + return getVertex(x,y); } //---------------------------------------------- -Ogre::Vector3 TerrainRenderable::getVertexPosition(int x, int y) { - return Ogre::Vector3(x*mSegment->getVertexSeperation(), getVertexHeight(x,y) , y*mSegment->getVertexSeperation()); +Ogre::Vector3 TerrainMesh::getVertexPosition(int x, int y) { + return Ogre::Vector3(x*getVertexSeperation(), getVertexHeight(x,y) , y*getVertexSeperation()); } //---------------------------------------------- -void TerrainRenderable::calculateVetexValues() { - using namespace Ogre; - - //get the texture offsets for the higher uv - float xUVOffset = 0; - float yUVOffset = 0; - - if ( mTerrain->isTextureFadingEnabled() ) { - assert(0); +void TerrainMesh::calculateVetexValues() { + using namespace Ogre; + + //get the texture offsets for the higher uv + float xUVOffset = 0; + float yUVOffset = 0; + + if ( g_Terrain->isTextureFadingEnabled() ) { + 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 end = mWidth; + int start = 0; + int end = mWidth; - if ( mUseSkirts ) { - --start; - ++end; - } + if ( mUseSkirts ) { + --start; + ++end; + } - float* verts = static_cast(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); - for ( int y = start; y < end; y++ ) { - for ( int x = start; x < end; x++ ) { + float* verts = static_cast(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); + for ( int y = start; y < end; y++ ) { + for ( int x = start; x < end; x++ ) { - //the skirts - if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) { + //the skirts + if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) { - if ( x < 0 ) *verts++ = 0; - else if ( x > (mWidth-1) ) *verts++ = (mWidth-1)*mSegment->getVertexSeperation(); - else *verts++ = x*mSegment->getVertexSeperation(); + if ( x < 0 ) *verts++ = 0; + else if ( x > (mWidth-1) ) *verts++ = (mWidth-1)*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; - else if ( y > (mWidth-1) ) *verts++ = (mWidth-1)*mSegment->getVertexSeperation(); - else *verts++ = y*mSegment->getVertexSeperation(); + if ( y < 0 ) *verts++ = 0; + else if ( y > (mWidth-1) ) *verts++ = (mWidth-1)*getVertexSeperation(); + else *verts++ = y*getVertexSeperation(); - for ( Ogre::uint i = 0; i < 3; i++ ) - *verts++ = 0; + for ( Ogre::uint i = 0; i < 3; i++ ) + *verts++ = 0; - float u = (float)(x) / (mWidth-1); - float v = (float)(y) / (mWidth-1); + float u = (float)(x) / (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++ = v; + *verts++ = u; + *verts++ = v; - if ( mTerrain->isTextureFadingEnabled() ) { - *verts++ = u; - *verts++ = v; - } - } else { + if ( g_Terrain->isTextureFadingEnabled() ) { + *verts++ = u; + *verts++ = v; + } + } else { - assert(y*mWidth+x>=0&&y*mWidth+x=0&&y*mWidth+xgetVertexSeperation(); - *verts++ = getVertexHeight(x,y); - *verts++ = y*mSegment->getVertexSeperation(); + //verts + *verts++ = x*getVertexSeperation(); + *verts++ = getVertexHeight(x,y); + *verts++ = y*getVertexSeperation(); - mMax = std::max(mMax, getVertexHeight(x,y)); - mMin = std::min(mMin, getVertexHeight(x,y)); + mMax = std::max(mMax, getVertexHeight(x,y)); + mMin = std::min(mMin, getVertexHeight(x,y)); - //normals - for ( Ogre::uint i = 0; i < 3; i++ ) - *verts++ = mSegment->getNormal(x,y,i); - //*verts++ = mInterface->getNormal((y*mWidth+x)*3+i); + //normals + for ( Ogre::uint i = 0; i < 3; i++ ) + *verts++ = getNormal(x,y,i); + //*verts++ = mInterface->getNormal((y*mWidth+x)*3+i); - const float u = (float)(x) / (mWidth-1); - const float v = (float)(y) / (mWidth-1); - assert(u>=0&&v>=0); - assert(u<=1&&v<=1); + const float u = (float)(x) / (mWidth-1); + const float v = (float)(y) / (mWidth-1); + assert(u>=0&&v>=0); + assert(u<=1&&v<=1); - *verts++ = u; - *verts++ = v; + *verts++ = u; + *verts++ = v; - if ( mTerrain->isTextureFadingEnabled() ) { - *verts++ = u/2.0f + xUVOffset; - *verts++ = v/2.0f + yUVOffset; - } - } + if ( g_Terrain->isTextureFadingEnabled() ) { + *verts++ = u/2.0f + xUVOffset; + *verts++ = v/2.0f + yUVOffset; } + } } - mMainBuffer->unlock(); + } + mMainBuffer->unlock(); } //---------------------------------------------- -void TerrainRenderable::setBounds() { - mBounds.setExtents(0,mMin,0, - (mWidth - 1) * mSegment->getVertexSeperation(), - mMax, - (mWidth - 1) * mSegment->getVertexSeperation()); +void TerrainMesh::setBounds() { + mBounds.setExtents(0,mMin,0, + (mWidth - 1) * getVertexSeperation(), + mMax, + (mWidth - 1) * getVertexSeperation()); - mCenter = Ogre::Vector3( ( (mWidth - 1) * mSegment->getVertexSeperation() ) / 2, - ( mMin + mMax ) / 2, - ( (mWidth - 1) * mSegment->getVertexSeperation() ) / 2); + mCenter = Ogre::Vector3( ( (mWidth - 1) * getVertexSeperation() ) / 2, + ( mMin + mMax ) / 2, + ( (mWidth - 1) * getVertexSeperation() ) / 2); - mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; + mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; } //---------------------------------------------- -void TerrainRenderable::createVertexBuffer() { - using namespace Ogre; +void TerrainMesh::createVertexBuffer() { + using namespace Ogre; - size_t vw = mWidth; - if ( mUseSkirts ) vw += 2; + size_t vw = mWidth; + if ( mUseSkirts ) vw += 2; - mVertexes = new VertexData(); - mVertexes->vertexStart = 0; - mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH; + mVertexes = new VertexData(); + mVertexes->vertexStart = 0; + mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH; - VertexDeclaration* vertexDecl = mVertexes->vertexDeclaration; - size_t currOffset = 0; + VertexDeclaration* vertexDecl = mVertexes->vertexDeclaration; + size_t currOffset = 0; - vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_POSITION); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); + vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_POSITION); + currOffset += VertexElement::getTypeSize(VET_FLOAT3); - vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_NORMAL); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); + vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_NORMAL); + currOffset += VertexElement::getTypeSize(VET_FLOAT3); - vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); - currOffset += VertexElement::getTypeSize(VET_FLOAT2); + vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); + currOffset += VertexElement::getTypeSize(VET_FLOAT2); - if ( mTerrain->isTextureFadingEnabled() ) { - vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 1); - currOffset += VertexElement::getTypeSize(VET_FLOAT2); - } + if ( g_Terrain->isTextureFadingEnabled() ) { + vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 1); + currOffset += VertexElement::getTypeSize(VET_FLOAT2); + } - mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( - vertexDecl->getVertexSize(0), // size of one whole vertex - mVertexes->vertexCount, // number of vertices - HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage - false); // no shadow buffer + mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( + vertexDecl->getVertexSize(0), // size of one whole vertex + mVertexes->vertexCount, // number of vertices + HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage + 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() ) - vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); + if ( g_Terrain->isMorhpingEnabled() ) + vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); } -Ogre::HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer( ) { - size_t vw = mWidth; - if ( mUseSkirts ) vw += 2; +Ogre::HardwareVertexBufferSharedPtr TerrainMesh::createDeltaBuffer( ) { + size_t vw = mWidth; + if ( mUseSkirts ) vw += 2; - const size_t totalVerts = (vw * vw); - return Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT1), - totalVerts, - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, - false); //no shadow buff + const size_t totalVerts = (vw * vw); + return Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT1), + totalVerts, + Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, + false); //no shadow buff } @@ -364,115 +381,115 @@ Ogre::HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer( ) { #define SET_DELTA_AT(x, y, v) \ if ( mUseSkirts ) pDeltas[( y + 1)*vw+ x + 1] = v; \ else pDeltas[( y)*vw+ x] = v; -void TerrainRenderable::calculateDeltaValues() { +void TerrainMesh::calculateDeltaValues() { - using namespace Ogre; - size_t vw = mWidth; - if ( mUseSkirts ) vw += 2; + using namespace Ogre; + size_t vw = mWidth; + if ( mUseSkirts ) vw += 2; - //must be using morphing to use this function - assert(mTerrain->isMorhpingEnabled()); + //must be using morphing to use this function + assert(g_Terrain->isMorhpingEnabled()); - const size_t step = 2; + const size_t step = 2; - // Create a set of delta values - mDeltaBuffer = createDeltaBuffer(); - float* pDeltas = static_cast(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD)); - memset(pDeltas, 0, (vw)*(vw) * sizeof(float)); + // Create a set of delta values + mDeltaBuffer = createDeltaBuffer(); + float* pDeltas = static_cast(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD)); + memset(pDeltas, 0, (vw)*(vw) * sizeof(float)); - return; + return; - bool flag=false; - for ( size_t y = 0; y < mWidth-step; y += step ) { - for ( size_t x = 0; x < mWidth-step; x += step ) { - //create the diffrence between the full vertex if the vertex wasn't their + bool flag=false; + for ( size_t y = 0; y < mWidth-step; y += step ) { + for ( size_t x = 0; x < mWidth-step; x += step ) { + //create the diffrence between the full vertex if the vertex wasn't their - float bottom_left = getVertexHeight(x,y); - float bottom_right = getVertexHeight(x+step,y); + float bottom_left = getVertexHeight(x,y); + float bottom_right = getVertexHeight(x+step,y); - float top_left = getVertexHeight(x,y+step); - float top_right = getVertexHeight(x+step,y+step); + float top_left = getVertexHeight(x,y+step); + float top_right = getVertexHeight(x+step,y+step); - //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+2, y+1, (bottom_right+top_right)/2 - getVertexHeight(x+2, y+1)) //right + //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+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, (bottom_right+bottom_left)/2 - getVertexHeight(x+1, y)) //bottom + 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 - //this might not be correct - if ( !flag ) - SET_DELTA_AT(x+1, y+1, (bottom_left+top_right)/2 - getVertexHeight(x+1, y+1)) //center - else - SET_DELTA_AT(x+1, y+1, (bottom_right+top_left)/2 - getVertexHeight(x+1, y+1)) //center + //this might not be correct + if ( !flag ) + SET_DELTA_AT(x+1, y+1, (bottom_left+top_right)/2 - getVertexHeight(x+1, y+1)) //center + else + SET_DELTA_AT(x+1, y+1, (bottom_right+top_left)/2 - getVertexHeight(x+1, y+1)) //center - flag = !flag; - } - flag = !flag; //flip tries for next row + flag = !flag; } + flag = !flag; //flip tries for next row + } - mDeltaBuffer->unlock(); - mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer); + mDeltaBuffer->unlock(); + mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer); } #undef SET_DELTA_AT //---------------------------------------------- -void TerrainRenderable::calculateIndexValues() { - using namespace Ogre; +void TerrainMesh::calculateIndexValues() { + using namespace Ogre; - size_t vw = mWidth-1; - if ( mUseSkirts ) vw += 2; + size_t vw = mWidth-1; + 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 - assert(mIndices==0); + //need to manage allocation if not null + assert(mIndices==0); - mIndices = new IndexData(); - mIndices->indexCount = indexCount; - mIndices->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer( - HardwareIndexBuffer::IT_16BIT, - indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); + mIndices = new IndexData(); + mIndices->indexCount = indexCount; + mIndices->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer( + HardwareIndexBuffer::IT_16BIT, + indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); - unsigned short* indices = static_cast(mIndices->indexBuffer->lock(0, - mIndices->indexBuffer->getSizeInBytes(), - HardwareBuffer::HBL_DISCARD)); + unsigned short* indices = static_cast(mIndices->indexBuffer->lock(0, + mIndices->indexBuffer->getSizeInBytes(), + HardwareBuffer::HBL_DISCARD)); - bool flag = false; - Ogre::uint indNum = 0; - for ( Ogre::uint y = 0; y < (vw); y+=1 ) { - for ( Ogre::uint x = 0; x < (vw); x+=1 ) { + bool flag = false; + Ogre::uint indNum = 0; + for ( Ogre::uint y = 0; y < (vw); y+=1 ) { + for ( Ogre::uint x = 0; x < (vw); x+=1 ) { - const int line1 = y * (vw+1) + x; - const int line2 = (y + 1) * (vw+1) + x; + const int line1 = y * (vw+1) + x; + const int line2 = (y + 1) * (vw+1) + x; - if ( flag ) { - *indices++ = line1; - *indices++ = line2; - *indices++ = line1 + 1; + if ( flag ) { + *indices++ = line1; + *indices++ = line2; + *indices++ = line1 + 1; - *indices++ = line1 + 1; - *indices++ = line2; - *indices++ = line2 + 1; - } else { - *indices++ = line1; - *indices++ = line2; - *indices++ = line2 + 1; + *indices++ = line1 + 1; + *indices++ = line2; + *indices++ = line2 + 1; + } else { + *indices++ = line1; + *indices++ = line2; + *indices++ = line2 + 1; - *indices++ = line1; - *indices++ = line2 + 1; - *indices++ = line1 + 1; - } - flag = !flag; //flip tris for next time + *indices++ = line1; + *indices++ = line2 + 1; + *indices++ = line1 + 1; + } + flag = !flag; //flip tris for next time - indNum+=6; - } - flag = !flag; //flip tries for next row + indNum+=6; } - assert(indNum==indexCount); - mIndices->indexBuffer->unlock(); - //return mIndices; + flag = !flag; //flip tries for next row + } + assert(indNum==indexCount); + mIndices->indexBuffer->unlock(); + //return mIndices; } diff --git a/terrain/cpp_terrainmesh.h b/terrain/cpp_terrainmesh.h index 245cc8432..03ec0610f 100644 --- a/terrain/cpp_terrainmesh.h +++ b/terrain/cpp_terrainmesh.h @@ -1,229 +1,343 @@ /** -* @brief Terrain for one cell. Handles building, destroying and LOD morphing -*/ -class TerrainRenderable : public Ogre::Renderable, public Ogre::MovableObject { -public: - - explicit TerrainRenderable(Terrain* t, QuadSegment* qs, int width, int depth, bool skirts); - - /** - * Calls destroy(); - */ - ~TerrainRenderable(); - - /** - * Creates the actual mesh, it has to be called manually, as it is not called in the constructor - */ - void create(Ogre::SceneNode* sn) ; - - /** - * Can be called manually and can be called from the destuctor. This destroyes the mesh - */ - 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); + * @brief Terrain for one cell. Handles building, destroying and LOD morphing + */ +#define QSDEBUG +class TerrainMesh : public Ogre::Renderable, public Ogre::MovableObject +{ + public: + + TerrainMesh(QuadData* qd, float segSize, float startX, float startY, + const Ogre::Vector3 &pos, + int width, int depth, bool skirts, + Ogre::SceneNode *parent); + + ~TerrainMesh() + { + destroy(); + + assert(node); + + node->detachAllObjects(); + node->getCreator()->destroySceneNode(node); } - 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; } - //----------------------------------------------------------------------- - void getRenderOperation( Ogre::RenderOperation& op ) { - op.useIndexes = true; - op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; - op.vertexData = mVertexes; - op.indexData = mIndices; + return mLightList; + } + //----------------------------------------------------------------------- + virtual const Ogre::String& getMovableType( void ) const { + static Ogre::String t = "MW_TERRAIN"; + 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_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; - } - //----------------------------------------------------------------------- - virtual const Ogre::String& getMovableType( void ) const { - static Ogre::String t = "MW_TERRAIN"; - return t; - } - //----------------------------------------------------------------------- - void _updateRenderQueue( Ogre::RenderQueue* queue ) { - mLightListDirty = true; - queue->addRenderable(this, mRenderQueueID); +#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); + } + + private: + + void createMaterial() + { + assert(mSegmentSize>0); + if ( mSegmentSize == 1 ) //we can use the top level material + { + mMaterial = mQuadData->getMaterial(); + return; + } + + if ( mQuadData->getTexture().length() ) + assert(0&&"NOT IMPLEMENTED"); + + const std::vector& tref = mQuadData->getTextureIndexRef(); + const int indexSize = sqrt(tref.size()); + const int cellIndexSize = indexSize - 2; + + //plus 1 to take into account border + const int xoff = float(cellIndexSize) * mX; + const int yoff = float(cellIndexSize) * mY; + const int size = float(cellIndexSize) * mSegmentSize; + + std::vector 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)); + } } - const Ogre::AxisAlignedBox& getBoundingBox( void ) const { - return mBounds; - }; - //----------------------------------------------------------------------- - Ogre::Real getBoundingRadius(void) const { - return mBoundingRadius; + mMaterial = g_materialGen->getAlphaMat + (ti,size, + 1, 1.0f/size, + mQuadData->getUsedResourcesRef()); + } + + 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); } - //----------------------------------------------------------------------- - - /** - * @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; - } - -private: - - /** - * @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(); - - /** - * @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; +#endif + return float((mQuadData->getVertexWidth()-1))*x; + } + + void computeOffsets() + { + mXOffset = computeOffset(mX); + mYOffset = computeOffset(mY); + } + + QuadData* mQuadData; + float mSegmentSize; + float mX, mY; + int mXOffset, mYOffset; + + /** + * @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; }; diff --git a/terrain/terrain.d b/terrain/terrain.d index c1f1e8afa..46d96a1a1 100644 --- a/terrain/terrain.d +++ b/terrain/terrain.d @@ -55,11 +55,13 @@ void makePath(char[] pt) makeDir(pt); } -void initTerrain() +void initTerrain(bool doGen) { makePath("cache/terrain"); - //terr_genData(); + if(doGen) + terr_genData(); + terr_setupRendering(); } diff --git a/util/dbg.h b/util/dbg.h index 4498cd32f..422dfe70c 100644 --- a/util/dbg.h +++ b/util/dbg.h @@ -13,4 +13,4 @@ class MTrace { dbg_untrace(); } }; -#define TRACE(x) MTrace _trc(x); +#define TRACE(x) MTrace __trc(x);