00001 /* 00002 ----------------------------------------------------------------------------- 00003 This source file is part of OGRE 00004 (Object-oriented Graphics Rendering Engine) 00005 For the latest info, see http://ogre.sourceforge.net/ 00006 00007 Copyright © 2000-2002 The OGRE Team 00008 Also see acknowledgements in Readme.html 00009 00010 This program is free software; you can redistribute it and/or modify it under 00011 the terms of the GNU Lesser General Public License as published by the Free Software 00012 Foundation; either version 2 of the License, or (at your option) any later 00013 version. 00014 00015 This program is distributed in the hope that it will be useful, but WITHOUT 00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 00018 00019 You should have received a copy of the GNU Lesser General Public License along with 00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to 00022 http://www.gnu.org/copyleft/lesser.txt. 00023 ----------------------------------------------------------------------------- 00024 */ 00025 #include "OgreStableHeaders.h" 00026 00027 #include "OgreSceneManager.h" 00028 00029 #include "OgreCamera.h" 00030 #include "OgreRenderSystem.h" 00031 #include "OgreMeshManager.h" 00032 #include "OgreMesh.h" 00033 #include "OgreSubMesh.h" 00034 #include "OgreEntity.h" 00035 #include "OgreSubEntity.h" 00036 #include "OgreLight.h" 00037 #include "OgreMath.h" 00038 #include "OgreControllerManager.h" 00039 #include "OgreMaterialManager.h" 00040 #include "OgreAnimation.h" 00041 #include "OgreAnimationTrack.h" 00042 #include "OgreRenderQueueSortingGrouping.h" 00043 #include "OgreOverlay.h" 00044 #include "OgreOverlayManager.h" 00045 #include "OgreStringConverter.h" 00046 #include "OgreRenderQueueListener.h" 00047 #include "OgreBillboardSet.h" 00048 #include "OgrePass.h" 00049 #include "OgreTechnique.h" 00050 #include "OgreTextureUnitState.h" 00051 #include "OgreException.h" 00052 #include "OgreLogManager.h" 00053 #include "OgreHardwareBufferManager.h" 00054 #include "OgreRoot.h" 00055 #include "OgreSpotShadowFadePng.h" 00056 #include "OgreGpuProgramManager.h" 00057 #include "OgreGpuProgram.h" 00058 #include "OgreShadowVolumeExtrudeProgram.h" 00059 00060 // This class implements the most basic scene manager 00061 00062 #include <cstdio> 00063 00064 namespace Ogre { 00065 00066 SceneManager::SceneManager() : 00067 mRenderQueue(0), 00068 mSkyPlaneEntity(0), 00069 mSkyPlaneNode(0), 00070 mSkyDomeNode(0), 00071 mSkyBoxNode(0), 00072 mSkyPlaneEnabled(false), 00073 mSkyBoxEnabled(false), 00074 mSkyDomeEnabled(false), 00075 mFogMode(FOG_NONE), 00076 mShadowCasterPlainBlackPass(0), 00077 mShadowReceiverPass(0), 00078 mDisplayNodes(false), 00079 mShowBoundingBoxes(false), 00080 mShadowTechnique(SHADOWTYPE_NONE), 00081 mDebugShadows(false), 00082 mShadowColour(ColourValue(0.25, 0.25, 0.25)), 00083 mShadowDebugPass(0), 00084 mShadowStencilPass(0), 00085 mShadowModulativePass(0), 00086 mShadowMaterialInitDone(false), 00087 mShadowIndexBufferSize(51200), 00088 mFullScreenQuad(0), 00089 mShadowDirLightExtrudeDist(10000), 00090 mIlluminationStage(IRS_NONE), 00091 mShadowTextureSize(512), 00092 mShadowTextureCount(1), 00093 mShadowUseInfiniteFarPlane(true), 00094 mShadowCasterSphereQuery(0), 00095 mShadowCasterAABBQuery(0), 00096 mShadowFarDist(0), 00097 mShadowFarDistSquared(0), 00098 mShadowTextureOffset(0.6), 00099 mShadowTextureFadeStart(0.7), 00100 mShadowTextureFadeEnd(0.9) 00101 00102 { 00103 // Root scene node 00104 mSceneRoot = new SceneNode(this, "root node"); 00105 00106 // init sky 00107 size_t i; 00108 for (i = 0; i < 6; ++i) 00109 { 00110 mSkyBoxEntity[i] = 0; 00111 } 00112 for (i = 0; i < 5; ++i) 00113 { 00114 mSkyDomeEntity[i] = 0; 00115 } 00116 00117 00118 } 00119 00120 SceneManager::~SceneManager() 00121 { 00122 clearScene(); 00123 removeAllCameras(); 00124 delete mSceneRoot; 00125 delete mFullScreenQuad; 00126 delete mShadowCasterSphereQuery; 00127 delete mShadowCasterAABBQuery; 00128 delete mRenderQueue; 00129 } 00130 //----------------------------------------------------------------------- 00131 RenderQueue* SceneManager::getRenderQueue(void) 00132 { 00133 if (!mRenderQueue) 00134 { 00135 initRenderQueue(); 00136 } 00137 return mRenderQueue; 00138 } 00139 //----------------------------------------------------------------------- 00140 void SceneManager::initRenderQueue(void) 00141 { 00142 mRenderQueue = new RenderQueue(); 00143 // init render queues that do not need shadows 00144 mRenderQueue->getQueueGroup(RENDER_QUEUE_BACKGROUND)->setShadowsEnabled(false); 00145 mRenderQueue->getQueueGroup(RENDER_QUEUE_OVERLAY)->setShadowsEnabled(false); 00146 mRenderQueue->getQueueGroup(RENDER_QUEUE_SKIES_EARLY)->setShadowsEnabled(false); 00147 mRenderQueue->getQueueGroup(RENDER_QUEUE_SKIES_LATE)->setShadowsEnabled(false); 00148 } 00149 //----------------------------------------------------------------------- 00150 Camera* SceneManager::createCamera(const String& name) 00151 { 00152 // Check name not used 00153 if (mCameras.find(name) != mCameras.end()) 00154 { 00155 Except( 00156 Exception::ERR_DUPLICATE_ITEM, 00157 "A camera with the name " + name + " already exists", 00158 "SceneManager::createCamera" ); 00159 } 00160 00161 Camera *c = new Camera(name, this); 00162 mCameras.insert(CameraList::value_type(name, c)); 00163 00164 00165 return c; 00166 } 00167 00168 //----------------------------------------------------------------------- 00169 Camera* SceneManager::getCamera(const String& name) 00170 { 00171 CameraList::iterator i = mCameras.find(name); 00172 if (i == mCameras.end()) 00173 { 00174 return 0; 00175 } 00176 else 00177 { 00178 return i->second; 00179 } 00180 } 00181 00182 //----------------------------------------------------------------------- 00183 void SceneManager::removeCamera(Camera *cam) 00184 { 00185 // Find in list 00186 CameraList::iterator i = mCameras.begin(); 00187 for (; i != mCameras.end(); ++i) 00188 { 00189 if (i->second == cam) 00190 { 00191 mCameras.erase(i); 00192 // notify render targets 00193 mDestRenderSystem->_notifyCameraRemoved(cam); 00194 delete cam; 00195 break; 00196 } 00197 } 00198 00199 } 00200 00201 //----------------------------------------------------------------------- 00202 void SceneManager::removeCamera(const String& name) 00203 { 00204 // Find in list 00205 CameraList::iterator i = mCameras.find(name); 00206 if (i != mCameras.end()) 00207 { 00208 // Notify render system 00209 mDestRenderSystem->_notifyCameraRemoved(i->second); 00210 delete i->second; 00211 mCameras.erase(i); 00212 } 00213 00214 } 00215 00216 //----------------------------------------------------------------------- 00217 void SceneManager::removeAllCameras(void) 00218 { 00219 00220 CameraList::iterator i = mCameras.begin(); 00221 for (; i != mCameras.end(); ++i) 00222 { 00223 // Notify render system 00224 mDestRenderSystem->_notifyCameraRemoved(i->second); 00225 delete i->second; 00226 } 00227 mCameras.clear(); 00228 } 00229 00230 //----------------------------------------------------------------------- 00231 Light* SceneManager::createLight(const String& name) 00232 { 00233 // Check name not used 00234 if (mLights.find(name) != mLights.end()) 00235 { 00236 Except( 00237 Exception::ERR_DUPLICATE_ITEM, 00238 "A light with the name " + name + " already exists", 00239 "SceneManager::createLight" ); 00240 } 00241 00242 Light *l = new Light(name); 00243 mLights.insert(SceneLightList::value_type(name, l)); 00244 return l; 00245 } 00246 00247 //----------------------------------------------------------------------- 00248 Light* SceneManager::getLight(const String& name) 00249 { 00250 SceneLightList::iterator i = mLights.find(name); 00251 if (i == mLights.end()) 00252 { 00253 return 0; 00254 } 00255 else 00256 { 00257 return i->second; 00258 } 00259 } 00260 00261 //----------------------------------------------------------------------- 00262 void SceneManager::removeLight(Light *l) 00263 { 00264 // Find in list 00265 SceneLightList::iterator i = mLights.begin(); 00266 for (; i != mLights.end(); ++i) 00267 { 00268 if (i->second == l) 00269 { 00270 mLights.erase(i); 00271 delete l; 00272 break; 00273 } 00274 } 00275 00276 } 00277 00278 //----------------------------------------------------------------------- 00279 void SceneManager::removeLight(const String& name) 00280 { 00281 // Find in list 00282 SceneLightList::iterator i = mLights.find(name); 00283 if (i != mLights.end()) 00284 { 00285 delete i->second; 00286 mLights.erase(i); 00287 } 00288 00289 } 00290 00291 //----------------------------------------------------------------------- 00292 void SceneManager::removeAllLights(void) 00293 { 00294 00295 SceneLightList::iterator i = mLights.begin(); 00296 for (; i != mLights.end(); ++i) 00297 { 00298 delete i->second; 00299 } 00300 mLights.clear(); 00301 } 00302 //----------------------------------------------------------------------- 00303 bool SceneManager::lightLess::operator()(const Light* a, const Light* b) const 00304 { 00305 return a->tempSquareDist < b->tempSquareDist; 00306 } 00307 //----------------------------------------------------------------------- 00308 void SceneManager::_populateLightList(const Vector3& position, Real radius, LightList& destList) 00309 { 00310 // Really basic trawl of the lights, then sort 00311 // Subclasses could do something smarter 00312 destList.clear(); 00313 Real squaredRadius = radius * radius; 00314 00315 SceneLightList::iterator i, iend; 00316 iend = mLights.end(); 00317 for (i = mLights.begin(); i != iend; ++i) 00318 { 00319 Light* lt = i->second; 00320 if (lt->isVisible()) 00321 { 00322 if (lt->getType() == Light::LT_DIRECTIONAL) 00323 { 00324 // No distance 00325 lt->tempSquareDist = 0.0f; 00326 destList.push_back(lt); 00327 } 00328 else 00329 { 00330 // Calc squared distance 00331 lt->tempSquareDist = (lt->getDerivedPosition() - position).squaredLength(); 00332 lt->tempSquareDist -= squaredRadius; 00333 // only add in-range lights 00334 Real range = lt->getAttenuationRange(); 00335 if (lt->tempSquareDist <= (range * range)) 00336 { 00337 destList.push_back(lt); 00338 } 00339 } 00340 } 00341 } 00342 00343 // Sort 00344 std::sort(destList.begin(), destList.end(), lightLess()); 00345 00346 00347 } 00348 //----------------------------------------------------------------------- 00349 Entity* SceneManager::createEntity(const String& entityName, PrefabType ptype) 00350 { 00351 switch (ptype) 00352 { 00353 case PT_PLANE: 00354 return createEntity(entityName, "Prefab_Plane"); 00355 00356 break; 00357 } 00358 00359 return 0; 00360 } 00361 00362 //----------------------------------------------------------------------- 00363 Entity* SceneManager::createEntity( 00364 const String& entityName, 00365 const String& meshName ) 00366 { 00367 // Check name not used 00368 EntityList::iterator it = mEntities.find( entityName ); 00369 if( it != mEntities.end() ) 00370 { 00371 Except( 00372 Exception::ERR_DUPLICATE_ITEM, 00373 "An entity with the name " + entityName + " already exists", 00374 "SceneManager::createEntity" ); 00375 } 00376 00377 // Get mesh (load if required) 00378 Mesh* pMesh = MeshManager::getSingleton().load( meshName ); 00379 00380 // Create entity 00381 Entity* e = new Entity( entityName, pMesh, this ); 00382 00383 // Add to internal list 00384 mEntities[entityName] = e; //.insert(EntityList::value_type(entityName, e)); 00385 00386 return e; 00387 } 00388 00389 //----------------------------------------------------------------------- 00390 Entity* SceneManager::getEntity(const String& name) 00391 { 00392 EntityList::iterator i = mEntities.find(name); 00393 if (i == mEntities.end()) 00394 { 00395 return 0; 00396 } 00397 else 00398 { 00399 return i->second; 00400 } 00401 } 00402 00403 //----------------------------------------------------------------------- 00404 void SceneManager::removeEntity(Entity *cam) 00405 { 00406 // Find in list 00407 EntityList::iterator i = mEntities.begin(); 00408 for (; i != mEntities.end(); ++i) 00409 { 00410 if (i->second == cam) 00411 { 00412 mEntities.erase(i); 00413 delete cam; 00414 break; 00415 } 00416 } 00417 00418 } 00419 00420 //----------------------------------------------------------------------- 00421 void SceneManager::removeEntity(const String& name) 00422 { 00423 // Find in list 00424 EntityList::iterator i = mEntities.find(name); 00425 if (i != mEntities.end()) 00426 { 00427 delete i->second; 00428 mEntities.erase(i); 00429 } 00430 00431 } 00432 00433 //----------------------------------------------------------------------- 00434 void SceneManager::removeAllEntities(void) 00435 { 00436 00437 EntityList::iterator i = mEntities.begin(); 00438 for (; i != mEntities.end(); ++i) 00439 { 00440 delete i->second; 00441 } 00442 mEntities.clear(); 00443 } 00444 00445 //----------------------------------------------------------------------- 00446 void SceneManager::removeAllBillboardSets(void) 00447 { 00448 // Delete all BillboardSets 00449 for (BillboardSetList::iterator bi = mBillboardSets.begin(); 00450 bi != mBillboardSets.end(); ++bi) 00451 { 00452 delete bi->second; 00453 } 00454 mBillboardSets.clear(); 00455 } 00456 //----------------------------------------------------------------------- 00457 void SceneManager::clearScene(void) 00458 { 00459 // Delete all SceneNodes, except root that is 00460 for (SceneNodeList::iterator i = mSceneNodes.begin(); 00461 i != mSceneNodes.end(); ++i) 00462 { 00463 delete i->second; 00464 } 00465 mSceneNodes.clear(); 00466 mAutoTrackingSceneNodes.clear(); 00467 00468 // Clear root node of all children 00469 mSceneRoot->removeAllChildren(); 00470 mSceneRoot->detachAllObjects(); 00471 00472 removeAllEntities(); 00473 removeAllBillboardSets(); 00474 removeAllLights(); 00475 00476 // Clear animations 00477 destroyAllAnimations(); 00478 00479 // Remove sky nodes since they've been deleted 00480 mSkyBoxNode = mSkyPlaneNode = mSkyDomeNode = 0; 00481 mSkyBoxEnabled = mSkyPlaneEnabled = mSkyDomeEnabled = false; 00482 00483 } 00484 00485 //----------------------------------------------------------------------- 00486 Material* SceneManager::createMaterial(const String& name) 00487 { 00488 // Create using MaterialManager 00489 Material* m = (Material*)MaterialManager::getSingleton().create(name); 00490 00491 00492 return m; 00493 } 00494 //----------------------------------------------------------------------- 00495 Material* SceneManager::getDefaultMaterialSettings(void) 00496 { 00497 return Material::mDefaultSettings; 00498 } 00499 //----------------------------------------------------------------------- 00500 Material* SceneManager::getMaterial(const String& name) 00501 { 00502 return (Material*)MaterialManager::getSingleton().getByName(name); 00503 } 00504 00505 //----------------------------------------------------------------------- 00506 Material* SceneManager::getMaterial(int handle) 00507 { 00508 return static_cast<Material*>( 00509 MaterialManager::getSingleton().getByHandle(handle)); 00510 } 00511 //----------------------------------------------------------------------- 00512 SceneNode* SceneManager::createSceneNode(void) 00513 { 00514 SceneNode* sn = new SceneNode(this); 00515 assert(mSceneNodes.find(sn->getName()) == mSceneNodes.end()); 00516 mSceneNodes[sn->getName()] = sn; 00517 return sn; 00518 } 00519 //----------------------------------------------------------------------- 00520 SceneNode* SceneManager::createSceneNode(const String& name) 00521 { 00522 // Check name not used 00523 if (mSceneNodes.find(name) != mSceneNodes.end()) 00524 { 00525 Except( 00526 Exception::ERR_DUPLICATE_ITEM, 00527 "A scene node with the name " + name + " already exists", 00528 "SceneManager::createSceneNode" ); 00529 } 00530 00531 SceneNode* sn = new SceneNode(this, name); 00532 mSceneNodes[sn->getName()] = sn; 00533 return sn; 00534 } 00535 //----------------------------------------------------------------------- 00536 void SceneManager::destroySceneNode(const String& name) 00537 { 00538 SceneNodeList::iterator i = mSceneNodes.find(name); 00539 00540 if (i == mSceneNodes.end()) 00541 { 00542 Except(Exception::ERR_ITEM_NOT_FOUND, "SceneNode '" + name + "' not found.", 00543 "SceneManager::destroySceneNode"); 00544 } 00545 00546 // Find any scene nodes which are tracking this node, and turn them off 00547 AutoTrackingSceneNodes::iterator ai, aiend; 00548 aiend = mAutoTrackingSceneNodes.end(); 00549 for (ai = mAutoTrackingSceneNodes.begin(); ai != aiend; ++ai) 00550 { 00551 SceneNode* n = *ai; 00552 // Tracking this node 00553 if (n->getAutoTrackTarget() == i->second) 00554 { 00555 // turn off, this will notify SceneManager to remove 00556 n->setAutoTracking(false); 00557 // no need to reset iterator since set erase does not invalidate 00558 } 00559 // node is itself a tracker 00560 else if (n == i->second) 00561 { 00562 mAutoTrackingSceneNodes.erase(ai); 00563 } 00564 } 00565 00566 delete i->second; 00567 mSceneNodes.erase(i); 00568 } 00569 //----------------------------------------------------------------------- 00570 SceneNode* SceneManager::getRootSceneNode(void) const 00571 { 00572 return mSceneRoot; 00573 } 00574 //----------------------------------------------------------------------- 00575 SceneNode* SceneManager::getSceneNode(const String& name) const 00576 { 00577 SceneNodeList::const_iterator i = mSceneNodes.find(name); 00578 00579 if (i == mSceneNodes.end()) 00580 { 00581 Except(Exception::ERR_ITEM_NOT_FOUND, "SceneNode '" + name + "' not found.", 00582 "SceneManager::getSceneNode"); 00583 } 00584 00585 return i->second; 00586 00587 } 00588 //----------------------------------------------------------------------- 00589 Pass* SceneManager::setPass(Pass* pass) 00590 { 00591 static bool lastUsedVertexProgram = false; 00592 static bool lastUsedFragmentProgram = false; 00593 00594 if (mIlluminationStage == IRS_RENDER_TO_TEXTURE) 00595 { 00596 // Derive a special shadow caster pass from this one 00597 pass = deriveShadowCasterPass(pass); 00598 } 00599 else if (mIlluminationStage == IRS_RENDER_MODULATIVE_PASS) 00600 { 00601 pass = deriveShadowReceiverPass(pass); 00602 } 00603 00604 // TEST 00605 /* 00606 LogManager::getSingleton().logMessage("BEGIN PASS " + StringConverter::toString(pass->getIndex()) + 00607 " of " + pass->getParent()->getParent()->getName()); 00608 */ 00609 bool passSurfaceAndLightParams = true; 00610 00611 if (pass->hasVertexProgram()) 00612 { 00613 mDestRenderSystem->bindGpuProgram(pass->getVertexProgram()->_getBindingDelegate()); 00614 // bind parameters later since they can be per-object 00615 lastUsedVertexProgram = true; 00616 // does the vertex program want surface and light params passed to rendersystem? 00617 passSurfaceAndLightParams = pass->getVertexProgram()->getPassSurfaceAndLightStates(); 00618 } 00619 else 00620 { 00621 // Unbind program? 00622 if (lastUsedVertexProgram) 00623 { 00624 mDestRenderSystem->unbindGpuProgram(GPT_VERTEX_PROGRAM); 00625 lastUsedVertexProgram = false; 00626 } 00627 // Set fixed-function vertex parameters 00628 } 00629 00630 if (passSurfaceAndLightParams) 00631 { 00632 // Set surface reflectance properties, only valid if lighting is enabled 00633 if (pass->getLightingEnabled()) 00634 { 00635 mDestRenderSystem->_setSurfaceParams( 00636 pass->getAmbient(), 00637 pass->getDiffuse(), 00638 pass->getSpecular(), 00639 pass->getSelfIllumination(), 00640 pass->getShininess() ); 00641 } 00642 00643 // Dynamic lighting enabled? 00644 mDestRenderSystem->setLightingEnabled(pass->getLightingEnabled()); 00645 } 00646 00647 // Using a fragment program? 00648 if (pass->hasFragmentProgram()) 00649 { 00650 mDestRenderSystem->bindGpuProgram( 00651 pass->getFragmentProgram()->_getBindingDelegate()); 00652 // bind parameters later since they can be per-object 00653 lastUsedFragmentProgram = true; 00654 } 00655 else 00656 { 00657 // Unbind program? 00658 if (lastUsedFragmentProgram) 00659 { 00660 mDestRenderSystem->unbindGpuProgram(GPT_FRAGMENT_PROGRAM); 00661 lastUsedFragmentProgram = false; 00662 } 00663 00664 // Set fixed-function fragment settings 00665 00666 // Fog (assumes we want pixel fog which is the usual) 00667 // New fog params can either be from scene or from material 00668 FogMode newFogMode; 00669 ColourValue newFogColour; 00670 Real newFogStart, newFogEnd, newFogDensity; 00671 if (pass->getFogOverride()) 00672 { 00673 // New fog params from material 00674 newFogMode = pass->getFogMode(); 00675 newFogColour = pass->getFogColour(); 00676 newFogStart = pass->getFogStart(); 00677 newFogEnd = pass->getFogEnd(); 00678 newFogDensity = pass->getFogDensity(); 00679 } 00680 else 00681 { 00682 // New fog params from scene 00683 newFogMode = mFogMode; 00684 newFogColour = mFogColour; 00685 newFogStart = mFogStart; 00686 newFogEnd = mFogEnd; 00687 newFogDensity = mFogDensity; 00688 } 00689 mDestRenderSystem->_setFog( 00690 newFogMode, newFogColour, newFogDensity, newFogStart, newFogEnd); 00691 00692 } 00693 00694 // The rest of the settings are the same no matter whether we use programs or not 00695 00696 // Set scene blending 00697 mDestRenderSystem->_setSceneBlending( 00698 pass->getSourceBlendFactor(), pass->getDestBlendFactor()); 00699 00700 00701 // Texture unit settings 00702 00703 Pass::TextureUnitStateIterator texIter = pass->getTextureUnitStateIterator(); 00704 size_t unit = 0; 00705 while(texIter.hasMoreElements()) 00706 { 00707 TextureUnitState* pTex = texIter.getNext(); 00708 mDestRenderSystem->_setTextureUnitSettings(unit, *pTex); 00709 ++unit; 00710 } 00711 // Disable remaining texture units 00712 mDestRenderSystem->_disableTextureUnitsFrom(pass->getNumTextureUnitStates()); 00713 00714 // Set up non-texture related material settings 00715 // Depth buffer settings 00716 mDestRenderSystem->_setDepthBufferFunction(pass->getDepthFunction()); 00717 mDestRenderSystem->_setDepthBufferCheckEnabled(pass->getDepthCheckEnabled()); 00718 mDestRenderSystem->_setDepthBufferWriteEnabled(pass->getDepthWriteEnabled()); 00719 mDestRenderSystem->_setDepthBias(pass->getDepthBias()); 00720 // Set colour write mode 00721 // Right now we only use on/off, not per-channel 00722 bool colWrite = pass->getColourWriteEnabled(); 00723 mDestRenderSystem->_setColourBufferWriteEnabled(colWrite, colWrite, colWrite, colWrite); 00724 // Culling mode 00725 mDestRenderSystem->_setCullingMode(pass->getCullingMode()); 00726 // Shading 00727 mDestRenderSystem->setShadingType(pass->getShadingMode()); 00728 00729 return pass; 00730 } 00731 //----------------------------------------------------------------------- 00732 void SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays) 00733 { 00734 Root::getSingleton()._setCurrentSceneManager(this); 00735 // Prep Pass for use in debug shadows 00736 initShadowVolumeMaterials(); 00737 // Perform a quick pre-check to see whether we should override far distance 00738 // When using stencil volumes we have to use infinite far distance 00739 // to prevent dark caps getting clipped 00740 if ((mShadowTechnique == SHADOWTYPE_STENCIL_ADDITIVE || 00741 mShadowTechnique == SHADOWTYPE_STENCIL_MODULATIVE) && 00742 camera->getFarClipDistance() != 0 && 00743 mDestRenderSystem->getCapabilities()->hasCapability(RSC_INFINITE_FAR_PLANE) && 00744 mShadowUseInfiniteFarPlane) 00745 { 00746 // infinite far distance 00747 camera->setFarClipDistance(0); 00748 } 00749 00750 mCameraInProgress = camera; 00751 mCamChanged = true; 00752 00753 00754 // Update the scene, only do this once per frame 00755 static unsigned long lastFrameNumber = 0; 00756 unsigned long thisFrameNumber = Root::getSingleton().getCurrentFrameNumber(); 00757 if (thisFrameNumber != lastFrameNumber) 00758 { 00759 // Update animations 00760 _applySceneAnimations(); 00761 // Update controllers 00762 ControllerManager::getSingleton().updateAllControllers(); 00763 lastFrameNumber = thisFrameNumber; 00764 } 00765 00766 // Update scene graph for this camera (can happen multiple times per frame) 00767 _updateSceneGraph(camera); 00768 00769 // Auto-track nodes 00770 AutoTrackingSceneNodes::iterator atsni, atsniend; 00771 atsniend = mAutoTrackingSceneNodes.end(); 00772 for (atsni = mAutoTrackingSceneNodes.begin(); atsni != atsniend; ++atsni) 00773 { 00774 (*atsni)->_autoTrack(); 00775 } 00776 // Auto-track camera if required 00777 camera->_autoTrack(); 00778 00779 00780 // Are we using any shadows at all? 00781 if (mShadowTechnique != SHADOWTYPE_NONE && 00782 mIlluminationStage != IRS_RENDER_TO_TEXTURE) 00783 { 00784 // Locate any lights which could be affecting the frustum 00785 findLightsAffectingFrustum(camera); 00786 if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE 00787 /* || mShadowTechnique == SHADOWTYPE_TEXTURE_SHADOWMAP */) 00788 { 00789 // ******* 00790 // WARNING 00791 // ******* 00792 // This call will result in re-entrant calls to this method 00793 // therefore anything which comes before this is NOT 00794 // guaranteed persistent. Make sure that anything which 00795 // MUST be specific to this camera / target is done 00796 // AFTER THIS POINT 00797 prepareShadowTextures(camera, vp); 00798 // reset the cameras because of the re-entrant call 00799 mCameraInProgress = camera; 00800 mCamChanged = true; 00801 } 00802 } 00803 00804 // Invert vertex winding? 00805 if (camera->isReflected()) 00806 { 00807 mDestRenderSystem->setInvertVertexWinding(true); 00808 } 00809 else 00810 { 00811 mDestRenderSystem->setInvertVertexWinding(false); 00812 } 00813 00814 // Set the viewport 00815 setViewport(vp); 00816 00817 // Tell params about camera 00818 mAutoParamDataSource.setCurrentCamera(camera); 00819 // Set autoparams for finite dir light extrusion 00820 mAutoParamDataSource.setShadowDirLightExtrusionDistance(mShadowDirLightExtrudeDist); 00821 00822 // Tell params about current ambient light 00823 mAutoParamDataSource.setAmbientLightColour(mAmbientLight); 00824 00825 // Tell params about render target 00826 mAutoParamDataSource.setCurrentRenderTarget(vp->getTarget()); 00827 00828 00829 // Set camera window clipping planes (if any) 00830 if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_USER_CLIP_PLANES)) 00831 { 00832 if (camera->isWindowSet()) 00833 { 00834 const std::vector<Plane>& planeList = 00835 camera->getWindowPlanes(); 00836 for (ushort i = 0; i < 4; ++i) 00837 { 00838 mDestRenderSystem->enableClipPlane(i, true); 00839 mDestRenderSystem->setClipPlane(i, planeList[i]); 00840 } 00841 } 00842 else 00843 { 00844 for (ushort i = 0; i < 4; ++i) 00845 { 00846 mDestRenderSystem->enableClipPlane(i, false); 00847 } 00848 } 00849 } 00850 00851 // Clear the render queue 00852 getRenderQueue()->clear(); 00853 00854 // Parse the scene and tag visibles 00855 _findVisibleObjects(camera, 00856 mIlluminationStage == IRS_RENDER_TO_TEXTURE? true : false); 00857 // Add overlays, if viewport deems it 00858 if (vp->getOverlaysEnabled()) 00859 { 00860 OverlayManager::getSingleton()._queueOverlaysForRendering(camera, getRenderQueue(), vp); 00861 } 00862 // Queue skies 00863 _queueSkiesForRendering(camera); 00864 00865 00866 // Don't do view / proj here anymore 00867 // Checked per renderable now, although only changed when required 00868 //mDestRenderSystem->_setViewMatrix(camera->getViewMatrix()); 00869 //mDestRenderSystem->_setProjectionMatrix(camera->getProjectionMatrix()); 00870 00871 mDestRenderSystem->_beginGeometryCount(); 00872 // Begin the frame 00873 mDestRenderSystem->_beginFrame(); 00874 00875 // Set rasterisation mode 00876 mDestRenderSystem->_setRasterisationMode(camera->getDetailLevel()); 00877 00878 // Render scene content 00879 _renderVisibleObjects(); 00880 00881 // End frame 00882 mDestRenderSystem->_endFrame(); 00883 00884 // Notify camera or vis faces 00885 camera->_notifyRenderedFaces(mDestRenderSystem->_getFaceCount()); 00886 00887 00888 00889 } 00890 00891 00892 //----------------------------------------------------------------------- 00893 void SceneManager::_setDestinationRenderSystem(RenderSystem* sys) 00894 { 00895 mDestRenderSystem = sys; 00896 00897 } 00898 00899 00900 //----------------------------------------------------------------------- 00901 void SceneManager::setWorldGeometry(const String& filename) 00902 { 00903 // This default implementation cannot handle world geometry 00904 Except(Exception::ERR_INVALIDPARAMS, 00905 "World geometry is not supported by the generic SceneManager.", 00906 "SceneManager::setWorldGeometry"); 00907 } 00908 00909 //----------------------------------------------------------------------- 00910 bool SceneManager::materialLess::operator() (const Material* x, const Material* y) const 00911 { 00912 // If x transparent and y not, x > y (since x has to overlap y) 00913 if (x->isTransparent() && !y->isTransparent()) 00914 { 00915 return false; 00916 } 00917 // If y is transparent and x not, x < y 00918 else if (!x->isTransparent() && y->isTransparent()) 00919 { 00920 return true; 00921 } 00922 else 00923 { 00924 // Otherwise don't care (both transparent or both solid) 00925 // Just arbitrarily use pointer 00926 return x < y; 00927 } 00928 00929 } 00930 00931 //----------------------------------------------------------------------- 00932 void SceneManager::setSkyPlane( 00933 bool enable, 00934 const Plane& plane, 00935 const String& materialName, 00936 Real gscale, 00937 Real tiling, 00938 bool drawFirst, 00939 Real bow, 00940 int xsegments, int ysegments) 00941 { 00942 mSkyPlaneEnabled = enable; 00943 if (enable) 00944 { 00945 String meshName = "SkyPlane"; 00946 mSkyPlane = plane; 00947 00948 Material* m = getMaterial(materialName); 00949 if (!m) 00950 { 00951 Except(Exception::ERR_INVALIDPARAMS, 00952 "Sky plane material '" + materialName + "' not found.", 00953 "SceneManager::setSkyPlane"); 00954 } 00955 // Make sure the material doesn't update the depth buffer 00956 m->setDepthWriteEnabled(false); 00957 // Ensure loaded 00958 m->load(); 00959 00960 mSkyPlaneDrawFirst = drawFirst; 00961 00962 // Set up the plane 00963 Mesh* planeMesh = (Mesh*)MeshManager::getSingleton().getByName(meshName); 00964 if (planeMesh) 00965 { 00966 // Destroy the old one 00967 MeshManager::getSingleton().unload(planeMesh); 00968 delete planeMesh; 00969 } 00970 00971 // Create up vector 00972 Vector3 up = plane.normal.crossProduct(Vector3::UNIT_X); 00973 if (up == Vector3::ZERO) 00974 up = plane.normal.crossProduct(-Vector3::UNIT_Z); 00975 00976 // Create skyplane 00977 if( bow > 0 ) 00978 { 00979 // Build a curved skyplane 00980 planeMesh = MeshManager::getSingleton().createCurvedPlane( 00981 meshName, plane, gscale * 100, gscale * 100, gscale * bow * 100, 00982 xsegments, ysegments, false, 1, tiling, tiling, up); 00983 } 00984 else 00985 { 00986 planeMesh = MeshManager::getSingleton().createPlane( 00987 meshName, plane, gscale * 100, gscale * 100, xsegments, ysegments, false, 00988 1, tiling, tiling, up); 00989 } 00990 00991 // Create entity 00992 if (mSkyPlaneEntity) 00993 { 00994 // destroy old one, do it by name for speed 00995 removeEntity(meshName); 00996 } 00997 // Create, use the same name for mesh and entity 00998 mSkyPlaneEntity = createEntity(meshName, meshName); 00999 mSkyPlaneEntity->setMaterialName(materialName); 01000 mSkyPlaneEntity->setCastShadows(false); 01001 01002 // Create node and attach 01003 if (!mSkyPlaneNode) 01004 { 01005 mSkyPlaneNode = createSceneNode(meshName + "Node"); 01006 } 01007 else 01008 { 01009 mSkyPlaneNode->detachAllObjects(); 01010 } 01011 mSkyPlaneNode->attachObject(mSkyPlaneEntity); 01012 01013 } 01014 } 01015 //----------------------------------------------------------------------- 01016 void SceneManager::setSkyBox( 01017 bool enable, 01018 const String& materialName, 01019 Real distance, 01020 bool drawFirst, 01021 const Quaternion& orientation ) 01022 { 01023 mSkyBoxEnabled = enable; 01024 if (enable) 01025 { 01026 Material* m = getMaterial(materialName); 01027 if (!m) 01028 { 01029 Except(Exception::ERR_INVALIDPARAMS, 01030 "Sky box material '" + materialName + " not found.", 01031 "SceneManager::setSkyBox"); 01032 } 01033 // Make sure the material doesn't update the depth buffer 01034 m->setDepthWriteEnabled(false); 01035 // Ensure loaded 01036 m->load(); 01037 // Also clamp texture, don't wrap (otherwise edges can get filtered) 01038 m->getBestTechnique()->getPass(0)->getTextureUnitState(0)->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); 01039 01040 01041 mSkyBoxDrawFirst = drawFirst; 01042 01043 // Create node 01044 if (!mSkyBoxNode) 01045 { 01046 mSkyBoxNode = createSceneNode("SkyBoxNode"); 01047 } 01048 else 01049 { 01050 mSkyBoxNode->detachAllObjects(); 01051 } 01052 01053 MaterialManager& matMgr = MaterialManager::getSingleton(); 01054 // Set up the box (6 planes) 01055 for (int i = 0; i < 6; ++i) 01056 { 01057 Mesh* planeMesh = createSkyboxPlane((BoxPlane)i, distance, orientation); 01058 String entName = "SkyBoxPlane" + StringConverter::toString(i); 01059 01060 // Create entity 01061 if (mSkyBoxEntity[i]) 01062 { 01063 // destroy old one, do it by name for speed 01064 removeEntity(entName); 01065 } 01066 mSkyBoxEntity[i] = createEntity(entName, planeMesh->getName()); 01067 mSkyBoxEntity[i]->setCastShadows(false); 01068 // Have to create 6 materials, one for each frame 01069 // Used to use combined material but now we're using queue we can't split to change frame 01070 // This doesn't use much memory because textures aren't duplicated 01071 Material* boxMat = (Material*)matMgr.getByName(entName); 01072 if (!boxMat) 01073 { 01074 // Create new by clone 01075 boxMat = m->clone(entName); 01076 boxMat->load(); 01077 } 01078 else 01079 { 01080 // Copy over existing 01081 m->copyDetailsTo(boxMat); 01082 boxMat->load(); 01083 } 01084 // Set active frame 01085 boxMat->getBestTechnique()->getPass(0)->getTextureUnitState(0) 01086 ->setCurrentFrame(i); 01087 01088 mSkyBoxEntity[i]->setMaterialName(boxMat->getName()); 01089 01090 // Attach to node 01091 mSkyBoxNode->attachObject(mSkyBoxEntity[i]); 01092 } // for each plane 01093 01094 } 01095 01096 } 01097 //----------------------------------------------------------------------- 01098 void SceneManager::setSkyDome( 01099 bool enable, 01100 const String& materialName, 01101 Real curvature, 01102 Real tiling, 01103 Real distance, 01104 bool drawFirst, 01105 const Quaternion& orientation, 01106 int xsegments, int ysegments, int ySegmentsToKeep) 01107 { 01108 mSkyDomeEnabled = enable; 01109 if (enable) 01110 { 01111 Material* m = getMaterial(materialName); 01112 if (!m) 01113 { 01114 Except(Exception::ERR_INVALIDPARAMS, 01115 "Sky dome material '" + materialName + " not found.", 01116 "SceneManager::setSkyDome"); 01117 } 01118 // Make sure the material doesn't update the depth buffer 01119 m->setDepthWriteEnabled(false); 01120 // Ensure loaded 01121 m->load(); 01122 01123 mSkyDomeDrawFirst = drawFirst; 01124 01125 // Create node 01126 if (!mSkyDomeNode) 01127 { 01128 mSkyDomeNode = createSceneNode("SkyDomeNode"); 01129 } 01130 else 01131 { 01132 mSkyDomeNode->detachAllObjects(); 01133 } 01134 01135 // Set up the dome (5 planes) 01136 for (int i = 0; i < 5; ++i) 01137 { 01138 Mesh* planeMesh = createSkydomePlane((BoxPlane)i, curvature, 01139 tiling, distance, orientation, xsegments, ysegments, 01140 i!=BP_UP ? ySegmentsToKeep : -1); 01141 01142 String entName = "SkyDomePlane" + StringConverter::toString(i); 01143 01144 // Create entity 01145 if (mSkyDomeEntity[i]) 01146 { 01147 // destroy old one, do it by name for speed 01148 removeEntity(entName); 01149 } 01150 mSkyDomeEntity[i] = createEntity(entName, planeMesh->getName()); 01151 mSkyDomeEntity[i]->setMaterialName(m->getName()); 01152 mSkyDomeEntity[i]->setCastShadows(false); 01153 01154 // Attach to node 01155 mSkyDomeNode->attachObject(mSkyDomeEntity[i]); 01156 } // for each plane 01157 01158 } 01159 } 01160 //----------------------------------------------------------------------- 01161 Mesh* SceneManager::createSkyboxPlane( 01162 BoxPlane bp, 01163 Real distance, 01164 const Quaternion& orientation ) 01165 { 01166 Plane plane; 01167 String meshName; 01168 Vector3 up; 01169 01170 meshName = "SkyBoxPlane_"; 01171 // Set up plane equation 01172 plane.d = distance; 01173 switch(bp) 01174 { 01175 case BP_FRONT: 01176 plane.normal = Vector3::UNIT_Z; 01177 up = Vector3::UNIT_Y; 01178 meshName += "Front"; 01179 break; 01180 case BP_BACK: 01181 plane.normal = -Vector3::UNIT_Z; 01182 up = Vector3::UNIT_Y; 01183 meshName += "Back"; 01184 break; 01185 case BP_LEFT: 01186 plane.normal = Vector3::UNIT_X; 01187 up = Vector3::UNIT_Y; 01188 meshName += "Left"; 01189 break; 01190 case BP_RIGHT: 01191 plane.normal = -Vector3::UNIT_X; 01192 up = Vector3::UNIT_Y; 01193 meshName += "Right"; 01194 break; 01195 case BP_UP: 01196 plane.normal = -Vector3::UNIT_Y; 01197 up = Vector3::UNIT_Z; 01198 meshName += "Up"; 01199 break; 01200 case BP_DOWN: 01201 plane.normal = Vector3::UNIT_Y; 01202 up = -Vector3::UNIT_Z; 01203 meshName += "Down"; 01204 break; 01205 } 01206 // Modify by orientation 01207 plane.normal = orientation * plane.normal; 01208 up = orientation * up; 01209 01210 01211 // Check to see if existing plane 01212 MeshManager& mm = MeshManager::getSingleton(); 01213 Mesh* planeMesh = (Mesh*)mm.getByName(meshName); 01214 if(planeMesh) 01215 { 01216 // destroy existing 01217 mm.unload(planeMesh); 01218 delete planeMesh; 01219 } 01220 // Create new 01221 Real planeSize = distance * 2; 01222 const int BOX_SEGMENTS = 1; 01223 planeMesh = mm.createPlane(meshName, plane, planeSize, planeSize, BOX_SEGMENTS, BOX_SEGMENTS, false, 1, 1, 1, up); 01224 01225 //planeMesh->_dumpContents(meshName); 01226 01227 return planeMesh; 01228 01229 } 01230 //----------------------------------------------------------------------- 01231 Mesh* SceneManager::createSkydomePlane( 01232 BoxPlane bp, 01233 Real curvature, 01234 Real tiling, 01235 Real distance, 01236 const Quaternion& orientation, 01237 int xsegments, int ysegments, int ysegments_keep) 01238 { 01239 01240 Plane plane; 01241 String meshName; 01242 Vector3 up; 01243 01244 meshName = "SkyDomePlane_"; 01245 // Set up plane equation 01246 plane.d = distance; 01247 switch(bp) 01248 { 01249 case BP_FRONT: 01250 plane.normal = Vector3::UNIT_Z; 01251 up = Vector3::UNIT_Y; 01252 meshName += "Front"; 01253 break; 01254 case BP_BACK: 01255 plane.normal = -Vector3::UNIT_Z; 01256 up = Vector3::UNIT_Y; 01257 meshName += "Back"; 01258 break; 01259 case BP_LEFT: 01260 plane.normal = Vector3::UNIT_X; 01261 up = Vector3::UNIT_Y; 01262 meshName += "Left"; 01263 break; 01264 case BP_RIGHT: 01265 plane.normal = -Vector3::UNIT_X; 01266 up = Vector3::UNIT_Y; 01267 meshName += "Right"; 01268 break; 01269 case BP_UP: 01270 plane.normal = -Vector3::UNIT_Y; 01271 up = Vector3::UNIT_Z; 01272 meshName += "Up"; 01273 break; 01274 case BP_DOWN: 01275 // no down 01276 return 0; 01277 } 01278 // Modify by orientation 01279 plane.normal = orientation * plane.normal; 01280 up = orientation * up; 01281 01282 // Check to see if existing plane 01283 MeshManager& mm = MeshManager::getSingleton(); 01284 Mesh* planeMesh = (Mesh*)mm.getByName(meshName); 01285 if(planeMesh) 01286 { 01287 // destroy existing 01288 mm.unload(planeMesh); 01289 delete planeMesh; 01290 } 01291 // Create new 01292 Real planeSize = distance * 2; 01293 planeMesh = mm.createCurvedIllusionPlane(meshName, plane, planeSize, planeSize, curvature, 01294 xsegments, ysegments, false, 1, tiling, tiling, up, orientation, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, HardwareBuffer::HBU_STATIC_WRITE_ONLY, 01295 false, false, ysegments_keep); 01296 01297 //planeMesh->_dumpContents(meshName); 01298 01299 return planeMesh; 01300 01301 } 01302 01303 01304 //----------------------------------------------------------------------- 01305 void SceneManager::_updateSceneGraph(Camera* cam) 01306 { 01307 // Cascade down the graph updating transforms & world bounds 01308 // In this implementation, just update from the root 01309 // Smarter SceneManager subclasses may choose to update only 01310 // certain scene graph branches 01311 mSceneRoot->_update(true, false); 01312 01313 01314 } 01315 //----------------------------------------------------------------------- 01316 void SceneManager::_findVisibleObjects(Camera* cam, bool onlyShadowCasters) 01317 { 01318 // Tell nodes to find, cascade down all nodes 01319 mSceneRoot->_findVisibleObjects(cam, getRenderQueue(), true, 01320 mDisplayNodes, onlyShadowCasters); 01321 01322 } 01323 //----------------------------------------------------------------------- 01324 void SceneManager::_renderVisibleObjects(void) 01325 { 01326 // Render each separate queue 01327 RenderQueue::QueueGroupIterator queueIt = getRenderQueue()->_getQueueGroupIterator(); 01328 01329 // NB only queues which have been created are rendered, no time is wasted 01330 // parsing through non-existent queues (even though there are 10 available) 01331 01332 while (queueIt.hasMoreElements()) 01333 { 01334 // Get queue group id 01335 RenderQueueGroupID qId = queueIt.peekNextKey(); 01336 RenderQueueGroup* pGroup = queueIt.getNext(); 01337 01338 01339 bool repeatQueue = false; 01340 do // for repeating queues 01341 { 01342 // Fire queue started event 01343 if (fireRenderQueueStarted(qId)) 01344 { 01345 // Someone requested we skip this queue 01346 continue; 01347 } 01348 01349 renderQueueGroupObjects(pGroup); 01350 01351 // Fire queue ended event 01352 if (fireRenderQueueEnded(qId)) 01353 { 01354 // Someone requested we repeat this queue 01355 repeatQueue = true; 01356 } 01357 else 01358 { 01359 repeatQueue = false; 01360 } 01361 } while (repeatQueue); 01362 01363 } // for each queue group 01364 01365 } 01366 //----------------------------------------------------------------------- 01367 void SceneManager::renderAdditiveStencilShadowedQueueGroupObjects(RenderQueueGroup* pGroup) 01368 { 01369 RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); 01370 LightList lightList; 01371 01372 while (groupIt.hasMoreElements()) 01373 { 01374 RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); 01375 01376 // Sort the queue first 01377 pPriorityGrp->sort(mCameraInProgress); 01378 01379 // Clear light list 01380 lightList.clear(); 01381 01382 // Render all the ambient passes first, no light iteration, no lights 01383 mIlluminationStage = IRS_AMBIENT; 01384 renderObjects(pPriorityGrp->_getSolidPasses(), false, &lightList); 01385 // Also render any objects which have receive shadows disabled 01386 renderObjects(pPriorityGrp->_getSolidPassesNoShadow(), true); 01387 01388 01389 // Now iterate per light 01390 mIlluminationStage = IRS_PER_LIGHT; 01391 01392 // Iterate over lights, render all volumes to stencil 01393 LightList::const_iterator li, liend; 01394 liend = mLightsAffectingFrustum.end(); 01395 01396 for (li = mLightsAffectingFrustum.begin(); li != liend; ++li) 01397 { 01398 Light* l = *li; 01399 // Set light state 01400 01401 if (l->getCastShadows()) 01402 { 01403 // Clear stencil 01404 mDestRenderSystem->clearFrameBuffer(FBT_STENCIL); 01405 renderShadowVolumesToStencil(l, mCameraInProgress); 01406 // turn stencil check on 01407 mDestRenderSystem->setStencilCheckEnabled(true); 01408 // NB we render where the stencil is equal to zero to render lit areas 01409 mDestRenderSystem->setStencilBufferParams(CMPF_EQUAL, 0); 01410 } 01411 01412 // render lighting passes for this light 01413 if (lightList.empty()) 01414 lightList.push_back(l); 01415 else 01416 lightList[0] = l; 01417 renderObjects(pPriorityGrp->_getSolidPassesDiffuseSpecular(), false, &lightList); 01418 01419 // Reset stencil params 01420 mDestRenderSystem->setStencilBufferParams(); 01421 mDestRenderSystem->setStencilCheckEnabled(false); 01422 mDestRenderSystem->_setDepthBufferParams(); 01423 01424 }// for each light 01425 01426 01427 // Now render decal passes, no need to set lights as lighting will be disabled 01428 mIlluminationStage = IRS_DECAL; 01429 renderObjects(pPriorityGrp->_getSolidPassesDecal(), false); 01430 01431 01432 }// for each priority 01433 01434 // reset lighting stage 01435 mIlluminationStage = IRS_NONE; 01436 01437 // Iterate again - variable name changed to appease gcc. 01438 RenderQueueGroup::PriorityMapIterator groupIt2 = pGroup->getIterator(); 01439 while (groupIt2.hasMoreElements()) 01440 { 01441 RenderPriorityGroup* pPriorityGrp = groupIt2.getNext(); 01442 01443 // Do transparents 01444 renderObjects(pPriorityGrp->_getTransparentPasses(), true); 01445 01446 }// for each priority 01447 01448 01449 } 01450 //----------------------------------------------------------------------- 01451 void SceneManager::renderModulativeStencilShadowedQueueGroupObjects(RenderQueueGroup* pGroup) 01452 { 01453 /* For each light, we need to render all the solids from each group, 01454 then do the modulative shadows, then render the transparents from 01455 each group. 01456 Now, this means we are going to reorder things more, but that it required 01457 if the shadows are to look correct. The overall order is preserved anyway, 01458 it's just that all the transparents are at the end instead of them being 01459 interleaved as in the normal rendering loop. 01460 */ 01461 // Iterate through priorities 01462 RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); 01463 01464 while (groupIt.hasMoreElements()) 01465 { 01466 RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); 01467 01468 // Sort the queue first 01469 pPriorityGrp->sort(mCameraInProgress); 01470 01471 // Do (shadowable) solids 01472 renderObjects(pPriorityGrp->_getSolidPasses(), true); 01473 } 01474 01475 01476 // Iterate over lights, render all volumes to stencil 01477 LightList::const_iterator li, liend; 01478 liend = mLightsAffectingFrustum.end(); 01479 01480 for (li = mLightsAffectingFrustum.begin(); li != liend; ++li) 01481 { 01482 Light* l = *li; 01483 if (l->getCastShadows()) 01484 { 01485 // Clear stencil 01486 mDestRenderSystem->clearFrameBuffer(FBT_STENCIL); 01487 renderShadowVolumesToStencil(l, mCameraInProgress); 01488 // render full-screen shadow modulator for all lights 01489 setPass(mShadowModulativePass); 01490 // turn stencil check on 01491 mDestRenderSystem->setStencilCheckEnabled(true); 01492 // NB we render where the stencil is not equal to zero to render shadows, not lit areas 01493 mDestRenderSystem->setStencilBufferParams(CMPF_NOT_EQUAL, 0); 01494 renderSingleObject(mFullScreenQuad, mShadowModulativePass, false); 01495 // Reset stencil params 01496 mDestRenderSystem->setStencilBufferParams(); 01497 mDestRenderSystem->setStencilCheckEnabled(false); 01498 mDestRenderSystem->_setDepthBufferParams(); 01499 } 01500 01501 }// for each light 01502 01503 // Iterate again - variable name changed to appease gcc. 01504 RenderQueueGroup::PriorityMapIterator groupIt2 = pGroup->getIterator(); 01505 while (groupIt2.hasMoreElements()) 01506 { 01507 RenderPriorityGroup* pPriorityGrp = groupIt2.getNext(); 01508 01509 // Do non-shadowable solids 01510 renderObjects(pPriorityGrp->_getSolidPassesNoShadow(), true); 01511 01512 }// for each priority 01513 01514 01515 // Iterate again - variable name changed to appease gcc. 01516 RenderQueueGroup::PriorityMapIterator groupIt3 = pGroup->getIterator(); 01517 while (groupIt3.hasMoreElements()) 01518 { 01519 RenderPriorityGroup* pPriorityGrp = groupIt3.getNext(); 01520 01521 // Do transparents 01522 renderObjects(pPriorityGrp->_getTransparentPasses(), true); 01523 01524 }// for each priority 01525 01526 } 01527 //----------------------------------------------------------------------- 01528 void SceneManager::renderTextureShadowCasterQueueGroupObjects(RenderQueueGroup* pGroup) 01529 { 01530 static LightList nullLightList; 01531 // This is like the basic group render, except we skip all transparents 01532 // and we also render any non-shadowed objects 01533 // Note that non-shadow casters will have already been eliminated during 01534 // _findVisibleObjects 01535 01536 // Iterate through priorities 01537 RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); 01538 01539 // Override auto param ambient to force vertex programs and fixed function to 01540 // use shadow colour 01541 mAutoParamDataSource.setAmbientLightColour(mShadowColour); 01542 mDestRenderSystem->setAmbientLight(mShadowColour.r, mShadowColour.g, mShadowColour.b); 01543 01544 while (groupIt.hasMoreElements()) 01545 { 01546 RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); 01547 01548 // Sort the queue first 01549 pPriorityGrp->sort(mCameraInProgress); 01550 01551 // Do solids, override light list incase any vertex programs use them 01552 renderObjects(pPriorityGrp->_getSolidPasses(), false, &nullLightList); 01553 renderObjects(pPriorityGrp->_getSolidPassesNoShadow(), false, &nullLightList); 01554 // Do transparents that cast shadows 01555 renderTransparentShadowCasterObjects( 01556 pPriorityGrp->_getTransparentPasses(), false, &nullLightList); 01557 01558 01559 }// for each priority 01560 01561 // reset ambient light 01562 mAutoParamDataSource.setAmbientLightColour(mAmbientLight); 01563 mDestRenderSystem->setAmbientLight(mAmbientLight.r, mAmbientLight.g, mAmbientLight.b); 01564 } 01565 //----------------------------------------------------------------------- 01566 void SceneManager::renderModulativeTextureShadowedQueueGroupObjects(RenderQueueGroup* pGroup) 01567 { 01568 /* For each light, we need to render all the solids from each group, 01569 then do the modulative shadows, then render the transparents from 01570 each group. 01571 Now, this means we are going to reorder things more, but that it required 01572 if the shadows are to look correct. The overall order is preserved anyway, 01573 it's just that all the transparents are at the end instead of them being 01574 interleaved as in the normal rendering loop. 01575 */ 01576 // Iterate through priorities 01577 RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); 01578 01579 while (groupIt.hasMoreElements()) 01580 { 01581 RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); 01582 01583 // Sort the queue first 01584 pPriorityGrp->sort(mCameraInProgress); 01585 01586 // Do solids 01587 renderObjects(pPriorityGrp->_getSolidPasses(), true); 01588 renderObjects(pPriorityGrp->_getSolidPassesNoShadow(), true); 01589 } 01590 01591 01592 // Iterate over lights, render received shadows 01593 // only perform this if we're in the 'normal' render stage, to avoid 01594 // doing it during the render to texture 01595 if (mIlluminationStage == IRS_NONE) 01596 { 01597 mIlluminationStage = IRS_RENDER_MODULATIVE_PASS; 01598 01599 LightList::iterator i, iend; 01600 ShadowTextureList::iterator si, siend; 01601 iend = mLightsAffectingFrustum.end(); 01602 siend = mShadowTextures.end(); 01603 for (i = mLightsAffectingFrustum.begin(), si = mShadowTextures.begin(); 01604 i != iend && si != siend; ++i) 01605 { 01606 Light* l = *i; 01607 01608 if (!l->getCastShadows()) 01609 continue; 01610 01611 mCurrentShadowTexture = *si; 01612 // Hook up receiver texture 01613 mShadowReceiverPass->getTextureUnitState(0)->setTextureName( 01614 mCurrentShadowTexture->getName()); 01615 // Hook up projection frustum 01616 mShadowReceiverPass->getTextureUnitState(0)->setProjectiveTexturing( 01617 true, mCurrentShadowTexture->getViewport(0)->getCamera()); 01618 mAutoParamDataSource.setTextureProjector( 01619 mCurrentShadowTexture->getViewport(0)->getCamera()); 01620 // if this light is a spotlight, we need to add the spot fader layer 01621 if (l->getType() == Light::LT_SPOTLIGHT) 01622 { 01623 // Add spot fader if not present already 01624 if (mShadowReceiverPass->getNumTextureUnitStates() == 1) 01625 { 01626 TextureUnitState* t = 01627 mShadowReceiverPass->createTextureUnitState("spot_shadow_fade.png"); 01628 t->setProjectiveTexturing( 01629 true, mCurrentShadowTexture->getViewport(0)->getCamera()); 01630 t->setColourOperation(LBO_ADD); 01631 t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); 01632 } 01633 else 01634 { 01635 // Just set projector 01636 TextureUnitState* t = 01637 mShadowReceiverPass->getTextureUnitState(1); 01638 t->setProjectiveTexturing( 01639 true, mCurrentShadowTexture->getViewport(0)->getCamera()); 01640 } 01641 } 01642 else if (mShadowReceiverPass->getNumTextureUnitStates() > 1) 01643 { 01644 // remove spot fader layer 01645 mShadowReceiverPass->removeTextureUnitState(1); 01646 01647 } 01648 mShadowReceiverPass->_load(); 01649 01650 if (l->getCastShadows() && pGroup->getShadowsEnabled()) 01651 { 01652 renderTextureShadowReceiverQueueGroupObjects(pGroup); 01653 } 01654 01655 ++si; 01656 01657 }// for each light 01658 01659 mIlluminationStage = IRS_NONE; 01660 01661 } 01662 01663 // Iterate again - variable name changed to appease gcc. 01664 RenderQueueGroup::PriorityMapIterator groupIt3 = pGroup->getIterator(); 01665 while (groupIt3.hasMoreElements()) 01666 { 01667 RenderPriorityGroup* pPriorityGrp = groupIt3.getNext(); 01668 01669 // Do transparents 01670 renderObjects(pPriorityGrp->_getTransparentPasses(), true); 01671 01672 }// for each priority 01673 01674 } 01675 //----------------------------------------------------------------------- 01676 void SceneManager::renderTextureShadowReceiverQueueGroupObjects(RenderQueueGroup* pGroup) 01677 { 01678 static LightList nullLightList; 01679 01680 // Iterate through priorities 01681 RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); 01682 01683 // Override auto param ambient to force vertex programs to go full-bright 01684 mAutoParamDataSource.setAmbientLightColour(ColourValue::White); 01685 mDestRenderSystem->setAmbientLight(1, 1, 1); 01686 01687 while (groupIt.hasMoreElements()) 01688 { 01689 RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); 01690 01691 // Do solids, override light list incase any vertex programs use them 01692 renderObjects(pPriorityGrp->_getSolidPasses(), false, &nullLightList); 01693 01694 // Don't render transparents or passes which have shadow receipt disabled 01695 01696 }// for each priority 01697 01698 // reset ambient 01699 mAutoParamDataSource.setAmbientLightColour(mAmbientLight); 01700 mDestRenderSystem->setAmbientLight(mAmbientLight.r, mAmbientLight.g, mAmbientLight.b); 01701 01702 } 01703 //----------------------------------------------------------------------- 01704 bool SceneManager::validatePassForRendering(Pass* pass) 01705 { 01706 // Bypass if we're doing a texture shadow render and 01707 // this pass is after the first (only 1 pass needed for shadow texture) 01708 if ((mIlluminationStage == IRS_RENDER_TO_TEXTURE || 01709 mIlluminationStage == IRS_RENDER_MODULATIVE_PASS) && 01710 pass->getIndex() > 0) 01711 { 01712 return false; 01713 } 01714 01715 return true; 01716 } 01717 //----------------------------------------------------------------------- 01718 bool SceneManager::validateRenderableForRendering(Pass* pass, Renderable* rend) 01719 { 01720 // Skip this renderable if we're doing texture shadows, it casts shadows 01721 // and we're doing the render receivers pass 01722 if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE && 01723 mIlluminationStage == IRS_RENDER_MODULATIVE_PASS && 01724 rend->getCastsShadows()) 01725 { 01726 return false; 01727 } 01728 01729 return true; 01730 01731 } 01732 //----------------------------------------------------------------------- 01733 void SceneManager::renderObjects( 01734 const RenderPriorityGroup::SolidRenderablePassMap& objs, bool doLightIteration, 01735 const LightList* manualLightList) 01736 { 01737 // ----- SOLIDS LOOP ----- 01738 RenderPriorityGroup::SolidRenderablePassMap::const_iterator ipass, ipassend; 01739 ipassend = objs.end(); 01740 for (ipass = objs.begin(); ipass != ipassend; ++ipass) 01741 { 01742 // Fast bypass if this group is now empty 01743 if (ipass->second->empty()) continue; 01744 01745 // Give SM a chance to eliminate this pass 01746 if (!validatePassForRendering(ipass->first)) 01747 continue; 01748 01749 // For solids, we try to do each pass in turn 01750 Pass* usedPass = setPass(ipass->first); 01751 RenderPriorityGroup::RenderableList* rendList = ipass->second; 01752 RenderPriorityGroup::RenderableList::const_iterator irend, irendend; 01753 irendend = rendList->end(); 01754 for (irend = rendList->begin(); irend != irendend; ++irend) 01755 { 01756 // Give SM a chance to eliminate 01757 if (!validateRenderableForRendering(ipass->first, *irend)) 01758 continue; 01759 // Render a single object, this will set up auto params if required 01760 renderSingleObject(*irend, usedPass, doLightIteration, manualLightList); 01761 } 01762 } 01763 } 01764 //----------------------------------------------------------------------- 01765 void SceneManager::renderObjects( 01766 const RenderPriorityGroup::TransparentRenderablePassList& objs, bool doLightIteration, 01767 const LightList* manualLightList) 01768 { 01769 // ----- TRANSPARENT LOOP ----- 01770 // This time we render by Z, not by pass 01771 // The mTransparentObjects set needs to be ordered first 01772 // Render each non-transparent entity in turn, grouped by material 01773 RenderPriorityGroup::TransparentRenderablePassList::const_iterator itrans, itransend; 01774 01775 itransend = objs.end(); 01776 for (itrans = objs.begin(); 01777 itrans != itransend; ++itrans) 01778 { 01779 // For transparents, we have to accept that we can't sort entirely by pass 01780 setPass(itrans->pass); 01781 renderSingleObject(itrans->renderable, itrans->pass, doLightIteration, 01782 manualLightList); 01783 } 01784 01785 } 01786 //----------------------------------------------------------------------- 01787 void SceneManager::renderQueueGroupObjects(RenderQueueGroup* pGroup) 01788 { 01789 if (pGroup->getShadowsEnabled() && 01790 mShadowTechnique == SHADOWTYPE_STENCIL_ADDITIVE) 01791 { 01792 // Additive stencil shadows in use 01793 renderAdditiveStencilShadowedQueueGroupObjects(pGroup); 01794 } 01795 else if (pGroup->getShadowsEnabled() && 01796 mShadowTechnique == SHADOWTYPE_STENCIL_MODULATIVE) 01797 { 01798 // Modulative stencil shadows in use 01799 renderModulativeStencilShadowedQueueGroupObjects(pGroup); 01800 } 01801 else if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE) 01802 { 01803 // Modulative texture shadows in use 01804 if (mIlluminationStage == IRS_RENDER_TO_TEXTURE) 01805 { 01806 // Shadow caster pass 01807 if (pGroup->getShadowsEnabled()) 01808 renderTextureShadowCasterQueueGroupObjects(pGroup); 01809 } 01810 else 01811 { 01812 // Ordinary pass 01813 renderModulativeTextureShadowedQueueGroupObjects(pGroup); 01814 } 01815 } 01816 else 01817 { 01818 // No shadows, ordinary pass 01819 renderBasicQueueGroupObjects(pGroup); 01820 } 01821 01822 01823 } 01824 //----------------------------------------------------------------------- 01825 void SceneManager::renderBasicQueueGroupObjects(RenderQueueGroup* pGroup) 01826 { 01827 // Basic render loop 01828 // Iterate through priorities 01829 RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); 01830 01831 while (groupIt.hasMoreElements()) 01832 { 01833 RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); 01834 01835 // Sort the queue first 01836 pPriorityGrp->sort(mCameraInProgress); 01837 01838 // Do solids 01839 renderObjects(pPriorityGrp->_getSolidPasses(), true); 01840 // Do transparents 01841 renderObjects(pPriorityGrp->_getTransparentPasses(), true); 01842 01843 01844 }// for each priority 01845 } 01846 //----------------------------------------------------------------------- 01847 void SceneManager::renderTransparentShadowCasterObjects( 01848 const RenderPriorityGroup::TransparentRenderablePassList& objs, bool doLightIteration, 01849 const LightList* manualLightList) 01850 { 01851 // ----- TRANSPARENT LOOP as in renderObjects above, but changed a bit ----- 01852 RenderPriorityGroup::TransparentRenderablePassList::const_iterator itrans, itransend; 01853 01854 itransend = objs.end(); 01855 for (itrans = objs.begin(); 01856 itrans != itransend; ++itrans) 01857 { 01858 Renderable *r = itrans->renderable; 01859 Pass *p = itrans->pass; 01860 01861 // only render this pass if it's being forced to cast shadows 01862 if (p->getParent()->getParent()->getTransparencyCastsShadows()) 01863 { 01864 setPass(p); 01865 renderSingleObject(itrans->renderable, p, doLightIteration, manualLightList); 01866 } 01867 } 01868 } 01869 //----------------------------------------------------------------------- 01870 void SceneManager::renderSingleObject(Renderable* rend, Pass* pass, 01871 bool doLightIteration, const LightList* manualLightList) 01872 { 01873 static Matrix4 xform[256]; 01874 unsigned short numMatrices; 01875 static bool normalisedNormals = false; 01876 static SceneDetailLevel camDetailLevel = mCameraInProgress->getDetailLevel(); 01877 static SceneDetailLevel lastDetailLevel = camDetailLevel; 01878 static RenderOperation ro; 01879 static LightList localLightList; 01880 01881 if (pass->isProgrammable()) 01882 { 01883 // Tell auto params object about the renderable change 01884 mAutoParamDataSource.setCurrentRenderable(rend); 01885 pass->_updateAutoParamsNoLights(mAutoParamDataSource); 01886 } 01887 01888 // Set world transformation 01889 rend->getWorldTransforms(xform); 01890 numMatrices = rend->getNumWorldTransforms(); 01891 if (numMatrices > 1) 01892 { 01893 mDestRenderSystem->_setWorldMatrices(xform, numMatrices); 01894 } 01895 else 01896 { 01897 mDestRenderSystem->_setWorldMatrix(*xform); 01898 } 01899 01900 // Issue view / projection changes if any 01901 useRenderableViewProjMode(rend); 01902 01903 // Reissue any texture gen settings which are dependent on view matrix 01904 Pass::TextureUnitStateIterator texIter = pass->getTextureUnitStateIterator(); 01905 size_t unit = 0; 01906 while(texIter.hasMoreElements()) 01907 { 01908 TextureUnitState* pTex = texIter.getNext(); 01909 if (pTex->hasViewRelativeTextureCoordinateGeneration()) 01910 { 01911 mDestRenderSystem->_setTextureUnitSettings(unit, *pTex); 01912 } 01913 ++unit; 01914 } 01915 01916 01917 // Sort out normalisation 01918 bool thisNormalise = rend->getNormaliseNormals(); 01919 if (thisNormalise != normalisedNormals) 01920 { 01921 mDestRenderSystem->setNormaliseNormals(thisNormalise); 01922 normalisedNormals = thisNormalise; 01923 } 01924 01925 // Set up the solid / wireframe override 01926 SceneDetailLevel reqDetail = rend->getRenderDetail(); 01927 if (reqDetail != lastDetailLevel || reqDetail != camDetailLevel) 01928 { 01929 if (reqDetail > camDetailLevel) 01930 { 01931 // only downgrade detail; if cam says wireframe we don't go up to solid 01932 reqDetail = camDetailLevel; 01933 } 01934 mDestRenderSystem->_setRasterisationMode(reqDetail); 01935 lastDetailLevel = reqDetail; 01936 01937 } 01938 01939 mDestRenderSystem->setClipPlanes(rend->getClipPlanes()); 01940 01941 // Set up rendering operation 01942 rend->getRenderOperation(ro); 01943 ro.srcRenderable = rend; 01944 01945 if (doLightIteration) 01946 { 01947 // Here's where we issue the rendering operation to the render system 01948 // Note that we may do this once per light, therefore it's in a loop 01949 // and the light parameters are updated once per traversal through the 01950 // loop 01951 const LightList& rendLightList = rend->getLights(); 01952 bool iteratePerLight = pass->getRunOncePerLight(); 01953 size_t numIterations = iteratePerLight ? rendLightList.size() : 1; 01954 const LightList* pLightListToUse; 01955 for (size_t i = 0; i < numIterations; ++i) 01956 { 01957 // Determine light list to use 01958 if (iteratePerLight) 01959 { 01960 // Change the only element of local light list to be 01961 // the light at index i 01962 localLightList.clear(); 01963 // Check whether we need to filter this one out 01964 if (pass->getRunOnlyForOneLightType() && 01965 pass->getOnlyLightType() != rendLightList[i]->getType()) 01966 { 01967 // Skip 01968 continue; 01969 } 01970 01971 localLightList.push_back(rendLightList[i]); 01972 pLightListToUse = &localLightList; 01973 } 01974 else 01975 { 01976 // Use complete light list 01977 pLightListToUse = &rendLightList; 01978 } 01979 01980 // Do we need to update GPU program parameters? 01981 if (pass->isProgrammable()) 01982 { 01983 // Update any automatic gpu params for lights 01984 // Other bits of information will have to be looked up 01985 mAutoParamDataSource.setCurrentLightList(pLightListToUse); 01986 pass->_updateAutoParamsLightsOnly(mAutoParamDataSource); 01987 // NOTE: We MUST bind parameters AFTER updating the autos 01988 // TEST 01989 if (pass->hasVertexProgram()) 01990 { 01991 mDestRenderSystem->bindGpuProgramParameters(GPT_VERTEX_PROGRAM, 01992 pass->getVertexProgramParameters()); 01993 } 01994 if (pass->hasFragmentProgram()) 01995 { 01996 mDestRenderSystem->bindGpuProgramParameters(GPT_FRAGMENT_PROGRAM, 01997 pass->getFragmentProgramParameters()); 01998 } 01999 } 02000 // Do we need to update light states? 02001 // Only do this if fixed-function vertex lighting applies 02002 if (pass->getLightingEnabled() && !pass->hasVertexProgram()) 02003 { 02004 mDestRenderSystem->_useLights(*pLightListToUse, pass->getMaxSimultaneousLights()); 02005 } 02006 // issue the render op 02007 mDestRenderSystem->_render(ro); 02008 } // possibly iterate per light 02009 } 02010 else // no automatic light processing 02011 { 02012 // Do we need to update GPU program parameters? 02013 if (pass->isProgrammable()) 02014 { 02015 // Do we have a manual light list? 02016 if (manualLightList) 02017 { 02018 // Update any automatic gpu params for lights 02019 mAutoParamDataSource.setCurrentLightList(manualLightList); 02020 pass->_updateAutoParamsLightsOnly(mAutoParamDataSource); 02021 } 02022 02023 if (pass->hasVertexProgram()) 02024 { 02025 mDestRenderSystem->bindGpuProgramParameters(GPT_VERTEX_PROGRAM, 02026 pass->getVertexProgramParameters()); 02027 } 02028 if (pass->hasFragmentProgram()) 02029 { 02030 mDestRenderSystem->bindGpuProgramParameters(GPT_FRAGMENT_PROGRAM, 02031 pass->getFragmentProgramParameters()); 02032 } 02033 } 02034 02035 // Use manual lights if present, and not using vertex programs 02036 if (manualLightList && 02037 pass->getLightingEnabled() && !pass->hasVertexProgram()) 02038 { 02039 mDestRenderSystem->_useLights(*manualLightList, pass->getMaxSimultaneousLights()); 02040 } 02041 // issue the render op 02042 mDestRenderSystem->_render(ro); 02043 } 02044 } 02045 //----------------------------------------------------------------------- 02046 void SceneManager::setAmbientLight(const ColourValue& colour) 02047 { 02048 mAmbientLight = colour; 02049 mDestRenderSystem->setAmbientLight(colour.r, colour.g, colour.b); 02050 } 02051 //----------------------------------------------------------------------- 02052 const ColourValue& SceneManager::getAmbientLight(void) const 02053 { 02054 return mAmbientLight; 02055 } 02056 //----------------------------------------------------------------------- 02057 ViewPoint SceneManager::getSuggestedViewpoint(bool random) 02058 { 02059 // By default return the origin 02060 ViewPoint vp; 02061 vp.position = Vector3::ZERO; 02062 vp.orientation = Quaternion::IDENTITY; 02063 return vp; 02064 } 02065 //----------------------------------------------------------------------- 02066 void SceneManager::setFog(FogMode mode, const ColourValue& colour, Real density, Real start, Real end) 02067 { 02068 mFogMode = mode; 02069 mFogColour = colour; 02070 mFogStart = start; 02071 mFogEnd = end; 02072 mFogDensity = density; 02073 } 02074 //----------------------------------------------------------------------- 02075 FogMode SceneManager::getFogMode(void) const 02076 { 02077 return mFogMode; 02078 } 02079 //----------------------------------------------------------------------- 02080 const ColourValue& SceneManager::getFogColour(void) const 02081 { 02082 return mFogColour; 02083 } 02084 //----------------------------------------------------------------------- 02085 Real SceneManager::getFogStart(void) const 02086 { 02087 return mFogStart; 02088 } 02089 //----------------------------------------------------------------------- 02090 Real SceneManager::getFogEnd(void) const 02091 { 02092 return mFogEnd; 02093 } 02094 //----------------------------------------------------------------------- 02095 Real SceneManager::getFogDensity(void) const 02096 { 02097 return mFogDensity; 02098 } 02099 //----------------------------------------------------------------------- 02100 BillboardSet* SceneManager::createBillboardSet(const String& name, unsigned int poolSize) 02101 { 02102 // Check name not used 02103 if (mBillboardSets.find(name) != mBillboardSets.end()) 02104 { 02105 Except( 02106 Exception::ERR_DUPLICATE_ITEM, 02107 "A billboard set with the name " + name + " already exists", 02108 "SceneManager::createBillboardSet" ); 02109 } 02110 02111 BillboardSet* set = new BillboardSet( name, poolSize ); 02112 mBillboardSets[name] = set;//.insert(BillboardSetList::value_type(name, set)); 02113 02114 return set; 02115 } 02116 //----------------------------------------------------------------------- 02117 BillboardSet* SceneManager::getBillboardSet(const String& name) 02118 { 02119 BillboardSetList::iterator i = mBillboardSets.find(name); 02120 if (i == mBillboardSets.end()) 02121 { 02122 return 0; 02123 } 02124 else 02125 { 02126 return i->second; 02127 } 02128 } 02129 //----------------------------------------------------------------------- 02130 void SceneManager::removeBillboardSet(BillboardSet* set) 02131 { 02132 // Find in list 02133 BillboardSetList::iterator i = mBillboardSets.begin(); 02134 for (; i != mBillboardSets.end(); ++i) 02135 { 02136 if (i->second == set) 02137 { 02138 mBillboardSets.erase(i); 02139 delete set; 02140 break; 02141 } 02142 } 02143 02144 } 02145 //----------------------------------------------------------------------- 02146 void SceneManager::removeBillboardSet(const String& name) 02147 { 02148 // Find in list 02149 BillboardSetList::iterator i = mBillboardSets.find(name); 02150 if (i != mBillboardSets.end()) 02151 { 02152 delete i->second; 02153 mBillboardSets.erase(i); 02154 } 02155 } 02156 //----------------------------------------------------------------------- 02157 void SceneManager::setDisplaySceneNodes(bool display) 02158 { 02159 mDisplayNodes = display; 02160 } 02161 //----------------------------------------------------------------------- 02162 Animation* SceneManager::createAnimation(const String& name, Real length) 02163 { 02164 // Check name not used 02165 if (mAnimationsList.find(name) != mAnimationsList.end()) 02166 { 02167 Except( 02168 Exception::ERR_DUPLICATE_ITEM, 02169 "An animation with the name " + name + " already exists", 02170 "SceneManager::createAnimation" ); 02171 } 02172 02173 Animation* pAnim = new Animation(name, length); 02174 mAnimationsList[name] = pAnim; 02175 return pAnim; 02176 } 02177 //----------------------------------------------------------------------- 02178 Animation* SceneManager::getAnimation(const String& name) const 02179 { 02180 AnimationList::const_iterator i = mAnimationsList.find(name); 02181 if (i == mAnimationsList.end()) 02182 { 02183 Except(Exception::ERR_ITEM_NOT_FOUND, 02184 "Cannot find animation with name " + name, 02185 "SceneManager::getAnimation"); 02186 } 02187 return i->second; 02188 } 02189 //----------------------------------------------------------------------- 02190 void SceneManager::destroyAnimation(const String& name) 02191 { 02192 // Also destroy any animation states referencing this animation 02193 AnimationStateSet::iterator si, siend; 02194 siend = mAnimationStates.end(); 02195 for (si = mAnimationStates.begin(); si != siend; ) 02196 { 02197 if (si->second.getAnimationName() == name) 02198 { 02199 // erase, post increment to avoid the invalidated iterator 02200 mAnimationStates.erase(si++); 02201 } 02202 else 02203 { 02204 ++si; 02205 } 02206 } 02207 02208 AnimationList::iterator i = mAnimationsList.find(name); 02209 if (i == mAnimationsList.end()) 02210 { 02211 Except(Exception::ERR_ITEM_NOT_FOUND, 02212 "Cannot find animation with name " + name, 02213 "SceneManager::getAnimation"); 02214 } 02215 02216 // Free memory 02217 delete i->second; 02218 02219 mAnimationsList.erase(i); 02220 02221 02222 } 02223 //----------------------------------------------------------------------- 02224 void SceneManager::destroyAllAnimations(void) 02225 { 02226 // Destroy all states too, since they cannot reference destroyed animations 02227 destroyAllAnimationStates(); 02228 02229 AnimationList::iterator i; 02230 for (i = mAnimationsList.begin(); i != mAnimationsList.end(); ++i) 02231 { 02232 // destroy 02233 delete i->second; 02234 } 02235 mAnimationsList.clear(); 02236 } 02237 //----------------------------------------------------------------------- 02238 AnimationState* SceneManager::createAnimationState(const String& animName) 02239 { 02240 if (mAnimationStates.find(animName) != mAnimationStates.end()) 02241 { 02242 Except(Exception::ERR_DUPLICATE_ITEM, 02243 "Cannot create, AnimationState already exists: "+animName, 02244 "SceneManager::createAnimationState"); 02245 } 02246 02247 // Get animation, this will throw an exception if not found 02248 Animation* anim = getAnimation(animName); 02249 02250 // Create new state 02251 AnimationState newState(animName, 0, anim->getLength()); 02252 02253 // Record it 02254 std::pair<AnimationStateSet::iterator, bool> retPair = 02255 mAnimationStates.insert(AnimationStateSet::value_type(animName, newState)); 02256 02257 // Check boolean return 02258 if (retPair.second) 02259 { 02260 // insert was OK 02261 // Get pointer from iterator in pair 02262 return &(retPair.first->second); 02263 } 02264 else 02265 { 02266 // Problem 02267 // Not because of duplicate item, that's checked for above 02268 Except(Exception::ERR_INTERNAL_ERROR, "Unexpected error creating new animation state.", 02269 "SceneManager::createAnimationState"); 02270 } 02271 02272 02273 } 02274 //----------------------------------------------------------------------- 02275 AnimationState* SceneManager::getAnimationState(const String& animName) 02276 { 02277 AnimationStateSet::iterator i = mAnimationStates.find(animName); 02278 02279 if (i == mAnimationStates.end()) 02280 { 02281 Except(Exception::ERR_ITEM_NOT_FOUND, 02282 "Cannot locate animation state for animation " + animName, 02283 "SceneManager::getAnimationState"); 02284 } 02285 02286 return &(i->second); 02287 02288 } 02289 //----------------------------------------------------------------------- 02290 void SceneManager::destroyAnimationState(const String& name) 02291 { 02292 AnimationStateSet::iterator i = mAnimationStates.find(name); 02293 02294 if (i == mAnimationStates.end()) 02295 { 02296 Except(Exception::ERR_ITEM_NOT_FOUND, 02297 "Cannot locate animation state for animation " + name, 02298 "SceneManager::destroyAnimationState"); 02299 } 02300 02301 mAnimationStates.erase(i); 02302 02303 02304 } 02305 //----------------------------------------------------------------------- 02306 void SceneManager::destroyAllAnimationStates(void) 02307 { 02308 mAnimationStates.clear(); 02309 } 02310 //----------------------------------------------------------------------- 02311 void SceneManager::_applySceneAnimations(void) 02312 { 02313 AnimationStateSet::const_iterator i, iend; 02314 02315 i = mAnimationStates.begin(); 02316 iend = mAnimationStates.end(); 02317 02318 for (;i != iend; ++i) 02319 { 02320 if (i->second.getEnabled()) 02321 { 02322 Animation* anim = getAnimation(i->second.getAnimationName()); 02323 02324 // Reset any nodes involved 02325 // NB this excludes blended animations 02326 const Animation::TrackList& trackList = anim->_getTrackList(); 02327 Animation::TrackList::const_iterator ti, tend; 02328 ti = trackList.begin(); 02329 tend = trackList.end(); 02330 for (;ti != tend; ++ti) 02331 { 02332 Node* nd = ti->second->getAssociatedNode(); 02333 nd->resetToInitialState(); 02334 } 02335 02336 02337 // Apply the animation 02338 anim->apply(i->second.getTimePosition(), i->second.getWeight()); 02339 } 02340 } 02341 02342 02343 } 02344 //--------------------------------------------------------------------- 02345 void SceneManager::manualRender(RenderOperation* rend, 02346 Pass* pass, Viewport* vp, const Matrix4& worldMatrix, 02347 const Matrix4& viewMatrix, const Matrix4& projMatrix, 02348 bool doBeginEndFrame) 02349 { 02350 mDestRenderSystem->_setViewport(vp); 02351 mDestRenderSystem->_setWorldMatrix(worldMatrix); 02352 mDestRenderSystem->_setViewMatrix(viewMatrix); 02353 mDestRenderSystem->_setProjectionMatrix(projMatrix); 02354 02355 if (doBeginEndFrame) 02356 mDestRenderSystem->_beginFrame(); 02357 02358 setPass(pass); 02359 mDestRenderSystem->_render(*rend); 02360 02361 if (doBeginEndFrame) 02362 mDestRenderSystem->_endFrame(); 02363 02364 } 02365 //--------------------------------------------------------------------- 02366 Overlay* SceneManager::createOverlay(const String& name, ushort zorder) 02367 { 02368 /* 02369 // check not existing 02370 OverlayList::iterator i = mOverlays.find(name); 02371 if (i != mOverlays.end()) 02372 { 02373 Except(Exception::ERR_DUPLICATE_ITEM, 02374 "An overlay named " + name + " already exists.", 02375 "SceneManager::createOverlay"); 02376 } 02377 Overlay *newOverlay = new Overlay(name, zorder); 02378 02379 mOverlays.insert(OverlayList::value_type(name, newOverlay)); 02380 return newOverlay; 02381 */ 02382 02383 Overlay* newOverlay = (Overlay*)OverlayManager::getSingleton().create(name); 02384 newOverlay->setZOrder(zorder); 02385 return newOverlay; 02386 02387 02388 02389 } 02390 //--------------------------------------------------------------------- 02391 Overlay* SceneManager::getOverlay(const String& name) 02392 { 02393 /* 02394 OverlayList::iterator i = mOverlays.find(name); 02395 if (i == mOverlays.end()) 02396 { 02397 Except(Exception::ERR_ITEM_NOT_FOUND, 02398 "An overlay named " + name + " cannot be found.", 02399 "SceneManager::getOverlay"); 02400 } 02401 02402 return i->second; 02403 */ 02404 Overlay* ret = (Overlay*)OverlayManager::getSingleton().getByName(name); 02405 if (!ret) 02406 { 02407 Except(Exception::ERR_ITEM_NOT_FOUND, 02408 "An overlay named " + name + " cannot be found.", 02409 "SceneManager::getOverlay"); 02410 } 02411 02412 return ret; 02413 02414 } 02415 //--------------------------------------------------------------------- 02416 void SceneManager::destroyOverlay(const String& name) 02417 { 02418 /* 02419 OverlayList::iterator i = mOverlays.find(name); 02420 if (i == mOverlays.end()) 02421 { 02422 Except(Exception::ERR_ITEM_NOT_FOUND, 02423 "An overlay named " + name + " cannot be found.", 02424 "SceneManager::destroyOverlay"); 02425 } 02426 02427 delete i->second; 02428 mOverlays.erase(i); 02429 */ 02430 Overlay* pOver = (Overlay*)OverlayManager::getSingleton().getByName(name); 02431 if (!pOver) 02432 { 02433 Except(Exception::ERR_ITEM_NOT_FOUND, 02434 "An overlay named " + name + " cannot be found.", 02435 "SceneManager::destroyOverlay"); 02436 } 02437 OverlayManager::getSingleton().unload(pOver); 02438 delete pOver; 02439 02440 } 02441 //--------------------------------------------------------------------- 02442 void SceneManager::destroyAllOverlays(void) 02443 { 02444 /* 02445 OverlayList::iterator i, iend; 02446 iend = mOverlays.end(); 02447 for (i = mOverlays.begin(); i != iend; ++i) 02448 { 02449 delete i->second; 02450 } 02451 mOverlays.clear(); 02452 */ 02453 OverlayManager::getSingleton().unloadAndDestroyAll(); 02454 02455 02456 } 02457 //--------------------------------------------------------------------- 02458 void SceneManager::useRenderableViewProjMode(Renderable* pRend) 02459 { 02460 // Check view matrix 02461 static bool lastViewWasIdentity = false; 02462 bool useIdentityView = pRend->useIdentityView(); 02463 if (useIdentityView && (mCamChanged || !lastViewWasIdentity)) 02464 { 02465 // Using identity view now, change it 02466 mDestRenderSystem->_setViewMatrix(Matrix4::IDENTITY); 02467 lastViewWasIdentity = true; 02468 } 02469 else if (!useIdentityView && (mCamChanged || lastViewWasIdentity)) 02470 { 02471 // Coming back to normal from identity view 02472 mDestRenderSystem->_setViewMatrix(mCameraInProgress->getViewMatrix()); 02473 lastViewWasIdentity = false; 02474 } 02475 02476 static bool lastProjWasIdentity = false; 02477 bool useIdentityProj = pRend->useIdentityProjection(); 02478 02479 if (useIdentityProj && (mCamChanged || !lastProjWasIdentity)) 02480 { 02481 mDestRenderSystem->_setProjectionMatrix(Matrix4::IDENTITY); 02482 02483 lastProjWasIdentity = true; 02484 } 02485 else if (!useIdentityProj && (mCamChanged || lastProjWasIdentity)) 02486 { 02487 // Coming back from flat projection 02488 mDestRenderSystem->_setProjectionMatrix(mCameraInProgress->getProjectionMatrix()); 02489 lastProjWasIdentity = false; 02490 } 02491 02492 mCamChanged = false; 02493 02494 } 02495 02496 //--------------------------------------------------------------------- 02497 void SceneManager::_queueSkiesForRendering(Camera* cam) 02498 { 02499 // Update nodes 02500 // Translate the box by the camera position (constant distance) 02501 if (mSkyPlaneNode) 02502 { 02503 // The plane position relative to the camera has already been set up 02504 mSkyPlaneNode->setPosition(cam->getDerivedPosition()); 02505 } 02506 02507 if (mSkyBoxNode) 02508 { 02509 mSkyBoxNode->setPosition(cam->getDerivedPosition()); 02510 } 02511 02512 if (mSkyDomeNode) 02513 { 02514 mSkyDomeNode->setPosition(cam->getDerivedPosition()); 02515 } 02516 02517 RenderQueueGroupID qid; 02518 if (mSkyPlaneEnabled) 02519 { 02520 qid = mSkyPlaneDrawFirst? 02521 RENDER_QUEUE_SKIES_EARLY : RENDER_QUEUE_SKIES_LATE; 02522 getRenderQueue()->addRenderable(mSkyPlaneEntity->getSubEntity(0), qid, RENDERABLE_DEFAULT_PRIORITY); 02523 } 02524 02525 uint plane; 02526 if (mSkyBoxEnabled) 02527 { 02528 qid = mSkyBoxDrawFirst? 02529 RENDER_QUEUE_SKIES_EARLY : RENDER_QUEUE_SKIES_LATE; 02530 02531 for (plane = 0; plane < 6; ++plane) 02532 { 02533 getRenderQueue()->addRenderable( 02534 mSkyBoxEntity[plane]->getSubEntity(0), qid, RENDERABLE_DEFAULT_PRIORITY); 02535 } 02536 } 02537 02538 if (mSkyDomeEnabled) 02539 { 02540 qid = mSkyDomeDrawFirst? 02541 RENDER_QUEUE_SKIES_EARLY : RENDER_QUEUE_SKIES_LATE; 02542 02543 for (plane = 0; plane < 5; ++plane) 02544 { 02545 getRenderQueue()->addRenderable( 02546 mSkyDomeEntity[plane]->getSubEntity(0), qid, RENDERABLE_DEFAULT_PRIORITY); 02547 } 02548 } 02549 } 02550 //--------------------------------------------------------------------- 02551 void SceneManager::addRenderQueueListener(RenderQueueListener* newListener) 02552 { 02553 mRenderQueueListeners.push_back(newListener); 02554 } 02555 //--------------------------------------------------------------------- 02556 void SceneManager::removeRenderQueueListener(RenderQueueListener* delListener) 02557 { 02558 RenderQueueListenerList::iterator i, iend; 02559 iend = mRenderQueueListeners.end(); 02560 for (i = mRenderQueueListeners.begin(); i != iend; ++i) 02561 { 02562 if (*i == delListener) 02563 { 02564 mRenderQueueListeners.erase(i); 02565 break; 02566 } 02567 } 02568 02569 } 02570 //--------------------------------------------------------------------- 02571 bool SceneManager::fireRenderQueueStarted(RenderQueueGroupID id) 02572 { 02573 RenderQueueListenerList::iterator i, iend; 02574 bool skip = false; 02575 02576 iend = mRenderQueueListeners.end(); 02577 for (i = mRenderQueueListeners.begin(); i != iend; ++i) 02578 { 02579 (*i)->renderQueueStarted(id, skip); 02580 } 02581 return skip; 02582 } 02583 //--------------------------------------------------------------------- 02584 bool SceneManager::fireRenderQueueEnded(RenderQueueGroupID id) 02585 { 02586 RenderQueueListenerList::iterator i, iend; 02587 bool repeat = false; 02588 02589 iend = mRenderQueueListeners.end(); 02590 for (i = mRenderQueueListeners.begin(); i != iend; ++i) 02591 { 02592 (*i)->renderQueueEnded(id, repeat); 02593 } 02594 return repeat; 02595 } 02596 //--------------------------------------------------------------------- 02597 void SceneManager::setViewport(Viewport* vp) 02598 { 02599 mCurrentViewport = vp; 02600 // Set viewport in render system 02601 mDestRenderSystem->_setViewport(vp); 02602 } 02603 //--------------------------------------------------------------------- 02604 void SceneManager::showBoundingBoxes(bool bShow) 02605 { 02606 mShowBoundingBoxes = bShow; 02607 } 02608 //--------------------------------------------------------------------- 02609 bool SceneManager::getShowBoundingBoxes() const 02610 { 02611 return mShowBoundingBoxes; 02612 } 02613 //--------------------------------------------------------------------- 02614 void SceneManager::_notifyAutotrackingSceneNode(SceneNode* node, bool autoTrack) 02615 { 02616 if (autoTrack) 02617 { 02618 mAutoTrackingSceneNodes.insert(node); 02619 } 02620 else 02621 { 02622 mAutoTrackingSceneNodes.erase(node); 02623 } 02624 } 02625 //--------------------------------------------------------------------- 02626 void SceneManager::setShadowTechnique(ShadowTechnique technique) 02627 { 02628 mShadowTechnique = technique; 02629 if (technique == SHADOWTYPE_STENCIL_ADDITIVE || 02630 technique == SHADOWTYPE_STENCIL_MODULATIVE) 02631 { 02632 // Firstly check that we have a stencil 02633 // Otherwise forget it 02634 if (!mDestRenderSystem->getCapabilities()->hasCapability(RSC_HWSTENCIL)) 02635 { 02636 LogManager::getSingleton().logMessage( 02637 "WARNING: Stencil shadows were requested, but this device does not " 02638 "have a hardware stencil. Shadows disabled."); 02639 mShadowTechnique = SHADOWTYPE_NONE; 02640 } 02641 else if (mShadowIndexBuffer.isNull()) 02642 { 02643 // Create an estimated sized shadow index buffer 02644 mShadowIndexBuffer = HardwareBufferManager::getSingleton(). 02645 createIndexBuffer(HardwareIndexBuffer::IT_16BIT, 02646 mShadowIndexBufferSize, 02647 HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, 02648 false); 02649 // tell all meshes to prepare shadow volumes 02650 MeshManager::getSingleton().setPrepareAllMeshesForShadowVolumes(true); 02651 } 02652 } 02653 02654 if (mShadowTechnique == SHADOWTYPE_STENCIL_ADDITIVE) 02655 { 02656 // Additive stencil, we need to split everything by illumination stage 02657 getRenderQueue()->setSplitPassesByLightingType(true); 02658 } 02659 else 02660 { 02661 getRenderQueue()->setSplitPassesByLightingType(false); 02662 } 02663 02664 if (mShadowTechnique != SHADOWTYPE_NONE) 02665 { 02666 // Tell render queue to split off non-shadowable materials 02667 getRenderQueue()->setSplitNoShadowPasses(true); 02668 } 02669 else 02670 { 02671 getRenderQueue()->setSplitNoShadowPasses(false); 02672 } 02673 02674 if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE 02675 /* || mShadowTechnique == SHADOWTYPE_TEXTURE_SHADOWMAP */) 02676 { 02677 createShadowTextures(mShadowTextureSize, mShadowTextureCount); 02678 } 02679 02680 } 02681 //--------------------------------------------------------------------- 02682 void SceneManager::findLightsAffectingFrustum(const Camera* camera) 02683 { 02684 // Basic iteration for this SM 02685 mLightsAffectingFrustum.clear(); 02686 SceneLightList::iterator i, iend; 02687 iend = mLights.end(); 02688 Sphere sphere; 02689 for (i = mLights.begin(); i != iend; ++i) 02690 { 02691 Light* l = i->second; 02692 if (l->getType() == Light::LT_DIRECTIONAL) 02693 { 02694 // Always visible 02695 mLightsAffectingFrustum.push_back(l); 02696 } 02697 else 02698 { 02699 // NB treating spotlight as point for simplicity 02700 // Just see if the lights attenuation range is within the frustum 02701 sphere.setCenter(l->getDerivedPosition()); 02702 sphere.setRadius(l->getAttenuationRange()); 02703 if (camera->isVisible(sphere)) 02704 { 02705 mLightsAffectingFrustum.push_back(l); 02706 } 02707 02708 } 02709 } 02710 02711 } 02712 //--------------------------------------------------------------------- 02713 bool SceneManager::ShadowCasterSceneQueryListener::queryResult( 02714 MovableObject* object) 02715 { 02716 if (object->getCastShadows() && object->isVisible()) 02717 { 02718 if (mFarDistSquared) 02719 { 02720 // Check object is within the shadow far distance 02721 Vector3 toObj = object->getParentNode()->_getDerivedPosition() 02722 - mCamera->getDerivedPosition(); 02723 Real radius = object->getWorldBoundingSphere().getRadius(); 02724 Real dist = toObj.squaredLength(); 02725 if (dist - (radius * radius) > mFarDistSquared) 02726 { 02727 // skip, beyond max range 02728 return true; 02729 } 02730 } 02731 02732 // If the object is in the frustum, we can always see the shadow 02733 if (mCamera->isVisible(object->getWorldBoundingBox())) 02734 { 02735 mCasterList->push_back(object); 02736 return true; 02737 } 02738 02739 // Otherwise, object can only be casting a shadow into our view if 02740 // the light is outside the frustum (or it's a directional light, 02741 // which are always outside), and the object is intersecting 02742 // on of the volumes formed between the edges of the frustum and the 02743 // light 02744 if (!mIsLightInFrustum || mLight->getType() == Light::LT_DIRECTIONAL) 02745 { 02746 // Iterate over volumes 02747 PlaneBoundedVolumeList::const_iterator i, iend; 02748 iend = mLightClipVolumeList->end(); 02749 for (i = mLightClipVolumeList->begin(); i != iend; ++i) 02750 { 02751 if (i->intersects(object->getWorldBoundingBox())) 02752 { 02753 mCasterList->push_back(object); 02754 return true; 02755 } 02756 02757 } 02758 02759 } 02760 } 02761 return true; 02762 } 02763 //--------------------------------------------------------------------- 02764 bool SceneManager::ShadowCasterSceneQueryListener::queryResult( 02765 SceneQuery::WorldFragment* fragment) 02766 { 02767 // don't deal with world geometry 02768 return true; 02769 } 02770 //--------------------------------------------------------------------- 02771 const SceneManager::ShadowCasterList& SceneManager::findShadowCastersForLight( 02772 const Light* light, const Camera* camera) 02773 { 02774 mShadowCasterList.clear(); 02775 02776 if (light->getType() == Light::LT_DIRECTIONAL) 02777 { 02778 // Basic AABB query encompassing the frustum and the extrusion of it 02779 AxisAlignedBox aabb; 02780 const Vector3* corners = camera->getWorldSpaceCorners(); 02781 Vector3 min, max; 02782 Vector3 extrude = light->getDirection() * -mShadowDirLightExtrudeDist; 02783 // do first corner 02784 min = max = corners[0]; 02785 min.makeFloor(corners[0] + extrude); 02786 max.makeCeil(corners[0] + extrude); 02787 for (size_t c = 1; c < 8; ++c) 02788 { 02789 min.makeFloor(corners[c]); 02790 max.makeCeil(corners[c]); 02791 min.makeFloor(corners[c] + extrude); 02792 max.makeCeil(corners[c] + extrude); 02793 } 02794 aabb.setExtents(min, max); 02795 02796 if (!mShadowCasterAABBQuery) 02797 mShadowCasterAABBQuery = createAABBQuery(aabb); 02798 else 02799 mShadowCasterAABBQuery->setBox(aabb); 02800 // Execute, use callback 02801 mShadowCasterQueryListener.prepare(false, 02802 &(light->_getFrustumClipVolumes(camera)), 02803 light, camera, &mShadowCasterList, mShadowFarDistSquared); 02804 mShadowCasterAABBQuery->execute(&mShadowCasterQueryListener); 02805 02806 02807 } 02808 else 02809 { 02810 Sphere s(light->getPosition(), light->getAttenuationRange()); 02811 // eliminate early if camera cannot see light sphere 02812 if (camera->isVisible(s)) 02813 { 02814 if (!mShadowCasterSphereQuery) 02815 mShadowCasterSphereQuery = createSphereQuery(s); 02816 else 02817 mShadowCasterSphereQuery->setSphere(s); 02818 02819 // Determine if light is inside or outside the frustum 02820 bool lightInFrustum = camera->isVisible(light->getDerivedPosition()); 02821 const PlaneBoundedVolumeList* volList = 0; 02822 if (!lightInFrustum) 02823 { 02824 // Only worth building an external volume list if 02825 // light is outside the frustum 02826 volList = &(light->_getFrustumClipVolumes(camera)); 02827 } 02828 02829 // Execute, use callback 02830 mShadowCasterQueryListener.prepare(lightInFrustum, 02831 volList, light, camera, &mShadowCasterList, mShadowFarDistSquared); 02832 mShadowCasterSphereQuery->execute(&mShadowCasterQueryListener); 02833 02834 } 02835 02836 } 02837 02838 02839 return mShadowCasterList; 02840 } 02841 //--------------------------------------------------------------------- 02842 void SceneManager::initShadowVolumeMaterials(void) 02843 { 02844 02845 if (mShadowMaterialInitDone) 02846 return; 02847 02848 if (!mShadowDebugPass) 02849 { 02850 Material* matDebug = static_cast<Material*>( 02851 MaterialManager::getSingleton().getByName( 02852 "Ogre/Debug/ShadowVolumes")); 02853 if (!matDebug) 02854 { 02855 // Create 02856 matDebug = static_cast<Material*>( 02857 MaterialManager::getSingleton().create("Ogre/Debug/ShadowVolumes")); 02858 mShadowDebugPass = matDebug->getTechnique(0)->getPass(0); 02859 mShadowDebugPass->setSceneBlending(SBT_ADD); 02860 mShadowDebugPass->setLightingEnabled(false); 02861 mShadowDebugPass->setDepthWriteEnabled(false); 02862 TextureUnitState* t = mShadowDebugPass->createTextureUnitState(); 02863 t->setColourOperationEx(LBX_MODULATE, LBS_MANUAL, LBS_CURRENT, 02864 ColourValue(0.7, 0.0, 0.2)); 02865 mShadowDebugPass->setCullingMode(CULL_NONE); 02866 02867 if (mDestRenderSystem->getCapabilities()->hasCapability( 02868 RSC_VERTEX_PROGRAM)) 02869 { 02870 ShadowVolumeExtrudeProgram::initialise(); 02871 02872 // Enable the (infinite) point light extruder for now, just to get some params 02873 mShadowDebugPass->setVertexProgram( 02874 ShadowVolumeExtrudeProgram::programNames[ShadowVolumeExtrudeProgram::POINT_LIGHT]); 02875 mInfiniteExtrusionParams = 02876 mShadowDebugPass->getVertexProgramParameters(); 02877 mInfiniteExtrusionParams->setAutoConstant(0, 02878 GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); 02879 mInfiniteExtrusionParams->setAutoConstant(4, 02880 GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE); 02881 } 02882 matDebug->compile(); 02883 02884 } 02885 else 02886 { 02887 mShadowDebugPass = matDebug->getTechnique(0)->getPass(0); 02888 } 02889 } 02890 02891 if (!mShadowStencilPass) 02892 { 02893 02894 Material* matStencil = static_cast<Material*>( 02895 MaterialManager::getSingleton().getByName( 02896 "Ogre/StencilShadowVolumes")); 02897 if (!matStencil) 02898 { 02899 // Init 02900 matStencil = static_cast<Material*>( 02901 MaterialManager::getSingleton().create( 02902 "Ogre/StencilShadowVolumes")); 02903 mShadowStencilPass = matStencil->getTechnique(0)->getPass(0); 02904 02905 if (mDestRenderSystem->getCapabilities()->hasCapability( 02906 RSC_VERTEX_PROGRAM)) 02907 { 02908 02909 // Enable the finite point light extruder for now, just to get some params 02910 mShadowStencilPass->setVertexProgram( 02911 ShadowVolumeExtrudeProgram::programNames[ShadowVolumeExtrudeProgram::POINT_LIGHT_FINITE]); 02912 mFiniteExtrusionParams = 02913 mShadowStencilPass->getVertexProgramParameters(); 02914 mFiniteExtrusionParams->setAutoConstant(0, 02915 GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); 02916 mFiniteExtrusionParams->setAutoConstant(4, 02917 GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE); 02918 // Note extra parameter 02919 mFiniteExtrusionParams->setAutoConstant(5, 02920 GpuProgramParameters::ACT_SHADOW_EXTRUSION_DISTANCE); 02921 } 02922 matStencil->compile(); 02923 // Nothing else, we don't use this like a 'real' pass anyway, 02924 // it's more of a placeholder 02925 } 02926 else 02927 { 02928 mShadowStencilPass = matStencil->getTechnique(0)->getPass(0); 02929 } 02930 } 02931 02932 02933 02934 02935 if (!mShadowModulativePass) 02936 { 02937 02938 Material* matModStencil = static_cast<Material*>( 02939 MaterialManager::getSingleton().getByName( 02940 "Ogre/StencilShadowModulationPass")); 02941 if (!matModStencil) 02942 { 02943 // Init 02944 matModStencil = static_cast<Material*>( 02945 MaterialManager::getSingleton().create( 02946 "Ogre/StencilShadowModulationPass")); 02947 mShadowModulativePass = matModStencil->getTechnique(0)->getPass(0); 02948 mShadowModulativePass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO); 02949 mShadowModulativePass->setLightingEnabled(false); 02950 mShadowModulativePass->setDepthWriteEnabled(false); 02951 mShadowModulativePass->setDepthCheckEnabled(false); 02952 TextureUnitState* t = mShadowModulativePass->createTextureUnitState(); 02953 t->setColourOperationEx(LBX_MODULATE, LBS_MANUAL, LBS_CURRENT, 02954 mShadowColour); 02955 02956 } 02957 else 02958 { 02959 mShadowModulativePass = matModStencil->getTechnique(0)->getPass(0); 02960 } 02961 } 02962 02963 // Also init full screen quad while we're at it 02964 if (!mFullScreenQuad) 02965 { 02966 mFullScreenQuad = new Rectangle2D(); 02967 mFullScreenQuad->setCorners(-1,1,1,-1); 02968 } 02969 02970 // Also init shadow caster material for texture shadows 02971 if (!mShadowCasterPlainBlackPass) 02972 { 02973 Material* matPlainBlack = static_cast<Material*>( 02974 MaterialManager::getSingleton().getByName( 02975 "Ogre/TextureShadowCaster")); 02976 if (!matPlainBlack) 02977 { 02978 matPlainBlack = static_cast<Material*>( 02979 MaterialManager::getSingleton().create( 02980 "Ogre/TextureShadowCaster")); 02981 mShadowCasterPlainBlackPass = matPlainBlack->getTechnique(0)->getPass(0); 02982 // Lighting has to be on, because we need shadow coloured objects 02983 // Note that because we can't predict vertex programs, we'll have to 02984 // bind light values to those, and so we bind White to ambient 02985 // reflectance, and we'll set the ambient colour to the shadow colour 02986 mShadowCasterPlainBlackPass->setAmbient(ColourValue::White); 02987 mShadowCasterPlainBlackPass->setDiffuse(ColourValue::Black); 02988 mShadowCasterPlainBlackPass->setSelfIllumination(ColourValue::Black); 02989 mShadowCasterPlainBlackPass->setSpecular(ColourValue::Black); 02990 // no textures or anything else, we will bind vertex programs 02991 // every so often though 02992 } 02993 else 02994 { 02995 mShadowCasterPlainBlackPass = matPlainBlack->getTechnique(0)->getPass(0); 02996 } 02997 } 02998 02999 if (!mShadowReceiverPass) 03000 { 03001 Material* matShadRec = static_cast<Material*>( 03002 MaterialManager::getSingleton().getByName( 03003 "Ogre/TextureShadowReceiver")); 03004 if (!matShadRec) 03005 { 03006 matShadRec = static_cast<Material*>( 03007 MaterialManager::getSingleton().create("Ogre/TextureShadowReceiver")); 03008 mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0); 03009 mShadowReceiverPass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO); 03010 // No lighting, one texture unit 03011 // everything else will be bound as needed during the receiver pass 03012 mShadowReceiverPass->setLightingEnabled(false); 03013 TextureUnitState* t = mShadowReceiverPass->createTextureUnitState(); 03014 t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); 03015 } 03016 else 03017 { 03018 mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0); 03019 } 03020 } 03021 03022 // Set up spot shadow fade texture (loaded from code data block) 03023 Texture* spotShadowFadeTex = (Texture*) 03024 TextureManager::getSingleton().getByName("spot_shadow_fade.png"); 03025 if (!spotShadowFadeTex) 03026 { 03027 // Load the manual buffer into an image 03028 DataChunk chunk(SPOT_SHADOW_FADE_PNG, SPOT_SHADOW_FADE_PNG_SIZE); 03029 Image img; 03030 img.load(chunk, "png"); 03031 spotShadowFadeTex = 03032 TextureManager::getSingleton().loadImage("spot_shadow_fade.png", img, TEX_TYPE_2D); 03033 } 03034 03035 mShadowMaterialInitDone = true; 03036 } 03037 //--------------------------------------------------------------------- 03038 Pass* SceneManager::deriveShadowCasterPass(Pass* pass) 03039 { 03040 switch (mShadowTechnique) 03041 { 03042 case SHADOWTYPE_TEXTURE_MODULATIVE: 03043 if (pass->hasVertexProgram()) 03044 { 03045 // Have to merge the shadow caster vertex program in 03046 // This may in fact be blank, in which case it falls back on 03047 // fixed function 03048 mShadowCasterPlainBlackPass->setVertexProgram( 03049 pass->getShadowCasterVertexProgramName()); 03050 // Did this result in a new vertex program? 03051 if (mShadowCasterPlainBlackPass->hasVertexProgram()) 03052 { 03053 GpuProgram* prg = mShadowCasterPlainBlackPass->getVertexProgram(); 03054 // Load this program if not done already 03055 if (!prg->isLoaded()) 03056 prg->load(); 03057 // Copy params 03058 mShadowCasterPlainBlackPass->setVertexProgramParameters( 03059 pass->getShadowCasterVertexProgramParameters()); 03060 } 03061 // Also have to hack the light autoparams, that is done later 03062 } 03063 else if (mShadowCasterPlainBlackPass->hasVertexProgram()) 03064 { 03065 // reset 03066 mShadowCasterPlainBlackPass->setVertexProgram(""); 03067 } 03068 return mShadowCasterPlainBlackPass; 03069 /* 03070 case SHADOWTYPE_TEXTURE_SHADOWMAP: 03071 // todo 03072 return pass; 03073 */ 03074 default: 03075 return pass; 03076 }; 03077 03078 } 03079 //--------------------------------------------------------------------- 03080 Pass* SceneManager::deriveShadowReceiverPass(Pass* pass) 03081 { 03082 03083 switch (mShadowTechnique) 03084 { 03085 case SHADOWTYPE_TEXTURE_MODULATIVE: 03086 if (pass->hasVertexProgram()) 03087 { 03088 // Have to merge the receiver vertex program in 03089 // This may return "" which means fixed function will be used 03090 mShadowReceiverPass->setVertexProgram( 03091 pass->getShadowReceiverVertexProgramName()); 03092 // Did this result in a new vertex program? 03093 if (mShadowReceiverPass->hasVertexProgram()) 03094 { 03095 GpuProgram* prg = mShadowReceiverPass->getVertexProgram(); 03096 // Load this program if required 03097 if (!prg->isLoaded()) 03098 prg->load(); 03099 // Copy params 03100 mShadowReceiverPass->setVertexProgramParameters( 03101 pass->getShadowReceiverVertexProgramParameters()); 03102 } 03103 // Also have to hack the light autoparams, that is done later 03104 } 03105 else if (mShadowReceiverPass->hasVertexProgram()) 03106 { 03107 // reset 03108 mShadowReceiverPass->setVertexProgram(""); 03109 03110 } 03111 03112 return mShadowReceiverPass; 03113 /* 03114 case SHADOWTYPE_TEXTURE_SHADOWMAP: 03115 // todo 03116 return pass; 03117 */ 03118 default: 03119 return pass; 03120 }; 03121 03122 } 03123 //--------------------------------------------------------------------- 03124 void SceneManager::renderShadowVolumesToStencil(const Light* light, const Camera* camera) 03125 { 03126 03127 // Set up scissor test (point & spot lights only) 03128 bool scissored = false; 03129 if (light->getType() != Light::LT_DIRECTIONAL && 03130 mDestRenderSystem->getCapabilities()->hasCapability(RSC_SCISSOR_TEST)) 03131 { 03132 // Project the sphere onto the camera 03133 Real left, right, top, bottom; 03134 Sphere sphere(light->getDerivedPosition(), light->getAttenuationRange()); 03135 if (camera->projectSphere(sphere, &left, &top, &right, &bottom)) 03136 { 03137 scissored = true; 03138 // Turn normalised device coordinates into pixels 03139 int iLeft, iTop, iWidth, iHeight; 03140 mCurrentViewport->getActualDimensions(iLeft, iTop, iWidth, iHeight); 03141 size_t szLeft, szRight, szTop, szBottom; 03142 03143 szLeft = (size_t)(iLeft + ((left + 1) * 0.5 * iWidth)); 03144 szRight = (size_t)(iLeft + ((right + 1) * 0.5 * iWidth)); 03145 szTop = (size_t)(iTop + ((-top + 1) * 0.5 * iHeight)); 03146 szBottom = (size_t)(iTop + ((-bottom + 1) * 0.5 * iHeight)); 03147 03148 mDestRenderSystem->setScissorTest(true, szLeft, szTop, szRight, szBottom); 03149 } 03150 } 03151 03152 mDestRenderSystem->unbindGpuProgram(GPT_FRAGMENT_PROGRAM); 03153 03154 // Can we do a 2-sided stencil? 03155 bool stencil2sided = false; 03156 if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_TWO_SIDED_STENCIL) && 03157 mDestRenderSystem->getCapabilities()->hasCapability(RSC_STENCIL_WRAP)) 03158 { 03159 // enable 03160 stencil2sided = true; 03161 } 03162 03163 // Do we have access to vertex programs? 03164 bool extrudeInSoftware = true; 03165 bool finiteExtrude = !mShadowUseInfiniteFarPlane || 03166 !mDestRenderSystem->getCapabilities()->hasCapability(RSC_INFINITE_FAR_PLANE); 03167 if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM)) 03168 { 03169 extrudeInSoftware = false; 03170 // attach the appropriate extrusion vertex program 03171 // Note we never unset it because support for vertex programs is constant 03172 mShadowStencilPass->setVertexProgram( 03173 ShadowVolumeExtrudeProgram::getProgramName(light->getType(), finiteExtrude, false) 03174 , false); 03175 // Set params 03176 if (finiteExtrude) 03177 { 03178 mShadowStencilPass->setVertexProgramParameters(mFiniteExtrusionParams); 03179 } 03180 else 03181 { 03182 mShadowStencilPass->setVertexProgramParameters(mInfiniteExtrusionParams); 03183 } 03184 if (mDebugShadows) 03185 { 03186 mShadowDebugPass->setVertexProgram( 03187 ShadowVolumeExtrudeProgram::getProgramName(light->getType(), finiteExtrude, true) 03188 , false); 03189 // Set params 03190 if (finiteExtrude) 03191 { 03192 mShadowDebugPass->setVertexProgramParameters(mFiniteExtrusionParams); 03193 } 03194 else 03195 { 03196 mShadowDebugPass->setVertexProgramParameters(mInfiniteExtrusionParams); 03197 } 03198 } 03199 03200 mDestRenderSystem->bindGpuProgram(mShadowStencilPass->getVertexProgram()->_getBindingDelegate()); 03201 03202 } 03203 else 03204 { 03205 mDestRenderSystem->unbindGpuProgram(GPT_VERTEX_PROGRAM); 03206 } 03207 03208 // Add light to internal list for use in render call 03209 LightList lightList; 03210 // const_cast is forgiveable here since we pass this const 03211 lightList.push_back(const_cast<Light*>(light)); 03212 03213 // Turn off colour writing and depth writing 03214 mDestRenderSystem->_setColourBufferWriteEnabled(false, false, false, false); 03215 mDestRenderSystem->_setDepthBufferWriteEnabled(false); 03216 mDestRenderSystem->setStencilCheckEnabled(true); 03217 mDestRenderSystem->_setDepthBufferFunction(CMPF_LESS); 03218 03219 // Calculate extrusion distance 03220 Real extrudeDist; 03221 if (light->getType() == Light::LT_DIRECTIONAL) 03222 { 03223 extrudeDist = mShadowDirLightExtrudeDist; 03224 } 03225 03226 // Figure out the near clip volume 03227 const PlaneBoundedVolume& nearClipVol = 03228 light->_getNearClipVolume(camera); 03229 03230 // Get the shadow caster list 03231 const ShadowCasterList& casters = findShadowCastersForLight(light, camera); 03232 ShadowCasterList::const_iterator si, siend; 03233 siend = casters.end(); 03234 03235 // Determine whether zfail is required 03236 // We need to use zfail for ALL objects if we find a single object which 03237 // requires it 03238 bool zfailAlgo = false; 03239 unsigned long flags = 0; 03240 03241 for (si = casters.begin(); si != siend; ++si) 03242 { 03243 ShadowCaster* caster = *si; 03244 03245 if (nearClipVol.intersects(caster->getWorldBoundingBox())) 03246 { 03247 // We have a zfail case, we must use zfail for all objects 03248 zfailAlgo = true; 03249 break; 03250 } 03251 } 03252 03253 // Now iterate over the casters and render 03254 for (si = casters.begin(); si != siend; ++si) 03255 { 03256 ShadowCaster* caster = *si; 03257 flags = 0; 03258 03259 if (light->getType() != Light::LT_DIRECTIONAL) 03260 { 03261 extrudeDist = caster->getPointExtrusionDistance(light); 03262 } 03263 03264 if (!extrudeInSoftware && !finiteExtrude) 03265 { 03266 // hardware extrusion, to infinity (and beyond!) 03267 flags |= SRF_EXTRUDE_TO_INFINITY; 03268 } 03269 03270 if (zfailAlgo) 03271 { 03272 // We need to include the light and / or dark cap 03273 // But only if they will be visible 03274 if(camera->isVisible(caster->getLightCapBounds())) 03275 { 03276 flags |= SRF_INCLUDE_LIGHT_CAP; 03277 } 03278 } 03279 // Dark cap (no dark cap for directional lights using 03280 // hardware extrusion to infinity) 03281 if(!((flags & SRF_EXTRUDE_TO_INFINITY) && 03282 light->getType() == Light::LT_DIRECTIONAL) && 03283 camera->isVisible(caster->getDarkCapBounds(*light, extrudeDist))) 03284 { 03285 flags |= SRF_INCLUDE_DARK_CAP; 03286 } 03287 03288 // Get shadow renderables 03289 ShadowCaster::ShadowRenderableListIterator iShadowRenderables = 03290 caster->getShadowVolumeRenderableIterator(mShadowTechnique, 03291 light, &mShadowIndexBuffer, extrudeInSoftware, 03292 extrudeDist, flags); 03293 03294 // If using one-sided stencil, render the first pass of all shadow 03295 // renderables before all the second passes 03296 for (int i = 0; i < (stencil2sided ? 1 : 2); i++) 03297 { 03298 if (i == 1) 03299 iShadowRenderables = caster->getLastShadowVolumeRenderableIterator(); 03300 03301 while (iShadowRenderables.hasMoreElements()) 03302 { 03303 ShadowRenderable* sr = iShadowRenderables.getNext(); 03304 // omit hidden renderables 03305 if (sr->isVisible()) 03306 { 03307 // render volume, including dark and (maybe) light caps 03308 renderSingleShadowVolumeToStencil(sr, zfailAlgo, stencil2sided, &lightList, i); 03309 03310 // optionally render separate light cap 03311 if (sr->isLightCapSeparate() && (flags & SRF_INCLUDE_LIGHT_CAP)) 03312 { 03313 // must always fail depth check 03314 mDestRenderSystem->_setDepthBufferFunction(CMPF_ALWAYS_FAIL); 03315 assert(sr->getLightCapRenderable() && "Shadow renderable is missing a separate light cap renderable!"); 03316 renderSingleShadowVolumeToStencil(sr->getLightCapRenderable(), zfailAlgo, stencil2sided, &lightList, i); 03317 // reset depth function 03318 mDestRenderSystem->_setDepthBufferFunction(CMPF_LESS); 03319 } 03320 } 03321 } 03322 } 03323 } 03324 03325 // revert colour write state 03326 mDestRenderSystem->_setColourBufferWriteEnabled(true, true, true, true); 03327 // revert depth state 03328 mDestRenderSystem->_setDepthBufferParams(); 03329 03330 mDestRenderSystem->setStencilCheckEnabled(false); 03331 03332 mDestRenderSystem->unbindGpuProgram(GPT_VERTEX_PROGRAM); 03333 03334 if (scissored) 03335 { 03336 // disable scissor test 03337 mDestRenderSystem->setScissorTest(false); 03338 } 03339 03340 } 03341 //--------------------------------------------------------------------- 03342 void SceneManager::renderSingleShadowVolumeToStencil(ShadowRenderable* sr, bool zfailAlgo, bool stencil2sided, const LightList *manualLightList, bool bSecondPass) 03343 { 03344 // Render a shadow volume here 03345 // - if we have 2-sided stencil, one render with no culling 03346 // - otherwise, 2 renders, one with each culling method and invert the ops 03347 if (!bSecondPass) 03348 { 03349 setShadowVolumeStencilState(false, zfailAlgo, stencil2sided); 03350 renderSingleObject(sr, mShadowStencilPass, false, manualLightList); 03351 } 03352 03353 if (!stencil2sided && bSecondPass) 03354 { 03355 // Second pass 03356 setShadowVolumeStencilState(true, zfailAlgo, false); 03357 renderSingleObject(sr, mShadowStencilPass, false); 03358 } 03359 03360 // Do we need to render a debug shadow marker? 03361 if (mDebugShadows && (bSecondPass || stencil2sided)) 03362 { 03363 // reset stencil & colour ops 03364 mDestRenderSystem->setStencilBufferParams(); 03365 setPass(mShadowDebugPass); 03366 renderSingleObject(sr, mShadowDebugPass, false, manualLightList); 03367 mDestRenderSystem->_setColourBufferWriteEnabled(false, false, false, false); 03368 } 03369 } 03370 //--------------------------------------------------------------------- 03371 void SceneManager::setShadowVolumeStencilState(bool secondpass, bool zfail, bool twosided) 03372 { 03373 // First pass, do front faces if zpass 03374 // Second pass, do back faces if zpass 03375 // Invert if zfail 03376 // this is to ensure we always increment before decrement 03377 if ( (secondpass || zfail) && 03378 !(secondpass && zfail) ) 03379 { 03380 mDestRenderSystem->_setCullingMode( 03381 twosided? CULL_NONE : CULL_ANTICLOCKWISE); 03382 mDestRenderSystem->setStencilBufferParams( 03383 CMPF_ALWAYS_PASS, // always pass stencil check 03384 0, // no ref value (no compare) 03385 0xFFFFFFFF, // no mask 03386 SOP_KEEP, // stencil test will never fail 03387 zfail? (twosided? SOP_INCREMENT_WRAP : SOP_INCREMENT) : SOP_KEEP, // back face depth fail 03388 zfail? SOP_KEEP : (twosided? SOP_DECREMENT_WRAP : SOP_DECREMENT), // back face pass 03389 twosided 03390 ); 03391 } 03392 else 03393 { 03394 mDestRenderSystem->_setCullingMode( 03395 twosided? CULL_NONE : CULL_CLOCKWISE); 03396 mDestRenderSystem->setStencilBufferParams( 03397 CMPF_ALWAYS_PASS, // always pass stencil check 03398 0, // no ref value (no compare) 03399 0xFFFFFFFF, // no mask 03400 SOP_KEEP, // stencil test will never fail 03401 zfail? (twosided? SOP_DECREMENT_WRAP : SOP_DECREMENT) : SOP_KEEP, // front face depth fail 03402 zfail? SOP_KEEP : (twosided? SOP_INCREMENT_WRAP : SOP_INCREMENT), // front face pass 03403 twosided 03404 ); 03405 } 03406 } 03407 //--------------------------------------------------------------------- 03408 void SceneManager::setShadowColour(const ColourValue& colour) 03409 { 03410 mShadowColour = colour; 03411 03412 if (!mShadowModulativePass && !mShadowCasterPlainBlackPass) 03413 initShadowVolumeMaterials(); 03414 03415 mShadowModulativePass->getTextureUnitState(0)->setColourOperationEx( 03416 LBX_MODULATE, LBS_MANUAL, LBS_CURRENT, colour); 03417 } 03418 //--------------------------------------------------------------------- 03419 const ColourValue& SceneManager::getShadowColour(void) const 03420 { 03421 return mShadowColour; 03422 } 03423 //--------------------------------------------------------------------- 03424 void SceneManager::setShadowFarDistance(Real distance) 03425 { 03426 mShadowFarDist = distance; 03427 mShadowFarDistSquared = distance * distance; 03428 } 03429 //--------------------------------------------------------------------- 03430 void SceneManager::setShadowDirectionalLightExtrusionDistance(Real dist) 03431 { 03432 mShadowDirLightExtrudeDist = dist; 03433 } 03434 //--------------------------------------------------------------------- 03435 Real SceneManager::getShadowDirectionalLightExtrusionDistance(void) const 03436 { 03437 return mShadowDirLightExtrudeDist; 03438 } 03439 //--------------------------------------------------------------------- 03440 void SceneManager::setShadowIndexBufferSize(size_t size) 03441 { 03442 if (!mShadowIndexBuffer.isNull() && size != mShadowIndexBufferSize) 03443 { 03444 // re-create shadow buffer with new size 03445 mShadowIndexBuffer = HardwareBufferManager::getSingleton(). 03446 createIndexBuffer(HardwareIndexBuffer::IT_16BIT, 03447 mShadowIndexBufferSize, 03448 HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, 03449 false); 03450 } 03451 mShadowIndexBufferSize = size; 03452 } 03453 //--------------------------------------------------------------------- 03454 void SceneManager::setShadowTextureSize(unsigned short size) 03455 { 03456 // possibly recreate 03457 createShadowTextures(size, mShadowTextureCount); 03458 mShadowTextureSize = size; 03459 } 03460 //--------------------------------------------------------------------- 03461 void SceneManager::setShadowTextureCount(unsigned short count) 03462 { 03463 // possibly recreate 03464 createShadowTextures(mShadowTextureSize, count); 03465 mShadowTextureCount = count; 03466 } 03467 //--------------------------------------------------------------------- 03468 void SceneManager::setShadowTextureSettings(unsigned short size, unsigned short count) 03469 { 03470 if (!mShadowTextures.empty() && 03471 (count != mShadowTextureCount || 03472 size != mShadowTextureSize)) 03473 { 03474 // recreate 03475 createShadowTextures(size, count); 03476 } 03477 mShadowTextureCount = count; 03478 mShadowTextureSize = size; 03479 } 03480 //--------------------------------------------------------------------- 03481 void SceneManager::createShadowTextures(unsigned short size, unsigned short count) 03482 { 03483 static const String baseName = "Ogre/ShadowTexture"; 03484 03485 if ((mShadowTechnique != SHADOWTYPE_TEXTURE_MODULATIVE 03486 /*&& mShadowTechnique != SHADOWTYPE_TEXTURE_SHADOWMAP */) || 03487 !mShadowTextures.empty() && 03488 count == mShadowTextureCount && 03489 size == mShadowTextureSize) 03490 { 03491 // no change 03492 return; 03493 } 03494 03495 03496 // destroy existing 03497 ShadowTextureList::iterator i, iend; 03498 iend = mShadowTextures.end(); 03499 for (i = mShadowTextures.begin(); i != iend; ++i) 03500 { 03501 RenderTexture* r = *i; 03502 // remove camera and destroy texture 03503 removeCamera(r->getViewport(0)->getCamera()); 03504 mDestRenderSystem->destroyRenderTexture(r->getName()); 03505 } 03506 mShadowTextures.clear(); 03507 03508 // Recreate shadow textures 03509 for (unsigned short t = 0; t < count; ++t) 03510 { 03511 String targName = baseName + StringConverter::toString(t); 03512 String matName = baseName + "Mat" + StringConverter::toString(t); 03513 String camName = baseName + "Cam" + StringConverter::toString(t); 03514 03515 RenderTexture* shadowTex; 03516 if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE) 03517 { 03518 shadowTex = mDestRenderSystem->createRenderTexture( 03519 targName, size, size ); 03520 } 03521 /* 03522 else if (mShadowTechnique == SHADOWTYPE_TEXTURE_SHADOWMAP) 03523 { 03524 // todo 03525 } 03526 */ 03527 03528 // Create a camera to go with this texture 03529 Camera* cam = createCamera(camName); 03530 cam->setAspectRatio(1.0f); 03531 // Create a viewport 03532 Viewport *v = shadowTex->addViewport(cam); 03533 v->setClearEveryFrame(true); 03534 // remove overlays 03535 v->setOverlaysEnabled(false); 03536 // Don't update automatically - we'll do it when required 03537 shadowTex->setAutoUpdated(false); 03538 mShadowTextures.push_back(shadowTex); 03539 03540 // Also create corresponding Material used for rendering this shadow 03541 Material* mat = (Material*)MaterialManager::getSingleton().getByName(matName); 03542 if (!mat) 03543 { 03544 mat = (Material*)MaterialManager::getSingleton().create(matName); 03545 } 03546 else 03547 { 03548 mat->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); 03549 } 03550 // create texture unit referring to render target texture 03551 TextureUnitState* texUnit = 03552 mat->getTechnique(0)->getPass(0)->createTextureUnitState(targName); 03553 // set projective based on camera 03554 texUnit->setProjectiveTexturing(true, cam); 03555 texUnit->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); 03556 mat->touch(); 03557 03558 } 03559 } 03560 //--------------------------------------------------------------------- 03561 void SceneManager::prepareShadowTextures(Camera* cam, Viewport* vp) 03562 { 03563 // Set the illumination stage, prevents recursive calls 03564 IlluminationRenderStage savedStage = mIlluminationStage; 03565 mIlluminationStage = IRS_RENDER_TO_TEXTURE; 03566 03567 // Determine far shadow distance 03568 Real shadowDist = mShadowFarDist; 03569 if (!shadowDist) 03570 { 03571 // need a shadow distance, make one up 03572 shadowDist = cam->getNearClipDistance() * 300; 03573 } 03574 // set fogging to hide the shadow edge 03575 Real shadowOffset = shadowDist * mShadowTextureOffset; 03576 Real shadowEnd = shadowDist + shadowOffset; 03577 mShadowReceiverPass->setFog(true, FOG_LINEAR, ColourValue::White, 03578 0, shadowEnd * mShadowTextureFadeStart, shadowEnd * mShadowTextureFadeEnd); 03579 03580 // Iterate over the lights we've found, max out at the limit of light textures 03581 03582 LightList::iterator i, iend; 03583 ShadowTextureList::iterator si, siend; 03584 iend = mLightsAffectingFrustum.end(); 03585 siend = mShadowTextures.end(); 03586 for (i = mLightsAffectingFrustum.begin(), si = mShadowTextures.begin(); 03587 i != iend && si != siend; ++i) 03588 { 03589 Light* light = *i; 03590 RenderTexture* shadowTex = *si; 03591 // Skip non-shadowing lights 03592 if (!light->getCastShadows()) 03593 continue; 03594 03595 // Directional lights 03596 if (light->getType() == Light::LT_DIRECTIONAL) 03597 { 03598 03599 // set up the shadow texture 03600 Camera* texCam = shadowTex->getViewport(0)->getCamera(); 03601 // Set ortho projection 03602 texCam->setProjectionType(PT_ORTHOGRAPHIC); 03603 // set easy FOV and near dist so that texture covers far dist 03604 texCam->setFOVy(Degree(90)); 03605 texCam->setNearClipDistance(shadowDist); 03606 03607 // Set size of projection 03608 03609 // Calculate look at position 03610 // We want to look at a spot shadowOffset away from near plane 03611 // 0.5 is a litle too close for angles 03612 Vector3 target = cam->getDerivedPosition() + 03613 (cam->getDerivedDirection() * shadowOffset); 03614 03615 // Calculate position 03616 // We want to be in the -ve direction of the light direction 03617 // far enough to project for the dir light extrusion distance 03618 Vector3 pos = target + 03619 (light->getDerivedDirection() * -mShadowDirLightExtrudeDist); 03620 03621 // Calculate orientation 03622 Vector3 dir = (pos - target); // backwards since point down -z 03623 dir.normalise(); 03624 /* 03625 // Next section (camera oriented shadow map) abandoned 03626 // Always point in the same direction, if we don't do this then 03627 // we get 'shadow swimming' as camera rotates 03628 // As it is, we get swimming on moving but this is less noticeable 03629 03630 // calculate up vector, we want it aligned with cam direction 03631 Vector3 up = cam->getDerivedDirection(); 03632 // Check it's not coincident with dir 03633 if (up.dotProduct(dir) >= 1.0f) 03634 { 03635 // Use camera up 03636 up = cam->getUp(); 03637 } 03638 */ 03639 Vector3 up = Vector3::UNIT_Y; 03640 // Check it's not coincident with dir 03641 if (up.dotProduct(dir) >= 1.0f) 03642 { 03643 // Use camera up 03644 up = Vector3::UNIT_Z; 03645 } 03646 // cross twice to rederive, only direction is unaltered 03647 Vector3 left = dir.crossProduct(up); 03648 left.normalise(); 03649 up = dir.crossProduct(left); 03650 up.normalise(); 03651 // Derive quaternion from axes 03652 Quaternion q; 03653 q.FromAxes(left, up, dir); 03654 texCam->setOrientation(q); 03655 03656 // Round local x/y position based on a world-space texel; this helps to reduce 03657 // jittering caused by the projection moving with the camera 03658 // Viewport is 2 * near clip distance across (90 degree fov) 03659 Real worldTexelSize = (texCam->getNearClipDistance() * 20) / mShadowTextureSize; 03660 pos.x -= fmod(pos.x, worldTexelSize); 03661 pos.y -= fmod(pos.y, worldTexelSize); 03662 pos.z -= fmod(pos.z, worldTexelSize); 03663 // Finally set position 03664 texCam->setPosition(pos); 03665 03666 if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE) 03667 shadowTex->getViewport(0)->setBackgroundColour(ColourValue::White); 03668 03669 // Update target 03670 shadowTex->update(); 03671 03672 ++si; 03673 } 03674 // Spotlight 03675 else if (light->getType() == Light::LT_SPOTLIGHT) 03676 { 03677 03678 // set up the shadow texture 03679 Camera* texCam = shadowTex->getViewport(0)->getCamera(); 03680 // Set perspective projection 03681 texCam->setProjectionType(PT_PERSPECTIVE); 03682 // set FOV slightly larger than the spotlight range to ensure coverage 03683 texCam->setFOVy(light->getSpotlightOuterAngle()*1.2); 03684 texCam->setPosition(light->getDerivedPosition()); 03685 texCam->setDirection(light->getDerivedDirection()); 03686 // set near clip the same as main camera, since they are likely 03687 // to both reflect the nature of the scene 03688 texCam->setNearClipDistance(cam->getNearClipDistance()); 03689 03690 if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE) 03691 shadowTex->getViewport(0)->setBackgroundColour(ColourValue::White); 03692 03693 // Update target 03694 shadowTex->update(); 03695 03696 ++si; 03697 } 03698 } 03699 // Set the illumination stage, prevents recursive calls 03700 mIlluminationStage = savedStage; 03701 } 03702 //--------------------------------------------------------------------- 03703 AxisAlignedBoxSceneQuery* 03704 SceneManager::createAABBQuery(const AxisAlignedBox& box, unsigned long mask) 03705 { 03706 DefaultAxisAlignedBoxSceneQuery* q = new DefaultAxisAlignedBoxSceneQuery(this); 03707 q->setBox(box); 03708 q->setQueryMask(mask); 03709 return q; 03710 } 03711 //--------------------------------------------------------------------- 03712 SphereSceneQuery* 03713 SceneManager::createSphereQuery(const Sphere& sphere, unsigned long mask) 03714 { 03715 DefaultSphereSceneQuery* q = new DefaultSphereSceneQuery(this); 03716 q->setSphere(sphere); 03717 q->setQueryMask(mask); 03718 return q; 03719 } 03720 //--------------------------------------------------------------------- 03721 PlaneBoundedVolumeListSceneQuery* 03722 SceneManager::createPlaneBoundedVolumeQuery(const PlaneBoundedVolumeList& volumes, 03723 unsigned long mask) 03724 { 03725 DefaultPlaneBoundedVolumeListSceneQuery* q = new DefaultPlaneBoundedVolumeListSceneQuery(this); 03726 q->setVolumes(volumes); 03727 q->setQueryMask(mask); 03728 return q; 03729 } 03730 03731 //--------------------------------------------------------------------- 03732 RaySceneQuery* 03733 SceneManager::createRayQuery(const Ray& ray, unsigned long mask) 03734 { 03735 DefaultRaySceneQuery* q = new DefaultRaySceneQuery(this); 03736 q->setRay(ray); 03737 q->setQueryMask(mask); 03738 return q; 03739 } 03740 //--------------------------------------------------------------------- 03741 IntersectionSceneQuery* 03742 SceneManager::createIntersectionQuery(unsigned long mask) 03743 { 03744 03745 DefaultIntersectionSceneQuery* q = new DefaultIntersectionSceneQuery(this); 03746 q->setQueryMask(mask); 03747 return q; 03748 } 03749 //--------------------------------------------------------------------- 03750 void SceneManager::destroyQuery(SceneQuery* query) 03751 { 03752 delete query; 03753 } 03754 //--------------------------------------------------------------------- 03755 DefaultIntersectionSceneQuery::DefaultIntersectionSceneQuery(SceneManager* creator) 03756 : IntersectionSceneQuery(creator) 03757 { 03758 // No world geometry results supported 03759 mSupportedWorldFragments.insert(SceneQuery::WFT_NONE); 03760 } 03761 //--------------------------------------------------------------------- 03762 DefaultIntersectionSceneQuery::~DefaultIntersectionSceneQuery() 03763 { 03764 } 03765 //--------------------------------------------------------------------- 03766 void DefaultIntersectionSceneQuery::execute(IntersectionSceneQueryListener* listener) 03767 { 03768 // TODO: BillboardSets? Will need per-billboard collision most likely 03769 // Entities only for now 03770 SceneManager::EntityList::const_iterator a, b, theEnd; 03771 theEnd = mParentSceneMgr->mEntities.end(); 03772 int numEntities; 03773 // Loop a from first to last-1 03774 a = mParentSceneMgr->mEntities.begin(); 03775 numEntities = (uint)mParentSceneMgr->mEntities.size(); 03776 for (int i = 0; i < (numEntities - 1); ++i, ++a) 03777 { 03778 // Skip if a does not pass the mask 03779 if (! (a->second->getQueryFlags() & mQueryMask)) 03780 continue; 03781 03782 // Loop b from a+1 to last 03783 b = a; 03784 for (++b; b != theEnd; ++b) 03785 { 03786 // Apply mask to b (both must pass) 03787 if (b->second->getQueryFlags() & mQueryMask) 03788 { 03789 const AxisAlignedBox& box1 = a->second->getWorldBoundingBox(); 03790 const AxisAlignedBox& box2 = b->second->getWorldBoundingBox(); 03791 03792 if (box1.intersects(box2)) 03793 { 03794 listener->queryResult(a->second, b->second); 03795 } 03796 } 03797 03798 } 03799 } 03800 } 03801 //--------------------------------------------------------------------- 03802 DefaultAxisAlignedBoxSceneQuery:: 03803 DefaultAxisAlignedBoxSceneQuery(SceneManager* creator) 03804 : AxisAlignedBoxSceneQuery(creator) 03805 { 03806 // No world geometry results supported 03807 mSupportedWorldFragments.insert(SceneQuery::WFT_NONE); 03808 } 03809 //--------------------------------------------------------------------- 03810 DefaultAxisAlignedBoxSceneQuery::~DefaultAxisAlignedBoxSceneQuery() 03811 { 03812 } 03813 //--------------------------------------------------------------------- 03814 void DefaultAxisAlignedBoxSceneQuery::execute(SceneQueryListener* listener) 03815 { 03816 // TODO: BillboardSets? Will need per-billboard collision most likely 03817 // Entities only for now 03818 SceneManager::EntityList::const_iterator i, iEnd; 03819 iEnd = mParentSceneMgr->mEntities.end(); 03820 for (i = mParentSceneMgr->mEntities.begin(); i != iEnd; ++i) 03821 { 03822 if ((i->second->getQueryFlags() & mQueryMask) && mAABB.intersects(i->second->getWorldBoundingBox())) 03823 { 03824 listener->queryResult(i->second); 03825 } 03826 } 03827 } 03828 //--------------------------------------------------------------------- 03829 DefaultRaySceneQuery:: 03830 DefaultRaySceneQuery(SceneManager* creator) : RaySceneQuery(creator) 03831 { 03832 // No world geometry results supported 03833 mSupportedWorldFragments.insert(SceneQuery::WFT_NONE); 03834 } 03835 //--------------------------------------------------------------------- 03836 DefaultRaySceneQuery::~DefaultRaySceneQuery() 03837 { 03838 } 03839 //--------------------------------------------------------------------- 03840 void DefaultRaySceneQuery::execute(RaySceneQueryListener* listener) 03841 { 03842 // Note that becuase we have no scene partitioning, we actually 03843 // perform a complete scene search even if restricted results are 03844 // requested; smarter scene manager queries can utilise the paritioning 03845 // of the scene in order to reduce the number of intersection tests 03846 // required to fulfil the query 03847 03848 // TODO: BillboardSets? Will need per-billboard collision most likely 03849 // Entities only for now 03850 SceneManager::EntityList::const_iterator i, iEnd; 03851 iEnd = mParentSceneMgr->mEntities.end(); 03852 for (i = mParentSceneMgr->mEntities.begin(); i != iEnd; ++i) 03853 { 03854 if( (i->second->getQueryFlags() & mQueryMask) ) 03855 { 03856 // Do ray / box test 03857 std::pair<bool, Real> result = 03858 mRay.intersects(i->second->getWorldBoundingBox()); 03859 03860 if (result.first) 03861 { 03862 listener->queryResult(i->second, result.second); 03863 } 03864 } 03865 } 03866 03867 } 03868 //--------------------------------------------------------------------- 03869 DefaultSphereSceneQuery:: 03870 DefaultSphereSceneQuery(SceneManager* creator) : SphereSceneQuery(creator) 03871 { 03872 // No world geometry results supported 03873 mSupportedWorldFragments.insert(SceneQuery::WFT_NONE); 03874 } 03875 //--------------------------------------------------------------------- 03876 DefaultSphereSceneQuery::~DefaultSphereSceneQuery() 03877 { 03878 } 03879 //--------------------------------------------------------------------- 03880 void DefaultSphereSceneQuery::execute(SceneQueryListener* listener) 03881 { 03882 // TODO: BillboardSets? Will need per-billboard collision most likely 03883 // Entities only for now 03884 SceneManager::EntityList::const_iterator i, iEnd; 03885 iEnd = mParentSceneMgr->mEntities.end(); 03886 Sphere testSphere; 03887 for (i = mParentSceneMgr->mEntities.begin(); i != iEnd; ++i) 03888 { 03889 // Skip unattached 03890 if (!i->second->getParentNode() || !(i->second->getQueryFlags() & mQueryMask)) 03891 continue; 03892 03893 // Do sphere / sphere test 03894 testSphere.setCenter(i->second->getParentNode()->_getDerivedPosition()); 03895 testSphere.setRadius(i->second->getBoundingRadius()); 03896 if (mSphere.intersects(testSphere)) 03897 { 03898 listener->queryResult(i->second); 03899 } 03900 } 03901 } 03902 //--------------------------------------------------------------------- 03903 DefaultPlaneBoundedVolumeListSceneQuery:: 03904 DefaultPlaneBoundedVolumeListSceneQuery(SceneManager* creator) 03905 : PlaneBoundedVolumeListSceneQuery(creator) 03906 { 03907 // No world geometry results supported 03908 mSupportedWorldFragments.insert(SceneQuery::WFT_NONE); 03909 } 03910 //--------------------------------------------------------------------- 03911 DefaultPlaneBoundedVolumeListSceneQuery::~DefaultPlaneBoundedVolumeListSceneQuery() 03912 { 03913 } 03914 //--------------------------------------------------------------------- 03915 void DefaultPlaneBoundedVolumeListSceneQuery::execute(SceneQueryListener* listener) 03916 { 03917 // Entities only for now 03918 SceneManager::EntityList::const_iterator i, iEnd; 03919 iEnd = mParentSceneMgr->mEntities.end(); 03920 Sphere testSphere; 03921 for (i = mParentSceneMgr->mEntities.begin(); i != iEnd; ++i) 03922 { 03923 PlaneBoundedVolumeList::iterator pi, piend; 03924 piend = mVolumes.end(); 03925 for (pi = mVolumes.begin(); pi != piend; ++pi) 03926 { 03927 PlaneBoundedVolume& vol = *pi; 03928 // Do AABB / plane volume test 03929 if ((i->second->getQueryFlags() & mQueryMask) && vol.intersects(i->second->getWorldBoundingBox())) 03930 { 03931 listener->queryResult(i->second); 03932 break; 03933 } 03934 } 03935 } 03936 } 03937 03938 }
Copyright © 2002-2003 by The OGRE Team
Last modified Sun Nov 28 19:48:44 2004