mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 02:26:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			590 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			590 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "quadtreenode.hpp"
 | 
						|
 | 
						|
#include <OgreSceneManager.h>
 | 
						|
#include <OgreManualObject.h>
 | 
						|
#include <OgreSceneNode.h>
 | 
						|
#include <OgreMaterialManager.h>
 | 
						|
#include <OgreTextureManager.h>
 | 
						|
 | 
						|
#include "defaultworld.hpp"
 | 
						|
#include "chunk.hpp"
 | 
						|
#include "storage.hpp"
 | 
						|
#include "buffercache.hpp"
 | 
						|
#include "material.hpp"
 | 
						|
 | 
						|
using namespace Terrain;
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
    int Log2( int n )
 | 
						|
    {
 | 
						|
        assert(n > 0);
 | 
						|
        int targetlevel = 0;
 | 
						|
        while (n >>= 1) ++targetlevel;
 | 
						|
        return targetlevel;
 | 
						|
    }
 | 
						|
 | 
						|
    // Utility functions for neighbour finding algorithm
 | 
						|
    ChildDirection reflect(ChildDirection dir, Direction dir2)
 | 
						|
    {
 | 
						|
        assert(dir != Root);
 | 
						|
 | 
						|
        const int lookupTable[4][4] =
 | 
						|
        {
 | 
						|
            // NW  NE  SW  SE
 | 
						|
            {  SW, SE, NW, NE }, // N
 | 
						|
            {  NE, NW, SE, SW }, // E
 | 
						|
            {  SW, SE, NW, NE }, // S
 | 
						|
            {  NE, NW, SE, SW }  // W
 | 
						|
        };
 | 
						|
        return (ChildDirection)lookupTable[dir2][dir];
 | 
						|
    }
 | 
						|
 | 
						|
    bool adjacent(ChildDirection dir, Direction dir2)
 | 
						|
    {
 | 
						|
        assert(dir != Root);
 | 
						|
        const bool lookupTable[4][4] =
 | 
						|
        {
 | 
						|
            // NW    NE    SW     SE
 | 
						|
            {  true, true, false, false }, // N
 | 
						|
            {  false, true, false, true }, // E
 | 
						|
            {  false, false, true, true }, // S
 | 
						|
            {  true, false, true, false }  // W
 | 
						|
        };
 | 
						|
        return lookupTable[dir2][dir];
 | 
						|
    }
 | 
						|
 | 
						|
    // Algorithm described by Hanan Samet - 'Neighbour Finding in Quadtrees'
 | 
						|
    // http://www.cs.umd.edu/~hjs/pubs/SametPRIP81.pdf
 | 
						|
    QuadTreeNode* searchNeighbourRecursive (QuadTreeNode* currentNode, Direction dir)
 | 
						|
    {
 | 
						|
        if (!currentNode->getParent())
 | 
						|
            return NULL; // Arrived at root node, the root node does not have neighbours
 | 
						|
 | 
						|
        QuadTreeNode* nextNode;
 | 
						|
        if (adjacent(currentNode->getDirection(), dir))
 | 
						|
            nextNode = searchNeighbourRecursive(currentNode->getParent(), dir);
 | 
						|
        else
 | 
						|
            nextNode = currentNode->getParent();
 | 
						|
 | 
						|
        if (nextNode && nextNode->hasChildren())
 | 
						|
            return nextNode->getChild(reflect(currentNode->getDirection(), dir));
 | 
						|
        else
 | 
						|
            return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    // Create a 2D quad
 | 
						|
    void makeQuad(Ogre::SceneManager* sceneMgr, float left, float top, float right, float bottom, Ogre::MaterialPtr material)
 | 
						|
    {
 | 
						|
        Ogre::ManualObject* manual = sceneMgr->createManualObject();
 | 
						|
 | 
						|
        // Use identity view/projection matrices to get a 2d quad
 | 
						|
        manual->setUseIdentityProjection(true);
 | 
						|
        manual->setUseIdentityView(true);
 | 
						|
 | 
						|
        manual->begin(material->getName());
 | 
						|
 | 
						|
        float normLeft = left*2-1;
 | 
						|
        float normTop = top*2-1;
 | 
						|
        float normRight = right*2-1;
 | 
						|
        float normBottom = bottom*2-1;
 | 
						|
 | 
						|
        manual->position(normLeft, normTop, 0.0);
 | 
						|
        manual->textureCoord(0, 1);
 | 
						|
        manual->position(normRight, normTop, 0.0);
 | 
						|
        manual->textureCoord(1, 1);
 | 
						|
        manual->position(normRight, normBottom, 0.0);
 | 
						|
        manual->textureCoord(1, 0);
 | 
						|
        manual->position(normLeft, normBottom, 0.0);
 | 
						|
        manual->textureCoord(0, 0);
 | 
						|
 | 
						|
        manual->quad(0,1,2,3);
 | 
						|
 | 
						|
        manual->end();
 | 
						|
 | 
						|
        Ogre::AxisAlignedBox aabInf;
 | 
						|
        aabInf.setInfinite();
 | 
						|
        manual->setBoundingBox(aabInf);
 | 
						|
 | 
						|
        sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QuadTreeNode::QuadTreeNode(DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent)
 | 
						|
    : mMaterialGenerator(NULL)
 | 
						|
    , mIsDummy(false)
 | 
						|
    , mSize(size)
 | 
						|
    , mLodLevel(Log2(mSize))
 | 
						|
    , mBounds(Ogre::AxisAlignedBox::BOX_NULL)
 | 
						|
    , mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL)
 | 
						|
    , mDirection(dir)
 | 
						|
    , mCenter(center)
 | 
						|
    , mSceneNode(NULL)
 | 
						|
    , mParent(parent)
 | 
						|
    , mTerrain(terrain)
 | 
						|
    , mChunk(NULL)
 | 
						|
    , mLoadState(LS_Unloaded)
 | 
						|
{
 | 
						|
    mBounds.setNull();
 | 
						|
    for (int i=0; i<4; ++i)
 | 
						|
        mChildren[i] = NULL;
 | 
						|
    for (int i=0; i<4; ++i)
 | 
						|
        mNeighbours[i] = NULL;
 | 
						|
 | 
						|
    if (mDirection == Root)
 | 
						|
        mSceneNode = mTerrain->getRootSceneNode();
 | 
						|
    else
 | 
						|
        mSceneNode = mTerrain->getSceneManager()->createSceneNode();
 | 
						|
    Ogre::Vector2 pos (0,0);
 | 
						|
    if (mParent)
 | 
						|
        pos = mParent->getCenter();
 | 
						|
    pos = mCenter - pos;
 | 
						|
    float cellWorldSize = mTerrain->getStorage()->getCellWorldSize();
 | 
						|
 | 
						|
    Ogre::Vector3 sceneNodePos (pos.x*cellWorldSize, pos.y*cellWorldSize, 0);
 | 
						|
    mTerrain->convertPosition(sceneNodePos);
 | 
						|
 | 
						|
    mSceneNode->setPosition(sceneNodePos);
 | 
						|
 | 
						|
    mMaterialGenerator = new MaterialGenerator();
 | 
						|
    mMaterialGenerator->enableShaders(mTerrain->getShadersEnabled());
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er)
 | 
						|
{
 | 
						|
    mChildren[id] = new QuadTreeNode(mTerrain, id, size, center, this);
 | 
						|
}
 | 
						|
 | 
						|
QuadTreeNode::~QuadTreeNode()
 | 
						|
{
 | 
						|
    for (int i=0; i<4; ++i)
 | 
						|
        delete mChildren[i];
 | 
						|
    delete mChunk;
 | 
						|
    delete mMaterialGenerator;
 | 
						|
}
 | 
						|
 | 
						|
QuadTreeNode* QuadTreeNode::getNeighbour(Direction dir)
 | 
						|
{
 | 
						|
    return mNeighbours[static_cast<int>(dir)];
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::initNeighbours()
 | 
						|
{
 | 
						|
    for (int i=0; i<4; ++i)
 | 
						|
        mNeighbours[i] = searchNeighbourRecursive(this, (Direction)i);
 | 
						|
 | 
						|
    if (hasChildren())
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
            mChildren[i]->initNeighbours();
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::initAabb()
 | 
						|
{
 | 
						|
    float cellWorldSize = mTerrain->getStorage()->getCellWorldSize();
 | 
						|
    if (hasChildren())
 | 
						|
    {
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
        {
 | 
						|
            mChildren[i]->initAabb();
 | 
						|
            mBounds.merge(mChildren[i]->getBoundingBox());
 | 
						|
        }
 | 
						|
        float minH, maxH;
 | 
						|
        switch (mTerrain->getAlign())
 | 
						|
        {
 | 
						|
            case Terrain::Align_XY:
 | 
						|
                minH = mBounds.getMinimum().z;
 | 
						|
                maxH = mBounds.getMaximum().z;
 | 
						|
                break;
 | 
						|
            case Terrain::Align_XZ:
 | 
						|
                minH = mBounds.getMinimum().y;
 | 
						|
                maxH = mBounds.getMaximum().y;
 | 
						|
                break;
 | 
						|
            case Terrain::Align_YZ:
 | 
						|
                minH = mBounds.getMinimum().x;
 | 
						|
                maxH = mBounds.getMaximum().x;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
        Ogre::Vector3 min(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, minH);
 | 
						|
        Ogre::Vector3 max(Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, maxH));
 | 
						|
        mBounds = Ogre::AxisAlignedBox (min, max);
 | 
						|
        mTerrain->convertBounds(mBounds);
 | 
						|
    }
 | 
						|
    Ogre::Vector3 offset(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0);
 | 
						|
    mTerrain->convertPosition(offset);
 | 
						|
    mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + offset,
 | 
						|
                                        mBounds.getMaximum() + offset);
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box)
 | 
						|
{
 | 
						|
    mBounds = box;
 | 
						|
}
 | 
						|
 | 
						|
const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox()
 | 
						|
{
 | 
						|
    return mBounds;
 | 
						|
}
 | 
						|
 | 
						|
const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox()
 | 
						|
{
 | 
						|
    return mWorldBounds;
 | 
						|
}
 | 
						|
 | 
						|
bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
 | 
						|
{
 | 
						|
    if (isDummy())
 | 
						|
        return true;
 | 
						|
 | 
						|
    if (mBounds.isNull())
 | 
						|
        return true;
 | 
						|
 | 
						|
    float dist = mWorldBounds.distance(cameraPos);
 | 
						|
 | 
						|
    // Make sure our scene node is attached
 | 
						|
    if (!mSceneNode->isInSceneGraph())
 | 
						|
    {
 | 
						|
        mParent->getSceneNode()->addChild(mSceneNode);
 | 
						|
    }
 | 
						|
 | 
						|
    // Simple LOD selection
 | 
						|
    /// \todo use error metrics?
 | 
						|
    size_t wantedLod = 0;
 | 
						|
    float cellWorldSize = mTerrain->getStorage()->getCellWorldSize();
 | 
						|
 | 
						|
    if (dist > cellWorldSize*64)
 | 
						|
        wantedLod = 6;
 | 
						|
    else if (dist > cellWorldSize*32)
 | 
						|
        wantedLod = 5;
 | 
						|
    else if (dist > cellWorldSize*12)
 | 
						|
        wantedLod = 4;
 | 
						|
    else if (dist > cellWorldSize*5)
 | 
						|
        wantedLod = 3;
 | 
						|
    else if (dist > cellWorldSize*2)
 | 
						|
        wantedLod = 2;
 | 
						|
    else if (dist > cellWorldSize * 1.42) // < sqrt2 so the 3x3 grid around player is always highest lod
 | 
						|
        wantedLod = 1;
 | 
						|
 | 
						|
    bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod;
 | 
						|
 | 
						|
    if (wantToDisplay)
 | 
						|
    {
 | 
						|
        // Wanted LOD is small enough to render this node in one chunk
 | 
						|
        if (mLoadState == LS_Unloaded)
 | 
						|
        {
 | 
						|
            mLoadState = LS_Loading;
 | 
						|
            mTerrain->queueLoad(this);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        if (mLoadState == LS_Loaded)
 | 
						|
        {
 | 
						|
            // Additional (index buffer) LOD is currently disabled.
 | 
						|
            // This is due to a problem with the LOD selection when a node splits.
 | 
						|
            // After splitting, the distance is measured from the children's bounding boxes, which are possibly
 | 
						|
            // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD
 | 
						|
            // than the original node.
 | 
						|
            // In short, we'd sometimes get a switch to a lesser detail when actually moving closer.
 | 
						|
            // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour
 | 
						|
            // node hasn't split yet, and has a higher LOD than our node's child:
 | 
						|
            //  ----- ----- ------------
 | 
						|
            // | LOD | LOD |            |
 | 
						|
            // |  1  |  1  |            |
 | 
						|
            // |-----|-----|   LOD 0    |
 | 
						|
            // | LOD | LOD |            |
 | 
						|
            // |  0  |  0  |            |
 | 
						|
            //  ----- ----- ------------
 | 
						|
            // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're
 | 
						|
            // doing here.
 | 
						|
            // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way.
 | 
						|
            //mChunk->setAdditionalLod(wantedLod - mLodLevel);
 | 
						|
 | 
						|
            if (!mChunk->getVisible() && hasChildren())
 | 
						|
            {
 | 
						|
                for (int i=0; i<4; ++i)
 | 
						|
                    mChildren[i]->unload(true);
 | 
						|
            }
 | 
						|
            mChunk->setVisible(true);
 | 
						|
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        return false; // LS_Loading
 | 
						|
    }
 | 
						|
 | 
						|
    // We do not want to display this node - delegate to children if they are already loaded
 | 
						|
    if (!wantToDisplay && hasChildren())
 | 
						|
    {
 | 
						|
        if (mChunk)
 | 
						|
        {
 | 
						|
            // Are children already loaded?
 | 
						|
            bool childrenLoaded = true;
 | 
						|
            for (int i=0; i<4; ++i)
 | 
						|
                if (!mChildren[i]->update(cameraPos))
 | 
						|
                    childrenLoaded = false;
 | 
						|
 | 
						|
            if (!childrenLoaded)
 | 
						|
            {
 | 
						|
                mChunk->setVisible(true);
 | 
						|
                // Make sure child scene nodes are detached until all children are loaded
 | 
						|
                mSceneNode->removeAllChildren();
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Delegation went well, we can unload now
 | 
						|
                unload();
 | 
						|
 | 
						|
                for (int i=0; i<4; ++i)
 | 
						|
                {
 | 
						|
                    if (!mChildren[i]->getSceneNode()->isInSceneGraph())
 | 
						|
                        mSceneNode->addChild(mChildren[i]->getSceneNode());
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            bool success = true;
 | 
						|
            for (int i=0; i<4; ++i)
 | 
						|
                success = mChildren[i]->update(cameraPos) & success;
 | 
						|
            return success;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::load(const LoadResponseData &data)
 | 
						|
{
 | 
						|
    assert (!mChunk);
 | 
						|
 | 
						|
    mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data.mPositions, data.mNormals, data.mColours);
 | 
						|
    mChunk->setVisibilityFlags(mTerrain->getVisibilityFlags());
 | 
						|
    mChunk->setCastShadows(true);
 | 
						|
    mSceneNode->attachObject(mChunk);
 | 
						|
 | 
						|
    mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
 | 
						|
    mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled());
 | 
						|
 | 
						|
    if (mTerrain->areLayersLoaded())
 | 
						|
    {
 | 
						|
        if (mSize == 1)
 | 
						|
        {
 | 
						|
            mChunk->setMaterial(mMaterialGenerator->generate());
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            ensureCompositeMap();
 | 
						|
            mMaterialGenerator->setCompositeMap(mCompositeMap->getName());
 | 
						|
            mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap());
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // else: will be loaded in loadMaterials() after background thread has finished loading layers
 | 
						|
    mChunk->setVisible(false);
 | 
						|
 | 
						|
    mLoadState = LS_Loaded;
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::unload(bool recursive)
 | 
						|
{
 | 
						|
    if (mChunk)
 | 
						|
    {
 | 
						|
        mSceneNode->detachObject(mChunk);
 | 
						|
 | 
						|
        delete mChunk;
 | 
						|
        mChunk = NULL;
 | 
						|
 | 
						|
        if (!mCompositeMap.isNull())
 | 
						|
        {
 | 
						|
            Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName());
 | 
						|
            mCompositeMap.setNull();
 | 
						|
        }
 | 
						|
 | 
						|
        // Do *not* set this when we are still loading!
 | 
						|
        mLoadState = LS_Unloaded;
 | 
						|
    }
 | 
						|
 | 
						|
    if (recursive && hasChildren())
 | 
						|
    {
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
            mChildren[i]->unload(true);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::updateIndexBuffers()
 | 
						|
{
 | 
						|
    if (hasChunk())
 | 
						|
    {
 | 
						|
        // Fetch a suitable index buffer (which may be shared)
 | 
						|
        size_t ourLod = getActualLodLevel();
 | 
						|
 | 
						|
        int flags = 0;
 | 
						|
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
        {
 | 
						|
            QuadTreeNode* neighbour = getNeighbour((Direction)i);
 | 
						|
 | 
						|
            // If the neighbour isn't currently rendering itself,
 | 
						|
            // go up until we find one. NOTE: We don't need to go down,
 | 
						|
            // because in that case neighbour's detail would be higher than
 | 
						|
            // our detail and the neighbour would handle stitching by itself.
 | 
						|
            while (neighbour && !neighbour->hasChunk())
 | 
						|
                neighbour = neighbour->getParent();
 | 
						|
            size_t lod = 0;
 | 
						|
            if (neighbour)
 | 
						|
                lod = neighbour->getActualLodLevel();
 | 
						|
            if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are -
 | 
						|
                lod = 0;         // neighbours with more detail will do the stitching themselves
 | 
						|
            // Use 4 bits for each LOD delta
 | 
						|
            if (lod > 0)
 | 
						|
            {
 | 
						|
                assert (lod - ourLod < (1 << 4));
 | 
						|
                flags |= int(lod - ourLod) << (4*i);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        flags |= 0 /*((int)mAdditionalLod)*/ << (4*4);
 | 
						|
 | 
						|
        mChunk->setIndexBuffer(mTerrain->getBufferCache().getIndexBuffer(flags));
 | 
						|
    }
 | 
						|
    else if (hasChildren())
 | 
						|
    {
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
            mChildren[i]->updateIndexBuffers();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool QuadTreeNode::hasChunk()
 | 
						|
{
 | 
						|
    return mSceneNode->isInSceneGraph() && mChunk && mChunk->getVisible();
 | 
						|
}
 | 
						|
 | 
						|
size_t QuadTreeNode::getActualLodLevel()
 | 
						|
{
 | 
						|
    assert(hasChunk() && "Can't get actual LOD level if this node has no render chunk");
 | 
						|
    return mLodLevel /* + mChunk->getAdditionalLod() */;
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::loadLayers(const LayerCollection& collection)
 | 
						|
{
 | 
						|
    assert (!mMaterialGenerator->hasLayers());
 | 
						|
 | 
						|
    std::vector<Ogre::TexturePtr> blendTextures;
 | 
						|
    for (std::vector<Ogre::PixelBox>::const_iterator it = collection.mBlendmaps.begin(); it != collection.mBlendmaps.end(); ++it)
 | 
						|
    {
 | 
						|
        // TODO: clean up blend textures on destruction
 | 
						|
        static int count=0;
 | 
						|
        Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/"
 | 
						|
            + Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
 | 
						|
            Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format);
 | 
						|
 | 
						|
        Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true));
 | 
						|
        map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format);
 | 
						|
        blendTextures.push_back(map);
 | 
						|
    }
 | 
						|
 | 
						|
    mMaterialGenerator->setLayerList(collection.mLayers);
 | 
						|
    mMaterialGenerator->setBlendmapList(blendTextures);
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::loadMaterials()
 | 
						|
{
 | 
						|
    if (isDummy())
 | 
						|
        return;
 | 
						|
 | 
						|
    // Load children first since we depend on them when creating a composite map
 | 
						|
    if (hasChildren())
 | 
						|
    {
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
            mChildren[i]->loadMaterials();
 | 
						|
    }
 | 
						|
 | 
						|
    if (mChunk)
 | 
						|
    {
 | 
						|
        if (mSize == 1)
 | 
						|
        {
 | 
						|
            mChunk->setMaterial(mMaterialGenerator->generate());
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            ensureCompositeMap();
 | 
						|
            mMaterialGenerator->setCompositeMap(mCompositeMap->getName());
 | 
						|
            mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap());
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
 | 
						|
{
 | 
						|
    Ogre::SceneManager* sceneMgr = mTerrain->getCompositeMapSceneManager();
 | 
						|
 | 
						|
    if (mIsDummy)
 | 
						|
    {
 | 
						|
        // TODO - store this default material somewhere instead of creating one for each empty cell
 | 
						|
        MaterialGenerator matGen;
 | 
						|
        matGen.enableShaders(mTerrain->getShadersEnabled());
 | 
						|
        std::vector<LayerInfo> layer;
 | 
						|
        layer.push_back(mTerrain->getStorage()->getDefaultLayer());
 | 
						|
        matGen.setLayerList(layer);
 | 
						|
        makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT());
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if (mSize > 1)
 | 
						|
    {
 | 
						|
        assert(hasChildren());
 | 
						|
 | 
						|
        // 0,0 -------- 1,0
 | 
						|
        //  |     |      |
 | 
						|
        //  |-----|------|
 | 
						|
        //  |     |      |
 | 
						|
        // 0,1 -------- 1,1
 | 
						|
 | 
						|
        float halfW = area.width()/2.f;
 | 
						|
        float halfH = area.height()/2.f;
 | 
						|
        mChildren[NW]->prepareForCompositeMap(Ogre::TRect<float>(area.left, area.top, area.right-halfW, area.bottom-halfH));
 | 
						|
        mChildren[NE]->prepareForCompositeMap(Ogre::TRect<float>(area.left+halfW, area.top, area.right, area.bottom-halfH));
 | 
						|
        mChildren[SW]->prepareForCompositeMap(Ogre::TRect<float>(area.left, area.top+halfH, area.right-halfW, area.bottom));
 | 
						|
        mChildren[SE]->prepareForCompositeMap(Ogre::TRect<float>(area.left+halfW, area.top+halfH, area.right, area.bottom));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // TODO: when to destroy?
 | 
						|
        Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT();
 | 
						|
        makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::ensureCompositeMap()
 | 
						|
{
 | 
						|
    if (!mCompositeMap.isNull())
 | 
						|
        return;
 | 
						|
 | 
						|
    static int i=0;
 | 
						|
    std::stringstream name;
 | 
						|
    name << "terrain/comp" << i++;
 | 
						|
 | 
						|
    const int size = 128;
 | 
						|
    mCompositeMap = Ogre::TextureManager::getSingleton().createManual(
 | 
						|
                name.str(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
 | 
						|
        Ogre::TEX_TYPE_2D, size, size, Ogre::MIP_DEFAULT, Ogre::PF_A8B8G8R8);
 | 
						|
 | 
						|
    // Create quads for each cell
 | 
						|
    prepareForCompositeMap(Ogre::TRect<float>(0,0,1,1));
 | 
						|
 | 
						|
    mTerrain->renderCompositeMap(mCompositeMap);
 | 
						|
 | 
						|
    mTerrain->clearCompositeMapSceneManager();
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void QuadTreeNode::applyMaterials()
 | 
						|
{
 | 
						|
    if (mChunk)
 | 
						|
    {
 | 
						|
        mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
 | 
						|
        mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled());
 | 
						|
        if (mSize <= 1)
 | 
						|
            mChunk->setMaterial(mMaterialGenerator->generate());
 | 
						|
        else
 | 
						|
            mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap());
 | 
						|
    }
 | 
						|
    if (hasChildren())
 | 
						|
        for (int i=0; i<4; ++i)
 | 
						|
            mChildren[i]->applyMaterials();
 | 
						|
}
 |