diff --git a/Makefile b/Makefile index 51571c7c89..5a4e348ff9 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 mwheightmap palette point2\ -quad quaddata terraincls terrain terrainmesh +materialgen heightmap palette point2\ +quad quaddata terrain terrainmesh terrainmesh2 heightmap2 # FFmpeg files, in the form sound/cpp_X.cpp. avcodec_cpp=avcodec diff --git a/terrain/cpp_framelistener.cpp b/terrain/cpp_framelistener.cpp index 75c417c4ec..9323fb97b7 100644 --- a/terrain/cpp_framelistener.cpp +++ b/terrain/cpp_framelistener.cpp @@ -6,7 +6,7 @@ protected: */ bool frameEnded(const FrameEvent& evt) { - g_Terrain->update(evt.timeSinceLastFrame); + g_heightMap->update(evt.timeSinceLastFrame); return true; } @@ -16,19 +16,20 @@ public: // Add the frame listener mRoot->addFrameListener(this); - //our derived heightmap - g_heightMap = new MWHeightmap(); + // Create a root scene node first + Ogre::SceneNode *node = mSceneMgr->getRootSceneNode() + ->createChildSceneNode("TERRAIN_ROOT"); + + // The main terrain object + g_heightMap = new HeightMap(node); g_heightMap->load(TERRAIN_OUTPUT); - //setup terrain - g_Terrain = new Terrain(mSceneMgr->getRootSceneNode()->createChildSceneNode("TERRAIN_ROOT")); //root scene node - //fix settings - g_Terrain->setMorphingEnabled(false); - g_Terrain->setTextureFadingEnabled(false); + g_heightMap->setMorphingEnabled(false); + g_heightMap->setTextureFadingEnabled(false); //create the quad node - g_Terrain->create(); + g_heightMap->create(); } /* KILLME diff --git a/terrain/cpp_mwheightmap.cpp b/terrain/cpp_heightmap.cpp similarity index 57% rename from terrain/cpp_mwheightmap.cpp rename to terrain/cpp_heightmap.cpp index 8b74488b90..2bc5844e45 100644 --- a/terrain/cpp_mwheightmap.cpp +++ b/terrain/cpp_heightmap.cpp @@ -1,11 +1,10 @@ -/** - * @brief the class that deals with loading quads from the disk @todo a - * major improvment would be to store the data as a quad tree. It might - * improve lookup times. Then again. Might not - */ -class MWHeightmap +class HeightMap { public: + HeightMap(Ogre::SceneNode* r); + + ~HeightMap(); + /** * loads the quad data from the disk */ @@ -55,12 +54,41 @@ public: return true; } -private: + void create(); + + inline Ogre::SceneNode* getTerrainSceneNode(){return mTerrainSceneNode;} + + void update(Ogre::Real t); + + inline Ogre::Real getMorphSpeed(){return 1.0f;} + inline Ogre::Real getTextureFadeSpeed(){ return 2.0f;} + inline void setMorphingEnabled(bool enabled){ + mMorphingEnabled = enabled; + } + inline bool isMorphingEnabled() const{ + return mMorphingEnabled; + } + inline void setTextureFadingEnabled(bool enabled){ + if ( enabled && !mMorphingEnabled ) + OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "Cannot have fading but not morphing active", "Terrain::setTextureFadingEnabled"); + mTextureFadingEnabled = enabled; + } + + inline bool isTextureFadingEnabled() const{ + return mTextureFadingEnabled; + } + + private: + Ogre::SceneNode* mTerrainSceneNode; + Quad* mQuadRoot; + bool mMorphingEnabled; + bool mTextureFadingEnabled; + + BaseLand mBaseLand; ///ifs for the data file. Opned on load std::ifstream mDataIFS; ///holds the offsets of the quads Index mIndex; TexturePalette mPalette; - }; diff --git a/terrain/cpp_heightmap2.cpp b/terrain/cpp_heightmap2.cpp new file mode 100644 index 0000000000..bae9809a1d --- /dev/null +++ b/terrain/cpp_heightmap2.cpp @@ -0,0 +1,24 @@ +HeightMap::HeightMap(Ogre::SceneNode* r) + : mTerrainSceneNode(r), + mQuadRoot(0), + mMorphingEnabled(true), + mTextureFadingEnabled(true), + mBaseLand(r) +{ + +} +//---------------------------------------------- +HeightMap::~HeightMap(){ + delete mQuadRoot; +} +//---------------------------------------------- +void HeightMap::create(){ + mQuadRoot = new Quad(Quad::QL_ROOT, 0); +} +//---------------------------------------------- +void HeightMap::update(Ogre::Real t){ + assert(mQuadRoot); + mQuadRoot->update(t); + mBaseLand.update(); +} + diff --git a/terrain/cpp_quad.cpp b/terrain/cpp_quad.cpp index 4f6ac19662..7bc514d919 100644 --- a/terrain/cpp_quad.cpp +++ b/terrain/cpp_quad.cpp @@ -19,7 +19,6 @@ */ class Quad { - enum SplitState { SS_NONE, SS_SPLIT, SS_UNSPLIT }; /** * when each quad is split, the children can be one of 4 places, @@ -226,11 +225,11 @@ public: const Ogre::Vector3 pos(qx - mSideLength/2, 0,qy - mSideLength/2); - mSceneNode = g_Terrain->getTerrainSceneNode()->createChildSceneNode(pos); + mSceneNode = g_heightMap->getTerrainSceneNode()->createChildSceneNode(pos); // This was in create() - if ( mDepth == g_Terrain->getMaxDepth() ) + if ( mDepth == g_heightMap->getMaxDepth() ) for ( int y = 0; y < 4; ++y ) for ( int x = 0; x < 4; ++x ) { @@ -265,7 +264,7 @@ public: mSceneNode->removeAndDestroyAllChildren(); mSceneMgr->destroySceneNode(mSceneNode); - g_Terrain->getTerrainSceneNode()->detachAllObjects(); + g_heightMap->getTerrainSceneNode()->detachAllObjects(); delete mQuadData; mQuadData = NULL; diff --git a/terrain/cpp_quaddata.cpp b/terrain/cpp_quaddata.cpp index 18d37d9064..15741f060e 100644 --- a/terrain/cpp_quaddata.cpp +++ b/terrain/cpp_quaddata.cpp @@ -5,6 +5,8 @@ * a set of verts Normals and indicies to allow us to pass optimized * meshes */ +enum SplitState { SS_NONE, SS_SPLIT, SS_UNSPLIT }; + class QuadData { typedef std::list<Ogre::ResourcePtr> ResourceList; diff --git a/terrain/cpp_terrain.cpp b/terrain/cpp_terrain.cpp index d0e3e9c339..0bb8fd668b 100644 --- a/terrain/cpp_terrain.cpp +++ b/terrain/cpp_terrain.cpp @@ -54,13 +54,11 @@ const float UNSPLIT_FACTOR = 2.0; class Quad; class QuadData; -class Terrain; class MaterialGenerator; -class MWHeightmap; +class HeightMap; -MWHeightmap *g_heightMap; +HeightMap *g_heightMap; MaterialGenerator *g_materialGen; -Terrain *g_Terrain; #undef TRACE #define TRACE(x) @@ -91,17 +89,14 @@ Terrain *g_Terrain; // For rendering #include "cpp_baseland.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_terraincls.h" - -#include "cpp_quad.cpp" - -#include "cpp_terraincls.cpp" +#include "cpp_heightmap.cpp" #include "cpp_terrainmesh.cpp" +#include "cpp_quad.cpp" +#include "cpp_heightmap2.cpp" +#include "cpp_terrainmesh2.cpp" #include "cpp_framelistener.cpp" diff --git a/terrain/cpp_terraincls.cpp b/terrain/cpp_terraincls.cpp deleted file mode 100644 index ab033b3ddb..0000000000 --- a/terrain/cpp_terraincls.cpp +++ /dev/null @@ -1,35 +0,0 @@ -Terrain::Terrain(Ogre::SceneNode* r) - : mTerrainSceneNode(r), - mQuadRoot(0), - mMorphingEnabled(true), - mTextureFadingEnabled(true), - mBaseLand(r) -{ - -} -//---------------------------------------------- -Terrain::~Terrain(){ - delete mQuadRoot; -} -//---------------------------------------------- -void Terrain::create(){ - mQuadRoot = new Quad(Quad::QL_ROOT, 0); -} -//---------------------------------------------- -void Terrain::update(Ogre::Real t){ - assert(mQuadRoot); - mQuadRoot->update(t); - mBaseLand.update(); -} -//---------------------------------------------- -int Terrain::getMaxDepth(){ - return g_heightMap->getMaxDepth(); -} -/* -//---------------------------------------------- -void Terrain::reload(){ - delete mQuadRoot; - mQuadRoot = new Quad(Quad::QL_ROOT, 0); -} -//---------------------------------------------- -*/ diff --git a/terrain/cpp_terraincls.h b/terrain/cpp_terraincls.h deleted file mode 100644 index 0a9df4f741..0000000000 --- a/terrain/cpp_terraincls.h +++ /dev/null @@ -1,145 +0,0 @@ -class Terrain -{ -public: - /** - * @brief inits vars and creates quad tree - * @param d the heightmap that contains the data - * @param s the current scne manager - * @param r the root scene node that all terrain nodes are barsed off - * @param c the camera that the poistion data is taken from - * - * The terrain is create in the constructor. - * @todo change quad root creation to create/destroy funcs? - */ - explicit Terrain(Ogre::SceneNode* r); - /** - * @brief deletes the quad tree - */ - ~Terrain(); - - /** - * @brief creates the quad tree - * @remarks This must be called before any call to upate(). We cannot call it from the constructor as options - * may not have been set yet - */ - void create(); - - /** - * @brief recreates the quad tree by destroying everything and starting again. - * @remarks this is very slow, as it drops all created alpha maps and meshes. - * @todo check this works - */ - //void reload(); - - /** - * @brief sets the scene node that all of the terrain nodes are based off - * @param r the scene node to use for terrain - */ - inline void setTerrainSceneNode(Ogre::SceneNode* r){ mTerrainSceneNode = r; } - - /** - * @return the scene node that should be used as the root of the terrain - * - * This is mainly used by the quad tree - */ - inline Ogre::SceneNode* getTerrainSceneNode(){return mTerrainSceneNode;} - - /** - * @brief updates the quad tree, splittig and unsplitting where needed. - * - * There is no requirement for this to be called every frame - */ - void update(Ogre::Real t); - - /** - * @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. - */ - inline Ogre::Real getMorphSpeed(){return 1.0f;} - /** - * @brief the time in seconds it takes for the texture to fade to the higher detail - */ - inline Ogre::Real getTextureFadeSpeed(){ return 2.0f;} - - /** - * @return the maximum depth of the quad tree - * @remarks this is used by the terrain renderable for adding morhping to textures. It doesn't want to happen - * on the lowest detail - */ - int getMaxDepth(); - - /** - * @brief disables/enables morphing - * @remarks This will not happen instantly unless reload() is called - */ - - inline void setMorphingEnabled(bool enabled){ - mMorphingEnabled = enabled; - - } - /** - * @return true if morphing is enabled - */ - inline bool isMorhpingEnabled() const{ - return mMorphingEnabled; - } - - /** - * @brief disables/enables texture fading. - * @remarks This will not happen instantly unless reload() is called. Morphing is required atm - */ - inline void setTextureFadingEnabled(bool enabled){ - if ( enabled && !mMorphingEnabled ) - OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "Cannot have fading but not morphing active", "Terrain::setTextureFadingEnabled"); - mTextureFadingEnabled = enabled; - } - /** - * @return true if texture fading is enabled - */ - inline bool isTextureFadingEnabled() const{ - return mTextureFadingEnabled; - } -protected: - /// the scenenode that every other node is decended from. This - /// should be surplied by the user - Ogre::SceneNode* mTerrainSceneNode; - - ///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; - - BaseLand mBaseLand; -}; diff --git a/terrain/cpp_terrainmesh.cpp b/terrain/cpp_terrainmesh.cpp index 1368228ccd..03ec0610fc 100644 --- a/terrain/cpp_terrainmesh.cpp +++ b/terrain/cpp_terrainmesh.cpp @@ -1,495 +1,343 @@ -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) +/** + * @brief Terrain for one cell. Handles building, destroying and LOD morphing + */ +#define QSDEBUG +class TerrainMesh : public Ogre::Renderable, public Ogre::MovableObject { - // From QuadSegment() - assert(qd); - assert(segSize>0&&segSize<=1); - assert(mY>=0&&mY<=1); - assert(mX>=0&&mY<=1); + 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); + } + + /** + * Can be called manually and can be called from the destuctor. This + * destroyes the mesh + */ + // FIXME: We don't need this as a separate function. + void destroy(); + + /** + * @brief Checks if it needs to be split or unsplit and deals with + * the morph factor. time seconds since last frame + */ + void update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist); + + /** + * @todo Needs to work out what this does (if it does what it is meant to) + */ + void visitRenderables(Renderable::Visitor* visitor, + bool debugRenderables = false) { + visitor->visit(this, 0, false); + } + + virtual const Ogre::MaterialPtr& getMaterial( void ) const { + return mMaterial; + } + //----------------------------------------------------------------------- + void getRenderOperation( Ogre::RenderOperation& op ) { + op.useIndexes = true; + op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; + op.vertexData = mVertexes; + op.indexData = mIndices; + } + + //----------------------------------------------------------------------- + void getWorldTransforms( Ogre::Matrix4* xform ) const { + *xform = mParentNode->_getFullTransform(); + } + //----------------------------------------------------------------------- + const Ogre::Quaternion& getWorldOrientation(void) const { + return mParentNode->_getDerivedOrientation(); + } + //----------------------------------------------------------------------- + const Ogre::Vector3& getWorldPosition(void) const { + return mParentNode->_getDerivedPosition(); + } + //----------------------------------------------------------------------- + + Ogre::Real getSquaredViewDepth(const Ogre::Camera *cam) const { + Ogre::Vector3 diff = mCenter - cam->getDerivedPosition(); + // Use squared length to avoid square root + return diff.squaredLength(); + } + //----------------------------------------------------------------------- + + const Ogre::LightList& getLights(void) const { + if (mLightListDirty) { + getParentSceneNode()->getCreator()->_populateLightList( + mCenter, this->getBoundingRadius(), mLightList); + mLightListDirty = false; + } + return mLightList; + } + //----------------------------------------------------------------------- + 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 - { - //check sizes - const float qw = mQuadData->getVertexWidth()-1; - const float fsw = qw*segSize; - const int isw = (int)fsw; - assert(fsw==isw); - } + { + const int vw = getVertexWidth(); + assert(x<vw); + } #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); - } + return mQuadData->getVertex((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x)); } - if ( g_Terrain->isMorhpingEnabled() ) - calculateDeltaValues(); - - mBuilt = true; -} - -void TerrainMesh::destroy() { - if ( !mBuilt ) return; - - //deleting null values is fine iirc - delete mIndices; - -# if ENABLED_CRASHING == 1 + float getNormal(int x, int y, int z) { - delete mVertexes; + assert(z>=0&&z<3); + return mQuadData->getNormal(((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x))*3+z); } -# else + + private: + + void createMaterial() { - if ( mDepth != g_Terrain->getMaxDepth() ){ - delete mVertexes; + 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<int>& 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<int> ti; + + ti.resize((size+2)*(size+2), -1); + + for ( int y = 0; y < size+2; ++y ){ + for ( int x = 0; x < size+2; ++x ){ + ti[(y)*(size+2)+(x)] = tref.at((y+yoff)*(indexSize)+(x+xoff)); + } } - } -# endif - mBuilt = false; -} -//---------------------------------------------- -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()); + mMaterial = g_materialGen->getAlphaMat + (ti,size, + 1, 1.0f/size, + mQuadData->getUsedResourcesRef()); } - //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 ( g_Terrain->isTextureFadingEnabled() && - !mHasFadePass && - mTextureFadeFactor > 0 && - hasParentTexture() ) { - addFadePass(); + inline bool hasParentTexture() const{ + return mQuadData->hasParentTexture(); + } + inline const std::string& getParentTexture() const{ + return mQuadData->getParentTexture(); + } + inline int getVertexSeperation(){ + return mQuadData->getVertexSeperation(); } -} -//---------------------------------------------- -void TerrainMesh::addFadePass() { - assert(mHasFadePass==false); - - 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); - - //set fragment program - assert(g_Terrain->isTextureFadingEnabled()); - newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM); - - if ( g_Terrain->isMorhpingEnabled() && mDepth != g_Terrain->getMaxDepth() ) { - assert(g_Terrain->isMorhpingEnabled()); - newPass->setVertexProgram(MORPH_VERTEX_PROGRAM); + 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(); - //set texture to be used - newPass->createTextureUnitState(getParentTexture(), 1); -} -//---------------------------------------------- -void TerrainMesh::removeFadePass() { - assert(mHasFadePass==true); - mHasFadePass = false; - Ogre::MaterialPtr mat = getMaterial(); - Ogre::Technique* tech = mat->getTechnique(0); + /** + * @return the height at the given vertex + */ + float getVertexHeight(int x, int y); - tech->removePass(tech->getNumPasses()-1); -} -//---------------------------------------------- -void TerrainMesh::justSplit() { - mExtraMorphAmount = 1; - mLODMorphFactor = 1; - mTextureFadeFactor = 1; - mExtraFadeAmount = 1; + /** + * Inits the vertex stuff + */ + void createVertexBuffer(); - if ( g_Terrain->isTextureFadingEnabled() && hasParentTexture() ) - addFadePass(); -} + /** + * @brief fills the vertex buffer with data + * @todo I don't think tex co-ords are right + */ + void calculateVetexValues(); -//---------------------------------------------- -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); + /** + * @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); -} -//---------------------------------------------- -float TerrainMesh::getVertexHeight(int x, int y) { - return getVertex(x,y); -} -//---------------------------------------------- -Ogre::Vector3 TerrainMesh::getVertexPosition(int x, int y) { - return Ogre::Vector3(x*getVertexSeperation(), getVertexHeight(x,y) , y*getVertexSeperation()); -} -//---------------------------------------------- -void TerrainMesh::calculateVetexValues() { - using namespace Ogre; + /** + * @brief gets the indicies for the vertex data. + */ + void calculateIndexValues(); - //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; + * 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); } - */ - - int start = 0; - int end = mWidth; - - if ( mUseSkirts ) { - --start; - ++end; +#endif + return float((mQuadData->getVertexWidth()-1))*x; } - float* verts = static_cast<float*>(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) ) { - - 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 - - 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; - - float u = (float)(x) / (mWidth-1); - float v = (float)(y) / (mWidth-1); - - //clamped, so shouldn't matter if > 1 - - *verts++ = u; - *verts++ = v; - - if ( g_Terrain->isTextureFadingEnabled() ) { - *verts++ = u; - *verts++ = v; - } - } else { - - assert(y*mWidth+x>=0&&y*mWidth+x<mWidth*mWidth); - - //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)); - - //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); - - *verts++ = u; - *verts++ = v; - - if ( g_Terrain->isTextureFadingEnabled() ) { - *verts++ = u/2.0f + xUVOffset; - *verts++ = v/2.0f + yUVOffset; - } - } - } - } - mMainBuffer->unlock(); -} -//---------------------------------------------- -void TerrainMesh::setBounds() { - mBounds.setExtents(0,mMin,0, - (mWidth - 1) * getVertexSeperation(), - mMax, - (mWidth - 1) * getVertexSeperation()); - - mCenter = Ogre::Vector3( ( (mWidth - 1) * getVertexSeperation() ) / 2, - ( mMin + mMax ) / 2, - ( (mWidth - 1) * getVertexSeperation() ) / 2); - - mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; -} -//---------------------------------------------- -void TerrainMesh::createVertexBuffer() { - using namespace Ogre; - - size_t vw = mWidth; - if ( mUseSkirts ) vw += 2; - - mVertexes = new VertexData(); - mVertexes->vertexStart = 0; - mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH; - - 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_NORMAL); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); - - - vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); - 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); + void computeOffsets() + { + mXOffset = computeOffset(mX); + mYOffset = computeOffset(mY); } - 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 + QuadData* mQuadData; + float mSegmentSize; + float mX, mY; + int mXOffset, mYOffset; - mVertexes->vertexBufferBinding->setBinding(MAIN_BINDING, mMainBuffer); //bind the data + /** + * @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; - if ( g_Terrain->isMorhpingEnabled() ) - vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); + /** + * 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; -} - -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 - -} - - - - -//---------------------------------------------- -#define SET_DELTA_AT(x, y, v) \ -if ( mUseSkirts ) pDeltas[( y + 1)*vw+ x + 1] = v; \ -else pDeltas[( y)*vw+ x] = v; -void TerrainMesh::calculateDeltaValues() { - - using namespace Ogre; - size_t vw = mWidth; - if ( mUseSkirts ) vw += 2; - - //must be using morphing to use this function - assert(g_Terrain->isMorhpingEnabled()); - - const size_t step = 2; - - // Create a set of delta values - mDeltaBuffer = createDeltaBuffer(); - float* pDeltas = static_cast<float*>(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD)); - memset(pDeltas, 0, (vw)*(vw) * sizeof(float)); - - 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 - - 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); - - //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 - - //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 - } - - mDeltaBuffer->unlock(); - mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer); - -} -#undef SET_DELTA_AT - -//---------------------------------------------- -void TerrainMesh::calculateIndexValues() { - using namespace Ogre; - - - - size_t vw = mWidth-1; - if ( mUseSkirts ) vw += 2; - - const size_t indexCount = (vw)*(vw)*6; - - //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); - - unsigned short* indices = static_cast<unsigned short*>(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 ) { - - const int line1 = y * (vw+1) + x; - const int line2 = (y + 1) * (vw+1) + x; - - 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; - *indices++ = line2 + 1; - *indices++ = line1 + 1; - } - flag = !flag; //flip tris for next time - - indNum+=6; - } - flag = !flag; //flip tries for next row - } - assert(indNum==indexCount); - mIndices->indexBuffer->unlock(); - //return mIndices; -} + //Terrain bindings + static const int MAIN_BINDING = 0; + static const int DELTA_BINDING = 1; +}; diff --git a/terrain/cpp_terrainmesh.h b/terrain/cpp_terrainmesh.h deleted file mode 100644 index 03ec0610fc..0000000000 --- a/terrain/cpp_terrainmesh.h +++ /dev/null @@ -1,343 +0,0 @@ -/** - * @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); - } - - /** - * Can be called manually and can be called from the destuctor. This - * destroyes the mesh - */ - // FIXME: We don't need this as a separate function. - void destroy(); - - /** - * @brief Checks if it needs to be split or unsplit and deals with - * the morph factor. time seconds since last frame - */ - void update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist); - - /** - * @todo Needs to work out what this does (if it does what it is meant to) - */ - void visitRenderables(Renderable::Visitor* visitor, - bool debugRenderables = false) { - visitor->visit(this, 0, false); - } - - virtual const Ogre::MaterialPtr& getMaterial( void ) const { - return mMaterial; - } - //----------------------------------------------------------------------- - void getRenderOperation( Ogre::RenderOperation& op ) { - op.useIndexes = true; - op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; - op.vertexData = mVertexes; - op.indexData = mIndices; - } - - //----------------------------------------------------------------------- - void getWorldTransforms( Ogre::Matrix4* xform ) const { - *xform = mParentNode->_getFullTransform(); - } - //----------------------------------------------------------------------- - const Ogre::Quaternion& getWorldOrientation(void) const { - return mParentNode->_getDerivedOrientation(); - } - //----------------------------------------------------------------------- - const Ogre::Vector3& getWorldPosition(void) const { - return mParentNode->_getDerivedPosition(); - } - //----------------------------------------------------------------------- - - Ogre::Real getSquaredViewDepth(const Ogre::Camera *cam) const { - Ogre::Vector3 diff = mCenter - cam->getDerivedPosition(); - // Use squared length to avoid square root - return diff.squaredLength(); - } - //----------------------------------------------------------------------- - - const Ogre::LightList& getLights(void) const { - if (mLightListDirty) { - getParentSceneNode()->getCreator()->_populateLightList( - mCenter, this->getBoundingRadius(), mLightList); - mLightListDirty = false; - } - return mLightList; - } - //----------------------------------------------------------------------- - 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<vw); - } -#endif - return mQuadData->getVertex((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x)); - } - - float getNormal(int x, int y, int z) - { - assert(z>=0&&z<3); - return mQuadData->getNormal(((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x))*3+z); - } - - 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<int>& 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<int> ti; - - ti.resize((size+2)*(size+2), -1); - - for ( int y = 0; y < size+2; ++y ){ - for ( int x = 0; x < size+2; ++x ){ - ti[(y)*(size+2)+(x)] = tref.at((y+yoff)*(indexSize)+(x+xoff)); - } - } - - 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); - } -#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/cpp_terrainmesh2.cpp b/terrain/cpp_terrainmesh2.cpp new file mode 100644 index 0000000000..caa5f45581 --- /dev/null +++ b/terrain/cpp_terrainmesh2.cpp @@ -0,0 +1,495 @@ +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_heightMap->isMorphingEnabled() && mDepth != g_heightMap->getMaxDepth() ) { + Ogre::Technique* tech = getMaterial()->getTechnique(0); + for ( size_t i = 0; i < tech->getNumPasses(); ++i ) { + assert(g_heightMap->isMorphingEnabled()); + tech->getPass(i)->setVertexProgram(MORPH_VERTEX_PROGRAM); + } + } + + if ( g_heightMap->isMorphingEnabled() ) + calculateDeltaValues(); + + mBuilt = true; +} + +void TerrainMesh::destroy() { + if ( !mBuilt ) return; + + //deleting null values is fine iirc + delete mIndices; + +# if ENABLED_CRASHING == 1 + { + delete mVertexes; + } +# else + { + if ( mDepth != g_heightMap->getMaxDepth() ){ + delete mVertexes; + } + } +# endif + + mBuilt = false; +} +//---------------------------------------------- +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_heightMap->getMorphSpeed()); //decrease slowly + } + if ( mExtraFadeAmount > 0 ) { + mTextureFadeFactor += mExtraFadeAmount; + mExtraFadeAmount -= (time/g_heightMap->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 ( g_heightMap->isTextureFadingEnabled() && + !mHasFadePass && + mTextureFadeFactor > 0 && + hasParentTexture() ) { + addFadePass(); + } + +} +//---------------------------------------------- +void TerrainMesh::addFadePass() { + assert(mHasFadePass==false); + + if ( mDepth == g_heightMap->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); + + //set fragment program + assert(g_heightMap->isTextureFadingEnabled()); + newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM); + + if ( g_heightMap->isMorphingEnabled() && mDepth != g_heightMap->getMaxDepth() ) { + assert(g_heightMap->isMorphingEnabled()); + newPass->setVertexProgram(MORPH_VERTEX_PROGRAM); + } + + + //set texture to be used + newPass->createTextureUnitState(getParentTexture(), 1); +} +//---------------------------------------------- +void TerrainMesh::removeFadePass() { + assert(mHasFadePass==true); + mHasFadePass = false; + Ogre::MaterialPtr mat = getMaterial(); + Ogre::Technique* tech = mat->getTechnique(0); + + tech->removePass(tech->getNumPasses()-1); +} +//---------------------------------------------- +void TerrainMesh::justSplit() { + mExtraMorphAmount = 1; + mLODMorphFactor = 1; + mTextureFadeFactor = 1; + mExtraFadeAmount = 1; + + if ( g_heightMap->isTextureFadingEnabled() && hasParentTexture() ) + addFadePass(); +} + +//---------------------------------------------- +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 TerrainMesh::getVertexHeight(int x, int y) { + return getVertex(x,y); +} +//---------------------------------------------- +Ogre::Vector3 TerrainMesh::getVertexPosition(int x, int y) { + return Ogre::Vector3(x*getVertexSeperation(), getVertexHeight(x,y) , y*getVertexSeperation()); +} +//---------------------------------------------- +void TerrainMesh::calculateVetexValues() { + using namespace Ogre; + + //get the texture offsets for the higher uv + float xUVOffset = 0; + float yUVOffset = 0; + + if ( g_heightMap->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; + } + */ + + int start = 0; + int end = mWidth; + + if ( mUseSkirts ) { + --start; + ++end; + } + + float* verts = static_cast<float*>(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) ) { + + 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 + + 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; + + float u = (float)(x) / (mWidth-1); + float v = (float)(y) / (mWidth-1); + + //clamped, so shouldn't matter if > 1 + + *verts++ = u; + *verts++ = v; + + if ( g_heightMap->isTextureFadingEnabled() ) { + *verts++ = u; + *verts++ = v; + } + } else { + + assert(y*mWidth+x>=0&&y*mWidth+x<mWidth*mWidth); + + //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)); + + //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); + + *verts++ = u; + *verts++ = v; + + if ( g_heightMap->isTextureFadingEnabled() ) { + *verts++ = u/2.0f + xUVOffset; + *verts++ = v/2.0f + yUVOffset; + } + } + } + } + mMainBuffer->unlock(); +} +//---------------------------------------------- +void TerrainMesh::setBounds() { + mBounds.setExtents(0,mMin,0, + (mWidth - 1) * getVertexSeperation(), + mMax, + (mWidth - 1) * getVertexSeperation()); + + mCenter = Ogre::Vector3( ( (mWidth - 1) * getVertexSeperation() ) / 2, + ( mMin + mMax ) / 2, + ( (mWidth - 1) * getVertexSeperation() ) / 2); + + mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; +} +//---------------------------------------------- +void TerrainMesh::createVertexBuffer() { + using namespace Ogre; + + size_t vw = mWidth; + if ( mUseSkirts ) vw += 2; + + mVertexes = new VertexData(); + mVertexes->vertexStart = 0; + mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH; + + 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_NORMAL); + currOffset += VertexElement::getTypeSize(VET_FLOAT3); + + + vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); + currOffset += VertexElement::getTypeSize(VET_FLOAT2); + + if ( g_heightMap->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 + + mVertexes->vertexBufferBinding->setBinding(MAIN_BINDING, mMainBuffer); //bind the data + + if ( g_heightMap->isMorphingEnabled() ) + vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); + + +} + +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 + +} + + + + +//---------------------------------------------- +#define SET_DELTA_AT(x, y, v) \ +if ( mUseSkirts ) pDeltas[( y + 1)*vw+ x + 1] = v; \ +else pDeltas[( y)*vw+ x] = v; +void TerrainMesh::calculateDeltaValues() { + + using namespace Ogre; + size_t vw = mWidth; + if ( mUseSkirts ) vw += 2; + + //must be using morphing to use this function + assert(g_heightMap->isMorphingEnabled()); + + const size_t step = 2; + + // Create a set of delta values + mDeltaBuffer = createDeltaBuffer(); + float* pDeltas = static_cast<float*>(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD)); + memset(pDeltas, 0, (vw)*(vw) * sizeof(float)); + + 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 + + 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); + + //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 + + //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 + } + + mDeltaBuffer->unlock(); + mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer); + +} +#undef SET_DELTA_AT + +//---------------------------------------------- +void TerrainMesh::calculateIndexValues() { + using namespace Ogre; + + + + size_t vw = mWidth-1; + if ( mUseSkirts ) vw += 2; + + const size_t indexCount = (vw)*(vw)*6; + + //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); + + unsigned short* indices = static_cast<unsigned short*>(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 ) { + + const int line1 = y * (vw+1) + x; + const int line2 = (y + 1) * (vw+1) + x; + + 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; + *indices++ = line2 + 1; + *indices++ = line1 + 1; + } + flag = !flag; //flip tris for next time + + indNum+=6; + } + flag = !flag; //flip tries for next row + } + assert(indNum==indexCount); + mIndices->indexBuffer->unlock(); + //return mIndices; +}