mirror of
https://github.com/worldforge/ember
synced 2026-08-13 16:23:06 -04:00
470 lines
15 KiB
C++
470 lines
15 KiB
C++
/***********************************************************************
|
|
created: Tue Feb 17 2009
|
|
author: Paul D Turner
|
|
*************************************************************************/
|
|
/***************************************************************************
|
|
* Copyright (C) 2004 - 2013 Paul D Turner & The CEGUI Development Team
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
***************************************************************************/
|
|
#include "GeometryBuffer.h"
|
|
#include "Texture.h"
|
|
#include "OgreMacros.h"
|
|
#include "CEGUI/Vertex.h"
|
|
#include "CEGUI/RenderEffect.h"
|
|
|
|
#include <OgreRenderSystem.h>
|
|
#include <OgreQuaternion.h>
|
|
#include <OgreHardwareBufferManager.h>
|
|
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
#include <OgreHlmsSamplerblock.h>
|
|
#include <OgreRenderTarget.h>
|
|
#include <OgreViewport.h>
|
|
#endif
|
|
|
|
|
|
|
|
// Start of CEGUI namespace section
|
|
namespace CEGUI
|
|
{
|
|
//----------------------------------------------------------------------------//
|
|
static const Ogre::LayerBlendModeEx S_colourBlendMode =
|
|
{
|
|
Ogre::LBT_COLOUR,
|
|
Ogre::LBX_MODULATE,
|
|
Ogre::LBS_TEXTURE,
|
|
Ogre::LBS_DIFFUSE,
|
|
Ogre::ColourValue(0, 0, 0, 0),
|
|
Ogre::ColourValue(0, 0, 0, 0),
|
|
0, 0, 0
|
|
};
|
|
|
|
//----------------------------------------------------------------------------//
|
|
static const Ogre::LayerBlendModeEx S_alphaBlendMode =
|
|
{
|
|
Ogre::LBT_ALPHA,
|
|
Ogre::LBX_MODULATE,
|
|
Ogre::LBS_TEXTURE,
|
|
Ogre::LBS_DIFFUSE,
|
|
Ogre::ColourValue(0, 0, 0, 0),
|
|
Ogre::ColourValue(0, 0, 0, 0),
|
|
0, 0, 0
|
|
};
|
|
|
|
//----------------------------------------------------------------------------//
|
|
// Helper to allocate a vertex buffer and initialse a Ogre::RenderOperation
|
|
static void initialiseRenderOp(
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
Ogre::v1::RenderOperation& rop,
|
|
Ogre::v1::HardwareVertexBufferSharedPtr& vb,
|
|
#else
|
|
Ogre::RenderOperation& rop,
|
|
Ogre::HardwareVertexBufferSharedPtr& vb,
|
|
#endif
|
|
size_t count)
|
|
{
|
|
using namespace Ogre;
|
|
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
using namespace Ogre::v1;
|
|
#endif
|
|
|
|
// basic initialisation of render op
|
|
rop.vertexData = OGRE_NEW VertexData();
|
|
rop.operationType = RenderOperation::OT_TRIANGLE_LIST;
|
|
rop.useIndexes = false;
|
|
|
|
// setup vertex declaration for format we will use
|
|
VertexDeclaration* vd = rop.vertexData->vertexDeclaration;
|
|
size_t vd_offset = 0;
|
|
vd->addElement(0, vd_offset, VET_FLOAT3, VES_POSITION);
|
|
vd_offset += VertexElement::getTypeSize(VET_FLOAT3);
|
|
vd->addElement(0, vd_offset, VET_COLOUR, VES_DIFFUSE);
|
|
vd_offset += VertexElement::getTypeSize(VET_COLOUR);
|
|
vd->addElement(0, vd_offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
|
|
|
|
// create hardware vertex buffer
|
|
vb = HardwareBufferManager::getSingleton().createVertexBuffer(
|
|
vd->getVertexSize(0), count,
|
|
HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE,
|
|
false);
|
|
|
|
// bind vertex buffer
|
|
rop.vertexData->vertexBufferBinding->setBinding(0, vb);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
// Helper to cleanup what initialiseRenderOp did.
|
|
static void cleanupRenderOp(
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
Ogre::v1::RenderOperation& rop,
|
|
Ogre::v1::HardwareVertexBufferSharedPtr& vb
|
|
#else
|
|
Ogre::RenderOperation& rop,
|
|
Ogre::HardwareVertexBufferSharedPtr& vb
|
|
#endif
|
|
)
|
|
{
|
|
OGRE_DELETE rop.vertexData;
|
|
rop.vertexData = 0;
|
|
OGRE_RESET(vb);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
OgreGeometryBuffer::OgreGeometryBuffer(OgreRenderer& owner,
|
|
Ogre::RenderSystem& rs) :
|
|
d_owner(owner),
|
|
d_renderSystem(rs),
|
|
d_activeTexture(0),
|
|
d_clipRect(0, 0, 0, 0),
|
|
d_clippingActive(true),
|
|
d_translation(0, 0, 0),
|
|
d_rotation(Quaternion::IDENTITY),
|
|
d_pivot(0, 0, 0),
|
|
d_effect(0),
|
|
d_texelOffset(rs.getHorizontalTexelOffset(), rs.getVerticalTexelOffset()),
|
|
d_matrixValid(false),
|
|
d_sync(false)
|
|
{
|
|
initialiseRenderOp(d_renderOp, d_hwBuffer, 64);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
OgreGeometryBuffer::~OgreGeometryBuffer()
|
|
{
|
|
cleanupRenderOp(d_renderOp, d_hwBuffer);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::draw() const
|
|
{
|
|
if (!d_sync)
|
|
syncHardwareBuffer();
|
|
|
|
d_owner.setWorldMatrix(getMatrix());
|
|
d_owner.setupRenderingBlendMode(d_blendMode);
|
|
d_owner.updateShaderParams();
|
|
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
Ogre::Viewport* previousViewport = d_renderSystem._getViewport();
|
|
Ogre::Viewport* currentViewport = d_owner.getOgreRenderTarget()->getViewport(0);
|
|
|
|
Rectf previousClipRect;
|
|
previousClipRect.left(currentViewport->getScissorLeft());
|
|
previousClipRect.top(currentViewport->getScissorTop());
|
|
previousClipRect.right(currentViewport->getScissorWidth());
|
|
previousClipRect.bottom(currentViewport->getScissorHeight());
|
|
#endif
|
|
|
|
const int pass_count = d_effect ? d_effect->getPassCount() : 1;
|
|
for (int pass = 0; pass < pass_count; ++pass)
|
|
{
|
|
// set up RenderEffect
|
|
if (d_effect)
|
|
d_effect->performPreRenderFunctions(pass);
|
|
|
|
// draw the batches
|
|
size_t pos = 0;
|
|
BatchList::const_iterator i = d_batches.begin();
|
|
for ( ; i != d_batches.end(); ++i)
|
|
{
|
|
// Set up clipping for this buffer
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
if (i->clip)
|
|
{
|
|
int actualWidth = currentViewport->getActualWidth();
|
|
int actualHeight = currentViewport->getActualHeight();
|
|
float scissorsLeft = d_clipRect.left() / actualWidth;
|
|
float scissorsTop = d_clipRect.top() / actualHeight;
|
|
float scissorsWidth = (d_clipRect.right() - d_clipRect.left()) / actualWidth;
|
|
float scissorsHeight = (d_clipRect.bottom() - d_clipRect.top()) / actualHeight;
|
|
currentViewport->setScissors(scissorsLeft, scissorsTop, scissorsWidth, scissorsHeight);
|
|
}
|
|
else
|
|
{
|
|
currentViewport->setScissors(previousClipRect.left(), previousClipRect.top(),
|
|
previousClipRect.right(), previousClipRect.bottom());
|
|
}
|
|
|
|
d_renderSystem._setViewport(currentViewport);
|
|
#else
|
|
d_renderSystem.setScissorTest(
|
|
i->clip, Ogre::Rect((long)d_clipRect.left(), (long)d_clipRect.top(),
|
|
(long)d_clipRect.right(), (long)d_clipRect.bottom()));
|
|
#endif
|
|
|
|
d_renderOp.vertexData->vertexStart = pos;
|
|
d_renderOp.vertexData->vertexCount = i->vertexCount;
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
d_renderSystem._setTexture(0, true, i->texture.get());
|
|
#else
|
|
d_renderSystem._setTexture(0, true, i->texture);
|
|
#endif
|
|
initialiseTextureStates();
|
|
d_renderSystem._render(d_renderOp);
|
|
pos += i->vertexCount;
|
|
}
|
|
}
|
|
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
currentViewport->setScissors(previousClipRect.left(), previousClipRect.top(),
|
|
previousClipRect.right(), previousClipRect.bottom());
|
|
d_renderSystem._setViewport(previousViewport);
|
|
#endif
|
|
|
|
// clean up RenderEffect
|
|
if (d_effect)
|
|
d_effect->performPostRenderFunctions();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setTranslation(const Vector3f& v)
|
|
{
|
|
d_translation = v;
|
|
d_matrixValid = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setRotation(const Quaternion& r)
|
|
{
|
|
d_rotation = r;
|
|
d_matrixValid = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setPivot(const Vector3f& p)
|
|
{
|
|
d_pivot = p;
|
|
d_matrixValid = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setClippingRegion(const Rectf& region)
|
|
{
|
|
d_clipRect.top(ceguimax(0.0f, region.top()));
|
|
d_clipRect.bottom(ceguimax(0.0f, region.bottom()));
|
|
d_clipRect.left(ceguimax(0.0f, region.left()));
|
|
d_clipRect.right(ceguimax(0.0f, region.right()));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::appendVertex(const Vertex& vertex)
|
|
{
|
|
appendGeometry(&vertex, 1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::appendGeometry(const Vertex* const vbuff,
|
|
uint vertex_count)
|
|
{
|
|
// see if we should start a new batch
|
|
Ogre::TexturePtr t;
|
|
if (d_activeTexture)
|
|
t = d_activeTexture->getOgreTexture();
|
|
|
|
if (d_batches.empty() ||
|
|
d_batches.back().texture != t ||
|
|
d_batches.back().clip != d_clippingActive)
|
|
{
|
|
const BatchInfo batch = {t, 0, d_clippingActive};
|
|
d_batches.push_back(batch);
|
|
}
|
|
|
|
// update size of current batch
|
|
d_batches.back().vertexCount += vertex_count;
|
|
|
|
// buffer these vertices
|
|
OgreVertex v;
|
|
for (uint i = 0; i < vertex_count; ++i)
|
|
{
|
|
const Vertex& vs = vbuff[i];
|
|
// convert from CEGUI::Vertex to something directly usable by Ogre.
|
|
v.x = vs.position.d_x + d_texelOffset.d_x;
|
|
v.y = vs.position.d_y + d_texelOffset.d_y;
|
|
v.z = vs.position.d_z;
|
|
v.diffuse = colourToOgre(vs.colour_val);
|
|
v.u = vs.tex_coords.d_x;
|
|
v.v = vs.tex_coords.d_y;
|
|
|
|
d_vertices.push_back(v);
|
|
}
|
|
|
|
d_sync = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setActiveTexture(Texture* texture)
|
|
{
|
|
d_activeTexture = static_cast<OgreTexture*>(texture);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::reset()
|
|
{
|
|
d_vertices.clear();
|
|
d_batches.clear();
|
|
d_activeTexture = 0;
|
|
d_sync = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
Texture* OgreGeometryBuffer::getActiveTexture() const
|
|
{
|
|
return d_activeTexture;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
uint OgreGeometryBuffer::getVertexCount() const
|
|
{
|
|
return d_vertices.size();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
uint OgreGeometryBuffer::getBatchCount() const
|
|
{
|
|
return d_batches.size();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setRenderEffect(RenderEffect* effect)
|
|
{
|
|
d_effect = effect;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
RenderEffect* OgreGeometryBuffer::getRenderEffect()
|
|
{
|
|
return d_effect;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
Ogre::RGBA OgreGeometryBuffer::colourToOgre(const Colour& col) const
|
|
{
|
|
return Ogre::ColourValue(col.getRed(),
|
|
col.getGreen(),
|
|
col.getBlue(),
|
|
col.getAlpha()).getAsBYTE();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::updateMatrix() const
|
|
{
|
|
// translation to position geometry and offset to pivot point
|
|
Ogre::Matrix4 trans;
|
|
trans.makeTrans(d_translation.d_x + d_pivot.d_x,
|
|
d_translation.d_y + d_pivot.d_y,
|
|
d_translation.d_z + d_pivot.d_z);
|
|
|
|
// rotation
|
|
Ogre::Matrix4 rot(Ogre::Quaternion(
|
|
d_rotation.d_w, d_rotation.d_x, d_rotation.d_y, d_rotation.d_z));
|
|
|
|
// translation to remove rotation pivot offset
|
|
Ogre::Matrix4 inv_pivot_trans;
|
|
inv_pivot_trans.makeTrans(-d_pivot.d_x, -d_pivot.d_y, -d_pivot.d_z);
|
|
|
|
// calculate final matrix
|
|
d_matrix = trans * rot * inv_pivot_trans;
|
|
|
|
d_matrixValid = true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::syncHardwareBuffer() const
|
|
{
|
|
// Reallocate h/w buffer as requied
|
|
size_t size = d_hwBuffer->getNumVertices();
|
|
const size_t required_size = d_vertices.size();
|
|
if(size < required_size)
|
|
{
|
|
// calculate new size to use
|
|
while(size < required_size)
|
|
size *= 2;
|
|
|
|
// Reallocate the buffer
|
|
cleanupRenderOp(d_renderOp, d_hwBuffer);
|
|
initialiseRenderOp(d_renderOp, d_hwBuffer, size);
|
|
}
|
|
|
|
// copy vertex data into hw buffer
|
|
if (required_size > 0)
|
|
{
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
std::memcpy(d_hwBuffer->lock(Ogre::v1::HardwareVertexBuffer::HBL_DISCARD),
|
|
&d_vertices[0], sizeof(OgreVertex) * d_vertices.size());
|
|
#else
|
|
std::memcpy(d_hwBuffer->lock(Ogre::HardwareVertexBuffer::HBL_DISCARD),
|
|
&d_vertices[0], sizeof(OgreVertex) * d_vertices.size());
|
|
#endif
|
|
|
|
d_hwBuffer->unlock();
|
|
}
|
|
|
|
d_sync = true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
const Ogre::Matrix4& OgreGeometryBuffer::getMatrix() const
|
|
{
|
|
if (!d_matrixValid)
|
|
updateMatrix();
|
|
|
|
return d_matrix;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::initialiseTextureStates() const
|
|
{
|
|
using namespace Ogre;
|
|
d_renderSystem._setTextureCoordCalculation(0, TEXCALC_NONE);
|
|
d_renderSystem._setTextureCoordSet(0, 0);
|
|
#ifdef CEGUI_USE_OGRE_HLMS
|
|
d_renderSystem._setTextureMatrix(0, Matrix4::IDENTITY);
|
|
d_renderSystem._setTextureBlendMode(0, S_colourBlendMode);
|
|
d_renderSystem._setTextureBlendMode(0, S_alphaBlendMode);
|
|
d_renderSystem._disableTextureUnitsFrom(1);
|
|
|
|
d_renderSystem._setHlmsSamplerblock(0, d_owner.getHlmsSamplerblock());
|
|
#else
|
|
d_renderSystem._setSampler(0, d_owner.getSampler());
|
|
d_renderSystem._setTextureMatrix(0, Matrix4::IDENTITY);
|
|
d_renderSystem._setAlphaRejectSettings(CMPF_ALWAYS_PASS, 0, false);
|
|
d_renderSystem._setTextureBlendMode(0, S_colourBlendMode);
|
|
d_renderSystem._setTextureBlendMode(0, S_alphaBlendMode);
|
|
d_renderSystem._disableTextureUnitsFrom(1);
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
void OgreGeometryBuffer::setClippingActive(const bool active)
|
|
{
|
|
d_clippingActive = active;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
bool OgreGeometryBuffer::isClippingActive() const
|
|
{
|
|
return d_clippingActive;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
} // End of CEGUI namespace section
|