/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #include "OgreStableHeaders.h" // RenderSystem implementation // Note that most of this class is abstract since // we cannot know how to implement the behaviour without // being aware of the 3D API. However there are a few // simple functions which can have a base implementation #include "OgreRenderSystem.h" #include "OgreRoot.h" #include "OgreViewport.h" #include "OgreException.h" #include "OgreRenderTarget.h" #include "OgreRenderWindow.h" #include "OgreMeshManager.h" #include "OgreMaterial.h" #include "OgreTimer.h" #include "OgreHardwarePixelBuffer.h" #include "OgreHardwareOcclusionQuery.h" namespace Ogre { const PlaneList Renderable::msDummyPlaneList; // FIX ME: temporary static const TexturePtr sNullTexPtr; //----------------------------------------------------------------------- RenderSystem::RenderSystem() : mActiveRenderTarget(0) , mTextureManager(0) , mCapabilities(0) , mActiveViewport(0) // This means CULL clockwise vertices, i.e. front of poly is counter-clockwise // This makes it the same as OpenGL and other right-handed systems , mCullingMode(CULL_CLOCKWISE) , mVSync(true) , mWBuffer(false) , mInvertVertexWinding(false) , mDisabledTexUnitsFrom(0) , mCurrentPassIterationCount(0) , mVertexProgramBound(false) , mFragmentProgramBound(false) { // instanciate RenderSystemCapabilities mCapabilities = new RenderSystemCapabilities(); } //----------------------------------------------------------------------- RenderSystem::~RenderSystem() { shutdown(); delete mCapabilities; mCapabilities = 0; } //----------------------------------------------------------------------- void RenderSystem::_initRenderTargets(void) { // Init stats for( RenderTargetMap::iterator it = mRenderTargets.begin(); it != mRenderTargets.end(); ++it ) { it->second->resetStatistics(); } } //----------------------------------------------------------------------- void RenderSystem::_updateAllRenderTargets(void) { // Update all in order of priority // This ensures render-to-texture targets get updated before render windows RenderTargetPriorityMap::iterator itarg, itargend; itargend = mPrioritisedRenderTargets.end(); for( itarg = mPrioritisedRenderTargets.begin(); itarg != itargend; ++itarg ) { if( itarg->second->isActive() && itarg->second->isAutoUpdated()) itarg->second->update(); } } //----------------------------------------------------------------------- RenderWindow* RenderSystem::initialise(bool autoCreateWindow, const String& windowTitle) { // Have I been registered by call to Root::setRenderSystem? /** Don't do this anymore, just allow via Root RenderSystem* regPtr = Root::getSingleton().getRenderSystem(); if (!regPtr || regPtr != this) // Register self - library user has come to me direct Root::getSingleton().setRenderSystem(this); */ // Subclasses should take it from here // They should ALL call this superclass method from // their own initialise() implementations. mVertexProgramBound = false; mFragmentProgramBound = false; return 0; } //--------------------------------------------------------------------- RenderTexture * RenderSystem::createRenderTexture( const String & name, unsigned int width, unsigned int height, TextureType texType, PixelFormat internalFormat, const NameValuePairList *miscParams ) { /// Create a new 2D texture, and return surface to render to TexturePtr mTexture = TextureManager::getSingleton().createManual( name, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, texType, width, height, 0, internalFormat, TU_RENDERTARGET ); // Ensure texture loaded and internal resources created mTexture->load(); return mTexture->getBuffer()->getRenderTarget(); } //--------------------------------------------------------------------------------------------- void RenderSystem::destroyRenderWindow(const String& name) { destroyRenderTarget(name); } //--------------------------------------------------------------------------------------------- void RenderSystem::destroyRenderTexture(const String& name) { destroyRenderTarget(name); } //--------------------------------------------------------------------------------------------- void RenderSystem::destroyRenderTarget(const String& name) { RenderTarget* rt = detachRenderTarget(name); delete rt; } //--------------------------------------------------------------------------------------------- void RenderSystem::attachRenderTarget( RenderTarget &target ) { assert( target.getPriority() < OGRE_NUM_RENDERTARGET_GROUPS ); mRenderTargets.insert( RenderTargetMap::value_type( target.getName(), &target ) ); mPrioritisedRenderTargets.insert( RenderTargetPriorityMap::value_type(target.getPriority(), &target )); } //--------------------------------------------------------------------------------------------- RenderTarget * RenderSystem::getRenderTarget( const String &name ) { RenderTargetMap::iterator it = mRenderTargets.find( name ); RenderTarget *ret = NULL; if( it != mRenderTargets.end() ) { ret = it->second; } return ret; } //--------------------------------------------------------------------------------------------- RenderTarget * RenderSystem::detachRenderTarget( const String &name ) { RenderTargetMap::iterator it = mRenderTargets.find( name ); RenderTarget *ret = NULL; if( it != mRenderTargets.end() ) { ret = it->second; /* Remove the render target from the priority groups. */ RenderTargetPriorityMap::iterator itarg, itargend; itargend = mPrioritisedRenderTargets.end(); for( itarg = mPrioritisedRenderTargets.begin(); itarg != itargend; ++itarg ) { if( itarg->second == ret ) { mPrioritisedRenderTargets.erase( itarg ); break; } } mRenderTargets.erase( it ); } /// If detached render target is the active render target, reset active render target if(ret == mActiveRenderTarget) mActiveRenderTarget = 0; return ret; } //----------------------------------------------------------------------- Viewport* RenderSystem::_getViewport(void) { return mActiveViewport; } //----------------------------------------------------------------------- void RenderSystem::_setTextureUnitSettings(size_t texUnit, TextureUnitState& tl) { // This method is only ever called to set a texture unit to valid details // The method _disableTextureUnit is called to turn a unit off const TexturePtr& tex = tl._getTexturePtr(); // Vertex texture binding? if (mCapabilities->hasCapability(RSC_VERTEX_TEXTURE_FETCH) && !mCapabilities->getVertexTextureUnitsShared()) { if (tl.getBindingType() == TextureUnitState::BT_VERTEX) { // Bind vertex texture _setVertexTexture(texUnit, tex); // bind nothing to fragment unit (hardware isn't shared but fragment // unit can't be using the same index _setTexture(texUnit, true, sNullTexPtr); } else { // vice versa _setVertexTexture(texUnit, sNullTexPtr); _setTexture(texUnit, true, tex); } } else { // Shared vertex / fragment textures or no vertex texture support // Bind texture (may be blank) _setTexture(texUnit, true, tex); } // Set texture coordinate set _setTextureCoordSet(texUnit, tl.getTextureCoordSet()); // Set texture layer filtering _setTextureUnitFiltering(texUnit, tl.getTextureFiltering(FT_MIN), tl.getTextureFiltering(FT_MAG), tl.getTextureFiltering(FT_MIP)); // Set texture layer filtering _setTextureLayerAnisotropy(texUnit, tl.getTextureAnisotropy()); // Set mipmap biasing _setTextureMipmapBias(texUnit, tl.getTextureMipmapBias()); // Set blend modes // Note, colour before alpha is important _setTextureBlendMode(texUnit, tl.getColourBlendMode()); _setTextureBlendMode(texUnit, tl.getAlphaBlendMode()); // Texture addressing mode const TextureUnitState::UVWAddressingMode& uvw = tl.getTextureAddressingMode(); _setTextureAddressingMode(texUnit, uvw); // Set texture border colour only if required if (uvw.u == TextureUnitState::TAM_BORDER || uvw.v == TextureUnitState::TAM_BORDER || uvw.w == TextureUnitState::TAM_BORDER) { _setTextureBorderColour(texUnit, tl.getTextureBorderColour()); } // Set texture effects TextureUnitState::EffectMap::iterator effi; // Iterate over new effects bool anyCalcs = false; for (effi = tl.mEffects.begin(); effi != tl.mEffects.end(); ++effi) { switch (effi->second.type) { case TextureUnitState::ET_ENVIRONMENT_MAP: if (effi->second.subtype == TextureUnitState::ENV_CURVED) { _setTextureCoordCalculation(texUnit, TEXCALC_ENVIRONMENT_MAP); anyCalcs = true; } else if (effi->second.subtype == TextureUnitState::ENV_PLANAR) { _setTextureCoordCalculation(texUnit, TEXCALC_ENVIRONMENT_MAP_PLANAR); anyCalcs = true; } else if (effi->second.subtype == TextureUnitState::ENV_REFLECTION) { _setTextureCoordCalculation(texUnit, TEXCALC_ENVIRONMENT_MAP_REFLECTION); anyCalcs = true; } else if (effi->second.subtype == TextureUnitState::ENV_NORMAL) { _setTextureCoordCalculation(texUnit, TEXCALC_ENVIRONMENT_MAP_NORMAL); anyCalcs = true; } break; case TextureUnitState::ET_UVSCROLL: case TextureUnitState::ET_USCROLL: case TextureUnitState::ET_VSCROLL: case TextureUnitState::ET_ROTATE: case TextureUnitState::ET_TRANSFORM: break; case TextureUnitState::ET_PROJECTIVE_TEXTURE: _setTextureCoordCalculation(texUnit, TEXCALC_PROJECTIVE_TEXTURE, effi->second.frustum); anyCalcs = true; break; } } // Ensure any previous texcoord calc settings are reset if there are now none if (!anyCalcs) { _setTextureCoordCalculation(texUnit, TEXCALC_NONE); } // Change tetxure matrix _setTextureMatrix(texUnit, tl.getTextureTransform()); } //----------------------------------------------------------------------- void RenderSystem::_setTexture(size_t unit, bool enabled, const String &texname) { TexturePtr t = TextureManager::getSingleton().getByName(texname); _setTexture(unit, enabled, t); } //----------------------------------------------------------------------- void RenderSystem::_setVertexTexture(size_t unit, const TexturePtr& tex) { OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "This rendersystem does not support separate vertex texture samplers, " "you should use the regular texture samplers which are shared between " "the vertex and fragment units.", "RenderSystem::_setVertexTexture"); } //----------------------------------------------------------------------- void RenderSystem::_disableTextureUnit(size_t texUnit) { _setTexture(texUnit, false, sNullTexPtr); } //--------------------------------------------------------------------- void RenderSystem::_disableTextureUnitsFrom(size_t texUnit) { size_t disableTo = mCapabilities->getNumTextureUnits(); if (disableTo > mDisabledTexUnitsFrom) disableTo = mDisabledTexUnitsFrom; mDisabledTexUnitsFrom = texUnit; for (size_t i = texUnit; i < disableTo; ++i) { _disableTextureUnit(i); } } //----------------------------------------------------------------------- void RenderSystem::_setTextureUnitFiltering(size_t unit, FilterOptions minFilter, FilterOptions magFilter, FilterOptions mipFilter) { _setTextureUnitFiltering(unit, FT_MIN, minFilter); _setTextureUnitFiltering(unit, FT_MAG, magFilter); _setTextureUnitFiltering(unit, FT_MIP, mipFilter); } //----------------------------------------------------------------------- CullingMode RenderSystem::_getCullingMode(void) const { return mCullingMode; } //----------------------------------------------------------------------- bool RenderSystem::getWaitForVerticalBlank(void) const { return mVSync; } //----------------------------------------------------------------------- void RenderSystem::setWaitForVerticalBlank(bool enabled) { mVSync = enabled; } bool RenderSystem::getWBufferEnabled(void) const { return mWBuffer; } //----------------------------------------------------------------------- void RenderSystem::setWBufferEnabled(bool enabled) { mWBuffer = enabled; } //----------------------------------------------------------------------- void RenderSystem::shutdown(void) { // Remove occlusion queries for (HardwareOcclusionQueryList::iterator i = mHwOcclusionQueries.begin(); i != mHwOcclusionQueries.end(); ++i) { delete *i; } mHwOcclusionQueries.clear(); // Remove all the render targets. // (destroy primary target last since others may depend on it) RenderTarget* primary = 0; for (RenderTargetMap::iterator it = mRenderTargets.begin(); it != mRenderTargets.end(); ++it) { if (!primary && it->second->isPrimary()) primary = it->second; else delete it->second; } delete primary; mRenderTargets.clear(); mPrioritisedRenderTargets.clear(); } //----------------------------------------------------------------------- void RenderSystem::_beginGeometryCount(void) { mBatchCount = mFaceCount = mVertexCount = 0; } //----------------------------------------------------------------------- unsigned int RenderSystem::_getFaceCount(void) const { return static_cast< unsigned int >( mFaceCount ); } //----------------------------------------------------------------------- unsigned int RenderSystem::_getBatchCount(void) const { return static_cast< unsigned int >( mBatchCount ); } //----------------------------------------------------------------------- unsigned int RenderSystem::_getVertexCount(void) const { return static_cast< unsigned int >( mVertexCount ); } //----------------------------------------------------------------------- void RenderSystem::convertColourValue(const ColourValue& colour, uint32* pDest) { *pDest = VertexElement::convertColourValue(colour, getColourVertexElementType()); } //----------------------------------------------------------------------- void RenderSystem::_setWorldMatrices(const Matrix4* m, unsigned short count) { // Do nothing with these matrices here, it never used for now, // derived class should take care with them if required. // Set hardware matrix to nothing _setWorldMatrix(Matrix4::IDENTITY); } //----------------------------------------------------------------------- void RenderSystem::_render(const RenderOperation& op) { // Update stats size_t val; if (op.useIndexes) val = op.indexData->indexCount; else val = op.vertexData->vertexCount; // account for a pass having multiple iterations if (mCurrentPassIterationCount > 1) val *= mCurrentPassIterationCount; switch(op.operationType) { case RenderOperation::OT_TRIANGLE_LIST: mFaceCount += val / 3; break; case RenderOperation::OT_TRIANGLE_STRIP: case RenderOperation::OT_TRIANGLE_FAN: mFaceCount += val - 2; break; case RenderOperation::OT_POINT_LIST: case RenderOperation::OT_LINE_LIST: case RenderOperation::OT_LINE_STRIP: break; } mVertexCount += op.vertexData->vertexCount; mBatchCount += mCurrentPassIterationCount; } //----------------------------------------------------------------------- void RenderSystem::setInvertVertexWinding(bool invert) { mInvertVertexWinding = invert; } //----------------------------------------------------------------------- void RenderSystem::setClipPlane (ushort index, const Plane &p) { setClipPlane (index, p.normal.x, p.normal.y, p.normal.z, p.d); } //----------------------------------------------------------------------- void RenderSystem::_notifyCameraRemoved(const Camera* cam) { RenderTargetMap::iterator i, iend; iend = mRenderTargets.end(); for (i = mRenderTargets.begin(); i != iend; ++i) { RenderTarget* target = i->second; target->_notifyCameraRemoved(cam); } } //--------------------------------------------------------------------- bool RenderSystem::updatePassIterationRenderState(void) { if (mCurrentPassIterationCount <= 1) return false; --mCurrentPassIterationCount; if (!mActiveVertexGpuProgramParameters.isNull()) { mActiveVertexGpuProgramParameters->incPassIterationNumber(); bindGpuProgramPassIterationParameters(GPT_VERTEX_PROGRAM); } if (!mActiveFragmentGpuProgramParameters.isNull()) { mActiveFragmentGpuProgramParameters->incPassIterationNumber(); bindGpuProgramPassIterationParameters(GPT_FRAGMENT_PROGRAM); } return true; } //----------------------------------------------------------------------- void RenderSystem::addListener(Listener* l) { mEventListeners.push_back(l); } //----------------------------------------------------------------------- void RenderSystem::removeListener(Listener* l) { mEventListeners.remove(l); } //----------------------------------------------------------------------- void RenderSystem::fireEvent(const String& name, const NameValuePairList* params) { for(ListenerList::iterator i = mEventListeners.begin(); i != mEventListeners.end(); ++i) { (*i)->eventOccurred(name, params); } } //----------------------------------------------------------------------- void RenderSystem::destroyHardwareOcclusionQuery( HardwareOcclusionQuery *hq) { HardwareOcclusionQueryList::iterator i = std::find(mHwOcclusionQueries.begin(), mHwOcclusionQueries.end(), hq); if (i != mHwOcclusionQueries.end()) { mHwOcclusionQueries.erase(i); delete hq; } } //----------------------------------------------------------------------- void RenderSystem::bindGpuProgram(GpuProgram* prg) { switch(prg->getType()) { case GPT_VERTEX_PROGRAM: mVertexProgramBound = true; break; case GPT_FRAGMENT_PROGRAM: mFragmentProgramBound = true; break; } } //----------------------------------------------------------------------- void RenderSystem::unbindGpuProgram(GpuProgramType gptype) { switch(gptype) { case GPT_VERTEX_PROGRAM: mVertexProgramBound = false; break; case GPT_FRAGMENT_PROGRAM: mFragmentProgramBound = false; break; } } //----------------------------------------------------------------------- bool RenderSystem::isGpuProgramBound(GpuProgramType gptype) { switch(gptype) { case GPT_VERTEX_PROGRAM: return mVertexProgramBound; case GPT_FRAGMENT_PROGRAM: return mFragmentProgramBound; } // Make compiler happy return false; } }