/*********************************************************************** 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 #include #include #ifdef CEGUI_USE_OGRE_HLMS #include #include #include #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(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