openmw-tes3coop/components/terrain/chunk.cpp
scrawl e27437f8ed New terrain renderer - improvements:
- Consistent triangle alignment, fixes a noticable crack near the census and excise office. Note that alignment is still not the same as vanilla. Vanilla uses a weird diagonal pattern. I hope there aren't more trouble spots that will force us to replicate vanilla, but at least we can do that now.
 - Fixes several blending issues and cell border seams
 - Fix map render to use the terrain bounding box instead of an arbitrary height
 - Different LODs are now properly connected instead of using skirts
 - Support self shadowing
 - Normals and colors are stored in the vertices instead of a texture, this enables per-vertex lighting which should improve performance, fix compatibility issues due to the PS getting too large and mimic vanilla better
 - Support a fixed function fallback (though the splatting shader usually performs better)
 - Designed for distant land support - test: https://www.youtube.com/watch?v=2wnd9EuPJIY - we can't really enable this yet due to depth precision issues when using a large view distance
2013-08-19 20:34:20 +02:00

171 lines
5.8 KiB
C++

#include "chunk.hpp"
#include <OgreSceneNode.h>
#include <OgreHardwareBufferManager.h>
#include <OgreMaterialManager.h>
#include <OgreTechnique.h>
#include <OgrePass.h>
#include "quadtreenode.hpp"
#include "terrain.hpp"
#include "storage.hpp"
namespace Terrain
{
Chunk::Chunk(QuadTreeNode* node, short lodLevel)
: mNode(node)
, mVertexLod(lodLevel)
, mAdditionalLod(0)
{
mVertexData = OGRE_NEW Ogre::VertexData;
mVertexData->vertexStart = 0;
// Set the total number of vertices
size_t numVertsOneSide = mNode->getSize() * (ESM::Land::LAND_SIZE-1);
numVertsOneSide /= std::pow(2, lodLevel);
numVertsOneSide += 1;
assert((int)numVertsOneSide == ESM::Land::LAND_SIZE);
mVertexData->vertexCount = numVertsOneSide * numVertsOneSide;
// Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc)
Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration;
Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr();
size_t nextBuffer = 0;
// Positions
vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
mVertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
// Normals
vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
mNormalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
// UV texture coordinates
vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT2,
Ogre::VES_TEXTURE_COORDINATES, 0);
Ogre::HardwareVertexBufferSharedPtr uvBuf = mNode->getTerrain()->getVertexBuffer(numVertsOneSide);
// Colours
vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(),
mVertexBuffer, mNormalBuffer, mColourBuffer);
mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer);
mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer);
mVertexData->vertexBufferBinding->setBinding(2, uvBuf);
mVertexData->vertexBufferBinding->setBinding(3, mColourBuffer);
mIndexData = OGRE_NEW Ogre::IndexData();
mIndexData->indexStart = 0;
}
void Chunk::updateIndexBuffer()
{
// Fetch a suitable index buffer (which may be shared)
size_t ourLod = mVertexLod + mAdditionalLod;
int flags = 0;
for (int i=0; i<4; ++i)
{
QuadTreeNode* neighbour = mNode->searchNeighbour((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 < std::pow(2,4));
flags |= int(lod - ourLod) << (4*i);
}
}
flags |= ((int)mAdditionalLod) << (4*4);
size_t numIndices;
mIndexBuffer = mNode->getTerrain()->getIndexBuffer(flags, numIndices);
mIndexData->indexCount = numIndices;
mIndexData->indexBuffer = mIndexBuffer;
}
Chunk::~Chunk()
{
OGRE_DELETE mVertexData;
OGRE_DELETE mIndexData;
}
void Chunk::setMaterial(const Ogre::MaterialPtr &material)
{
mMaterial = material;
}
const Ogre::AxisAlignedBox& Chunk::getBoundingBox(void) const
{
return mNode->getBoundingBox();
}
Ogre::Real Chunk::getBoundingRadius(void) const
{
return mNode->getBoundingBox().getHalfSize().length();
}
void Chunk::_updateRenderQueue(Ogre::RenderQueue* queue)
{
queue->addRenderable(this, mRenderQueueID);
}
void Chunk::visitRenderables(Ogre::Renderable::Visitor* visitor,
bool debugRenderables)
{
visitor->visit(this, 0, false);
}
const Ogre::MaterialPtr& Chunk::getMaterial(void) const
{
return mMaterial;
}
void Chunk::getRenderOperation(Ogre::RenderOperation& op)
{
assert (!mIndexBuffer.isNull() && "Trying to render, but no index buffer set!");
op.useIndexes = true;
op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
op.vertexData = mVertexData;
op.indexData = mIndexData;
}
void Chunk::getWorldTransforms(Ogre::Matrix4* xform) const
{
*xform = getParentSceneNode()->_getFullTransform();
}
Ogre::Real Chunk::getSquaredViewDepth(const Ogre::Camera* cam) const
{
return getParentSceneNode()->getSquaredViewDepth(cam);
}
const Ogre::LightList& Chunk::getLights(void) const
{
return queryLights();
}
}