ember/external/CEGUIOgreRenderer/GeometryBuffer.cpp
2022-01-23 20:35:27 +01:00

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