/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team 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. ----------------------------------------------------------------------------- */ #ifndef __RenderQueueSortingGrouping_H__ #define __RenderQueueSortingGrouping_H__ // Precompiler options #include "OgrePrerequisites.h" #include "OgreIteratorWrappers.h" #include "OgreMaterial.h" #include "OgreTechnique.h" #include "OgrePass.h" #include "OgreMaterialManager.h" namespace Ogre { /** Renderables in the queue grouped by priority. @remarks This class simply groups renderables for rendering. All the renderables contained in this class are destined for the same RenderQueueGroup (coarse groupings like those between the main scene and overlays) and have the same priority (fine groupings for detailed overlap control). @par This class optimises the grouped renderables by sorting them by material to reduce render state changes, and outsorts transparent objects. */ #ifdef GTP_VISIBILITY_MODIFIED_OGRE class _OgreExport RenderPriorityGroup #else class _OgrePrivate RenderPriorityGroup #endif // GTP_VISIBILITY_MODIFIED_OGRE { /** Internal struct reflecting a single Pass for a Renderable. This is used to sort transparent objects. */ struct RenderablePass { /// Pointer to the Renderable details Renderable* renderable; /// Pointer to the Pass Pass* pass; RenderablePass(Renderable* rend, Pass* p) :renderable(rend), pass(p) {} }; /// Comparator to order non-transparent object passes struct SolidQueueItemLess { bool _OgreExport operator()(const Pass* a, const Pass* b) const { // Sort by passHash, which is pass, then texture unit changes unsigned long hasha = a->getHash(); unsigned long hashb = b->getHash(); if (hasha == hashb) { // Must differentiate by pointer incase 2 passes end up with the same hash return a < b; } else { return hasha < hashb; } } }; /// Comparator to order transparent object passes struct TransparentQueueItemLess { const Camera* camera; bool _OgreExport operator()(const RenderablePass& a, const RenderablePass& b) const { if (a.renderable == b.renderable) { // Same renderable, sort by pass hash return a.pass->getHash() < b.pass->getHash(); } else { // Different renderables, sort by depth Real adepth = a.renderable->getSquaredViewDepth(camera); Real bdepth = b.renderable->getSquaredViewDepth(camera); if (adepth == bdepth) { // Must return deterministic result, doesn't matter what return a.pass < b.pass; } else { // Sort DESCENDING by depth (ie far objects first) return (adepth > bdepth); } } } }; public: /** Vector of RenderablePass objects, this is built on the assumption that vectors only ever increase in size, so even if we do clear() the memory stays allocated, ie fast */ typedef std::vector TransparentRenderablePassList; typedef std::vector RenderableList; /** Map of pass to renderable lists, used for solid objects since ordering within objects not important. */ typedef std::map SolidRenderablePassMap; protected: /// Parent queue group RenderQueueGroup* mParent; bool mSplitPassesByLightingType; bool mSplitNoShadowPasses; /// Solid pass list, used when no shadows, modulative shadows, or ambient passes for additive SolidRenderablePassMap mSolidPasses; /// Solid per-light pass list, used with additive shadows SolidRenderablePassMap mSolidPassesDiffuseSpecular; /// Solid decal (texture) pass list, used with additive shadows SolidRenderablePassMap mSolidPassesDecal; /// Solid pass list, used when shadows are enabled but shadow receive is turned off for these passes SolidRenderablePassMap mSolidPassesNoShadow; /// Transparent list TransparentRenderablePassList mTransparentPasses; /// Totally empties and destroys a solid pass map void destroySolidPassMap(SolidRenderablePassMap& passmap); /// remove a pass entry from all solid pass maps void removeSolidPassEntry(Pass* p); /// Clear a solid pass map at the end of a frame void clearSolidPassMap(SolidRenderablePassMap& passmap); /// Internal method for adding a solid renderable void addSolidRenderable(Technique* pTech, Renderable* rend, bool toNoShadowMap); /// Internal method for adding a solid renderable void addSolidRenderableSplitByLightType(Technique* pTech, Renderable* rend); /// Internal method for adding a transparent renderable void addTransparentRenderable(Technique* pTech, Renderable* rend); public: RenderPriorityGroup(RenderQueueGroup* parent, bool splitPassesByLightingType, bool splitNoShadowPasses) :mParent(parent), mSplitPassesByLightingType(splitPassesByLightingType), mSplitNoShadowPasses(splitNoShadowPasses) { } ~RenderPriorityGroup() { // destroy all the pass map entries destroySolidPassMap(mSolidPasses); destroySolidPassMap(mSolidPassesDecal); destroySolidPassMap(mSolidPassesDiffuseSpecular); destroySolidPassMap(mSolidPassesNoShadow); mTransparentPasses.clear(); } /** Get the collection of solid passes currently queued */ const SolidRenderablePassMap& _getSolidPasses(void) const { return mSolidPasses; } /** Get the collection of solid passes currently queued (per-light) */ const SolidRenderablePassMap& _getSolidPassesDiffuseSpecular(void) const { return mSolidPassesDiffuseSpecular; } /** Get the collection of solid passes currently queued (decal textures)*/ const SolidRenderablePassMap& _getSolidPassesDecal(void) const { return mSolidPassesDecal; } /** Get the collection of solid passes for which shadow receipt is disabled*/ const SolidRenderablePassMap& _getSolidPassesNoShadow(void) const { return mSolidPassesNoShadow; } /** Get the collection of transparent passes currently queued */ const TransparentRenderablePassList& _getTransparentPasses(void) const { return mTransparentPasses; } /** Add a renderable to this group. */ void addRenderable(Renderable* pRend); /** Sorts the objects which have been added to the queue; transparent objects by their depth in relation to the passed in Camera. */ void sort(const Camera* cam); /** Clears this group of renderables. */ void clear(void); #ifdef GTP_VISIBILITY_MODIFIED_OGRE enum {SOLID_PASSES = 1, SOLID_PASSES_DECAL = 2, SOLID_PASSES_DIFFUSE_SPECULAR = 4, SOLID_PASSES_NOSHADOW = 8, TRANSPARENT_PASSES = 16}; /** Clears passes determined by parameter. @param passes can be one or several (combined by |) of: SOLID_PASSES SOLID_PASSES_DECAL SOLID_PASSES_DIFFUSE_SPECULAR SOLID_PASSES_NOSHADOW TRANSPARENT_PASSES */ void clear(const int leavePassesInQueue); /** Destroys passes determined by parameter. @param passes can be one or several (combined by |) of: SOLID_PASSES SOLID_PASSES_DECAL SOLID_PASSES_DIFFUSE_SPECULAR SOLID_PASSES_NOSHADOW TRANSPARENT_PASSES */ void destroyPassMaps(const int leavePassesInQueue); #endif // GTP_VISIBILITY_MODIFIED_OGRE /** Sets whether or not the queue will split passes by their lighting type, ie ambient, per-light and decal. */ void setSplitPassesByLightingType(bool split) { mSplitPassesByLightingType = split; } /** Sets whether or not passes which have shadow receive disabled should be separated. */ void setSplitNoShadowPasses(bool split) { mSplitNoShadowPasses = split; } }; /** A grouping level underneath RenderQueue which groups renderables to be issued at coarsely the same time to the renderer. @remarks Each instance of this class itself hold RenderPriorityGroup instances, which are the groupings of renderables by priority for fine control of ordering (not required for most instances). @par This is an internal OGRE class, not intended for apps to use. */ class _OgrePrivate RenderQueueGroup { public: typedef std::map > PriorityMap; typedef MapIterator PriorityMapIterator; protected: RenderQueue* mParent; bool mSplitPassesByLightingType; bool mSplitNoShadowPasses; /// Map of RenderPriorityGroup objects PriorityMap mPriorityGroups; /// Whether shadows are enabled for this queue bool mShadowsEnabled; public: RenderQueueGroup(RenderQueue* parent, bool splitPassesByLightingType, bool splitNoShadowPasses) :mParent(parent), mSplitPassesByLightingType(splitPassesByLightingType), mSplitNoShadowPasses(splitNoShadowPasses), mShadowsEnabled(true) {} ~RenderQueueGroup() { // destroy contents now PriorityMap::iterator i; for (i = mPriorityGroups.begin(); i != mPriorityGroups.end(); ++i) { delete i->second; } } /** Get an iterator for browsing through child contents. */ PriorityMapIterator getIterator(void) { return PriorityMapIterator(mPriorityGroups.begin(), mPriorityGroups.end()); } /** Add a renderable to this group, with the given priority. */ void addRenderable(Renderable* pRend, ushort priority) { // Check if priority group is there PriorityMap::iterator i = mPriorityGroups.find(priority); RenderPriorityGroup* pPriorityGrp; if (i == mPriorityGroups.end()) { // Missing, create pPriorityGrp = new RenderPriorityGroup(this, mSplitPassesByLightingType, mSplitNoShadowPasses); mPriorityGroups.insert(PriorityMap::value_type(priority, pPriorityGrp)); } else { pPriorityGrp = i->second; } // Add pPriorityGrp->addRenderable(pRend); } /** Clears this group of renderables. @param destroy If false, doesn't delete any priority groups, just empties them. Saves on memory deallocations since the chances are rougly the same kinds of renderables are going to be sent to the queue again next time. If true, completely destroys. */ void clear(bool destroy = false) { PriorityMap::iterator i, iend; iend = mPriorityGroups.end(); for (i = mPriorityGroups.begin(); i != iend; ++i) { if (destroy) delete i->second; else i->second->clear(); } if (destroy) mPriorityGroups.clear(); } #ifdef GTP_VISIBILITY_MODIFIED_OGRE void clear(int passes) { PriorityMap::iterator i, iend; iend = mPriorityGroups.end(); for (i = mPriorityGroups.begin(); i != iend; ++i) { i->second->clear(passes); } } void destroyPasses(int passes) { PriorityMap::iterator i, iend; iend = mPriorityGroups.end(); for (i = mPriorityGroups.begin(); i != iend; ++i) { i->second->destroyPassMaps(passes); } } #endif // GTP_VISIBILITY_MODIFIED_OGRE /** Indicate whether a given queue group will be doing any shadow setup. @remarks This method allows you to inform the queue about a queue group, and to indicate whether this group will require shadow processing of any sort. In order to preserve rendering order, OGRE has to treat queue groups as very separate elements of the scene, and this can result in it having to duplicate shadow setup for each group. Therefore, if you know that a group which you are using will never need shadows, you should preregister the group using this method in order to improve the performance. */ void setShadowsEnabled(bool enabled) { mShadowsEnabled = enabled; } /** Are shadows enabled for this queue? */ bool getShadowsEnabled(void) const { return mShadowsEnabled; } /** Sets whether or not the queue will split passes by their lighting type, ie ambient, per-light and decal. */ void setSplitPassesByLightingType(bool split) { mSplitPassesByLightingType = split; PriorityMap::iterator i, iend; iend = mPriorityGroups.end(); for (i = mPriorityGroups.begin(); i != iend; ++i) { i->second->setSplitPassesByLightingType(split); } } /** Sets whether or not the queue will split passes which have shadow receive turned off (in their parent material), which is needed when certain shadow techniques are used. */ void setSplitNoShadowPasses(bool split) { mSplitNoShadowPasses = split; PriorityMap::iterator i, iend; iend = mPriorityGroups.end(); for (i = mPriorityGroups.begin(); i != iend; ++i) { i->second->setSplitNoShadowPasses(split); } } }; } #endif