/** Delete a anti portal instance by name */ void PCZSceneManager::destroyAntiPortal(const String& portalName) { // find the anti portal from the master portal list AntiPortal* p; AntiPortal* thePortal = 0; AntiPortalList::iterator it = mAntiPortals.begin(); while (it != mAntiPortals.end()) { p = *it; if (p->getName() == portalName) { thePortal = p; // erase entry in the master list mAntiPortals.erase(it); break; } it++; } if (thePortal) { // remove the Portal from it's home zone PCZone* homeZone = thePortal->getCurrentHomeZone(); if (homeZone) { // inform zone of portal change homeZone->setPortalsUpdated(true); homeZone->_removeAntiPortal(thePortal); } // delete the portal instance OGRE_DELETE thePortal; } }
/* Find the best (smallest) zone that contains a point */ PCZone * PCZSceneManager::findZoneForPoint(Vector3 & point) { PCZone * zone; PCZone * bestZone = mDefaultZone; Real bestVolume = Ogre::Math::POS_INFINITY; ZoneMap::iterator zit = mZones.begin(); while ( zit != mZones.end() ) { zone = zit->second; AxisAlignedBox aabb; zone->getAABB(aabb); SceneNode * enclosureNode = zone->getEnclosureNode(); if (enclosureNode != 0) { // since this is the "local" AABB, add in world translation of the enclosure node aabb.setMinimum(aabb.getMinimum() + enclosureNode->_getDerivedPosition()); aabb.setMaximum(aabb.getMaximum() + enclosureNode->_getDerivedPosition()); } if (aabb.contains(point)) { if (aabb.volume() < bestVolume) { // this zone is "smaller" than the current best zone, so make it // the new best zone bestZone = zone; bestVolume = aabb.volume(); } } // proceed to next zone in the list ++zit; } return bestZone; }
void DefaultZone::_checkNodeAgainstPortals(PCZSceneNode * pczsn, Portal * ignorePortal) { if (pczsn == mEnclosureNode || pczsn->allowedToVisit() == false) { // don't do any checking of enclosure node versus portals return; } PCZone * connectedZone; for ( PortalList::iterator it = mPortals.begin(); it != mPortals.end(); ++it ) { Portal * p = *it; //Check if the portal intersects the node if (p != ignorePortal && p->intersects(pczsn) != Portal::NO_INTERSECT) { // node is touching this portal connectedZone = p->getTargetZone(); // add zone to the nodes visiting zone list unless it is the home zone of the node if (connectedZone != pczsn->getHomeZone() && !pczsn->isVisitingZone(connectedZone)) { pczsn->addZoneToVisitingZonesMap(connectedZone); // tell the connected zone that the node is visiting it connectedZone->_addNode(pczsn); //recurse into the connected zone connectedZone->_checkNodeAgainstPortals(pczsn, p->getTargetPortal()); } } } }
bool PCZSceneManager::setOption( const String & key, const void * val ) { if ( key == "ShowBoundingBoxes" ) { mShowBoundingBoxes = * static_cast < const bool * > ( val ); return true; } else if ( key == "ShowPortals" ) { mShowPortals = * static_cast < const bool * > ( val ); return true; } // send option to each zone ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; if (zone->setOption(key, val ) == true) { return true; } } // try regular scenemanager option return SceneManager::setOption( key, val ); }
// delete a portal instance by pointer void PCZSceneManager::destroyPortal(Portal * p) { // remove the portal from it's target portal Portal * targetPortal = p->getTargetPortal(); if (targetPortal) { targetPortal->setTargetPortal(0); // the targetPortal will still have targetZone value, but targetPortal will be invalid } // remove the Portal from it's home zone PCZone * homeZone = p->getCurrentHomeZone(); if (homeZone) { // inform zone of portal change. Do here since PCZone is abstract homeZone->setPortalsUpdated(true); homeZone->_removePortal(p); } // remove the portal from the master portal list PortalList::iterator it = std::find( mPortals.begin(), mPortals.end(), p ); if (it != mPortals.end()) { mPortals.erase(it); } // delete the portal instance OGRE_DELETE p; }
// Create a camera for the scene Camera * PCZSceneManager::createCamera( const String &name ) { // Check name not used if (mCameras.find(name) != mCameras.end()) { OGRE_EXCEPT( Exception::ERR_DUPLICATE_ITEM, "A camera with the name " + name + " already exists", "PCZSceneManager::createCamera" ); } Camera * c = OGRE_NEW PCZCamera( name, this ); mCameras.insert( CameraList::value_type( name, c ) ); // create visible bounds aab map entry mCamVisibleObjectsMap[c] = VisibleObjectsBoundsInfo(); // tell all the zones about the new camera ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; zone->notifyCameraCreated( c ); } return c; }
// main visibility determination & render queue filling routine // over-ridden from base/default scene manager. This is *the* // main call. void PCZSceneManager::_findVisibleObjects(Camera* cam, VisibleObjectsBoundsInfo* visibleBounds, bool onlyShadowCasters) { // clear the render queue getRenderQueue()->clear(); // if we are re-rendering the scene again with the same camera, we can just use the cache. // this helps post processing compositors. unsigned long frameCount = Root::getSingleton().getNextFrameNumber(); if (mLastActiveCamera == cam && mFrameCount == frameCount) { RenderQueue* queue = getRenderQueue(); size_t count = mVisible.size(); for (size_t i = 0; i < count; ++i) { ((PCZSceneNode*)mVisible[i])->_addToRenderQueue( cam, queue, onlyShadowCasters, visibleBounds); } return; } // increment the visibility frame counter mFrameCount = frameCount; mLastActiveCamera = cam; // clear the list of visible nodes mVisible.clear(); // turn off sky enableSky(false); // remove all extra culling planes ((PCZCamera*)cam)->removeAllExtraCullingPlanes(); // update the camera ((PCZCamera*)cam)->update(); // get the home zone of the camera PCZone* cameraHomeZone = ((PCZSceneNode*)(cam->getParentSceneNode()))->getHomeZone(); // walk the zones, starting from the camera home zone, // adding all visible scene nodes to the mVisibles list cameraHomeZone->setLastVisibleFrame(mFrameCount); cameraHomeZone->findVisibleNodes((PCZCamera*)cam, mVisible, getRenderQueue(), visibleBounds, onlyShadowCasters, mDisplayNodes, mShowBoundingBoxes); }
// create any zone-specific data necessary for all zones for the given node void PCZSceneManager::createZoneSpecificNodeData(PCZSceneNode * node) { ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; if (zone->requiresZoneSpecificNodeData()) { zone->createNodeZoneData(node); } } }
//----------------------------------------------------------------------- void PCZLight::updateZones(PCZone * defaultZone, unsigned long frameCount) { //update the zones this light affects PCZone * homeZone; affectedZonesList.clear(); mAffectsVisibleZone = false; PCZSceneNode * sn = (PCZSceneNode*)(this->getParentSceneNode()); if (sn) { // start with the zone the light is in homeZone = sn->getHomeZone(); if (homeZone) { affectedZonesList.push_back(homeZone); if (homeZone->getLastVisibleFrame() == frameCount) { mAffectsVisibleZone = true; } } else { // error - scene node has no homezone! // just say it affects the default zone and leave it at that. affectedZonesList.push_back(defaultZone); if (defaultZone->getLastVisibleFrame() == frameCount) { mAffectsVisibleZone = true; } return; } } else { // ERROR! not connected to a scene node, // just say it affects the default zone and leave it at that. affectedZonesList.push_back(defaultZone); if (defaultZone->getLastVisibleFrame() == frameCount) { mAffectsVisibleZone = true; } return; } // now check visibility of each portal in the home zone. If visible to // the light then add the target zone of the portal to the list of // affected zones and recurse into the target zone static PCZFrustum portalFrustum; Vector3 v = getDerivedPosition(); portalFrustum.setOrigin(v); homeZone->_checkLightAgainstPortals(this, frameCount, &portalFrustum, 0); }
/** Overridden from SceneManager */ void PCZSceneManager::_renderScene(Camera* cam, Viewport *vp, bool includeOverlays) { // notify all the zones that a scene render is starting ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; zone->notifyBeginRenderScene(); } // do the regular _renderScene SceneManager::_renderScene(cam, vp, includeOverlays); }
/** Mark nodes dirty for every zone with moving portal in the scene */ void PCZSceneManager::_dirtyNodeByMovingPortals(void) { PCZone * zone; ZoneMap::iterator zit = mZones.begin(); while ( zit != mZones.end() ) { zone = zit->second; // this call mark nodes dirty base on moving portals zone->dirtyNodeByMovingPortals(); // proceed to next zone in the list ++zit; } }
void PCZSceneManager::_updatePortalZoneData(void) { PCZone * zone; ZoneMap::iterator zit = mZones.begin(); while ( zit != mZones.end() ) { zone = zit->second; // this callchecks for portal zone changes & applies zone data changes as necessary zone->updatePortalsZoneData(); // proceed to next zone in the list ++zit; } }
/** Overridden from SceneManager */ void PCZSceneManager::setWorldGeometryRenderQueue(uint8 qid) { // tell all the zones about the new WorldGeometryRenderQueue ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; zone->notifyWorldGeometryRenderQueue( qid ); } // call the regular scene manager version SceneManager::setWorldGeometryRenderQueue(qid); }
void PCZSceneManager::setZoneGeometry(const String & zoneName, PCZSceneNode * parentNode, const String & filename) { ZoneMap::iterator i; PCZone * zone; i = mZones.find(zoneName); if (i != mZones.end()) { zone = i->second; zone->setZoneGeometry( filename, parentNode ); return; } }
/** Delete a anti portal instance by pointer */ void PCZSceneManager::destroyAntiPortal(AntiPortal * p) { // remove the Portal from it's home zone PCZone* homeZone = p->getCurrentHomeZone(); if (homeZone) { // inform zone of portal change. Do here since PCZone is abstract homeZone->setPortalsUpdated(true); homeZone->_removeAntiPortal(p); } // remove the portal from the master portal list AntiPortalList::iterator it = std::find(mAntiPortals.begin(), mAntiPortals.end(), p); if (it != mAntiPortals.end()) mAntiPortals.erase(it); // delete the portal instance OGRE_DELETE p; }
/** Create a zone from a file (type of file * depends on the zone type * ZoneType_Default uses an Ogre Model (.mesh) file * ZoneType_Octree uses an Ogre Model (.mesh) file * ZoneType_Terrain uses a Terrain.CFG file */ PCZone * PCZSceneManager::createZoneFromFile(const String &zoneTypeName, const String &zoneName, PCZSceneNode * parentNode, const String &filename) { PCZone * newZone; // create a new default zone newZone = mZoneFactoryManager->createPCZone(this, zoneTypeName, zoneName); // add to the global list of zones mZones[newZone->getName()] = newZone; if (filename != "none") { // set the zone geometry newZone->setZoneGeometry(filename, parentNode); } return newZone; }
/* The following function does the following: * 1) Remove references to the node from zones the node is visiting * 2) Clear the node's list of zones it is visiting */ void PCZSceneNode::clearNodeFromVisitedZones( void ) { if (mVisitingZones.size() > 0) { // first go through the list of zones this node is visiting // and remove references to this node PCZone* zone; ZoneMap::iterator it = mVisitingZones.begin(); while ( it != mVisitingZones.end() ) { zone = it->second; zone->removeNode(this); ++it; } // second, clear the visiting zones list mVisitingZones.clear(); } }
/* Create a zone with the given name and parent zone */ PCZone * PCZSceneManager::createZone(const String& zoneType, const String& instanceName) { if (mZones.find(instanceName) != mZones.end()) { OGRE_EXCEPT( Exception::ERR_DUPLICATE_ITEM, "A zone with the name " + instanceName + " already exists", "PCZSceneManager::createZone" ); } PCZone * newZone = mZoneFactoryManager->createPCZone(this, zoneType, instanceName); if (newZone) { // add to the global list of zones mZones[instanceName] = newZone; } if (newZone->requiresZoneSpecificNodeData()) { createZoneSpecificNodeData(newZone); } return newZone; }
void PCZSceneManager::findNodesIn( const Ray &r, PCZSceneNodeList &list, PCZone * startZone, PCZSceneNode *exclude ) { PortalList visitedPortals; if (startZone) { // start in startzone, and recurse through portals if necessary startZone->_findNodes(r, list, visitedPortals, true, true, exclude); } else { ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; zone->_findNodes( r, list, visitedPortals, false, false, exclude ); } } }
// delete a portal instance by pointer void PCZSceneManager::destroyPortal(const String & portalName) { // find the portal from the master portal list Portal * p; Portal * thePortal = 0; PortalList::iterator it = mPortals.begin(); while (it != mPortals.end()) { p = *it; if (p->getName() == portalName) { thePortal = p; // erase entry in the master list mPortals.erase(it); break; } it++; } if (thePortal) { // remove the portal from it's target portal Portal * targetPortal = thePortal->getTargetPortal(); if (targetPortal) { targetPortal->setTargetPortal(0); // the targetPortal will still have targetZone value, but targetPortal will be invalid } // remove the Portal from it's home zone PCZone * homeZone = thePortal->getCurrentHomeZone(); if (homeZone) { // inform zone of portal change homeZone->setPortalsUpdated(true); homeZone->_removePortal(thePortal); } // delete the portal instance OGRE_DELETE thePortal; } }
void PCZSceneManager::findNodesIn( const PlaneBoundedVolume &volume, PCZSceneNodeList &list, PCZone * startZone, PCZSceneNode *exclude ) { PortalList visitedPortals; if (startZone) { // start in startzone, and recurse through portals if necessary startZone->_findNodes(volume, list, visitedPortals, true, true, exclude); } else { // no start zone specified, so check all zones ZoneMap::iterator i; PCZone * zone; for (i = mZones.begin(); i != mZones.end(); i++) { zone = i->second; zone->_findNodes( volume, list, visitedPortals, false, false, exclude ); } } }
// update zone-specific data for any zone that the node is touching void PCZSceneNode::updateZoneData(void) { ZoneData* zoneData; PCZone * zone; // make sure home zone data is updated zone = mHomeZone; if (zone->requiresZoneSpecificNodeData()) { zoneData = getZoneData(zone); zoneData->update(); } // update zone data for any zones visited ZoneMap::iterator it = mVisitingZones.begin(); while ( it != mVisitingZones.end() ) { zone = it->second; if (zone->requiresZoneSpecificNodeData()) { zoneData = getZoneData(zone); zoneData->update(); } ++it; } }
/* The following function checks if a node has left it's current home zone. * This is done by checking each portal in the zone. If the node has crossed * the portal, then the current zone is no longer the home zone of the node. The * function then recurses into the connected zones. Once a zone is found where * the node does NOT cross out through a portal, that zone is the new home zone. * When this function is done, the node should have the correct home zone already * set. A pointer is returned to this zone as well. * * NOTE: If the node does not have a home zone when this function is called on it, * the function will do its best to find the proper zone for the node using * bounding box volume testing. This CAN fail to find the correct zone in * some scenarios, so it is best for the user to EXPLICITLY set the home * zone of the node when the node is added to the scene using * PCZSceneNode::setHomeZone() */ void PCZSceneManager::_updateHomeZone( PCZSceneNode * pczsn, bool allowBackTouches ) { // Skip if root PCZoneTree has been destroyed (shutdown conditions) if (!mDefaultZone) return; PCZone * startzone; PCZone * newHomeZone; // start with current home zone of the node startzone = pczsn->getHomeZone(); if (startzone) { if (!pczsn->isAnchored()) { newHomeZone = startzone->updateNodeHomeZone(pczsn, false); } else { newHomeZone = startzone; } if (newHomeZone != startzone) { // add the node to the home zone newHomeZone->_addNode(pczsn); } } else { // the node hasn't had it's home zone set yet, so do our best to // find the home zone using volume testing. Vector3 nodeCenter = pczsn->_getDerivedPosition(); PCZone * bestZone = findZoneForPoint(nodeCenter); // set the best zone as the node's home zone pczsn->setHomeZone(bestZone); // add the node to the zone bestZone->_addNode(pczsn); } return; }
/** (recursive) check the given light against all portals in the zone * NOTE: This is the default implementation, which doesn't take advantage * of any zone-specific optimizations for checking portal visibility */ void DefaultZone::_checkLightAgainstPortals(PCZLight *light, unsigned long frameCount, PCZFrustum *portalFrustum, Portal * ignorePortal) { for ( PortalList::iterator it = mPortals.begin(); it != mPortals.end(); ++it ) { Portal * p = *it; if (p != ignorePortal) { // calculate the direction vector from light to portal Vector3 lightToPortal = p->getDerivedCP() - light->getDerivedPosition(); if (portalFrustum->isVisible(p)) { // portal is facing the light, but some light types need to // check illumination radius too. PCZone * targetZone = p->getTargetZone(); switch(light->getType()) { case Light::LT_POINT: // point lights - just check if within illumination range if (lightToPortal.length() <= light->getAttenuationRange()) { // if portal is quad portal it must be pointing towards the light if ((p->getType() == Portal::PORTAL_TYPE_QUAD && lightToPortal.dotProduct(p->getDerivedDirection()) < 0.0) || (p->getType() != Portal::PORTAL_TYPE_QUAD)) { if (!light->affectsZone(targetZone)) { light->addZoneToAffectedZonesList(targetZone); if (targetZone->getLastVisibleFrame() == frameCount) { light->setAffectsVisibleZone(true); } // set culling frustum from the portal portalFrustum->addPortalCullingPlanes(p); // recurse into the target zone of the portal p->getTargetZone()->_checkLightAgainstPortals(light, frameCount, portalFrustum, p->getTargetPortal()); // remove the planes added by this portal portalFrustum->removePortalCullingPlanes(p); } } } break; case Light::LT_DIRECTIONAL: // directionals have infinite range, so just make sure // the direction is facing the portal if (lightToPortal.dotProduct(light->getDerivedDirection()) >= 0.0) { // if portal is quad portal it must be pointing towards the light if ((p->getType() == Portal::PORTAL_TYPE_QUAD && lightToPortal.dotProduct(p->getDerivedDirection()) < 0.0) || (p->getType() != Portal::PORTAL_TYPE_QUAD)) { if (!light->affectsZone(targetZone)) { light->addZoneToAffectedZonesList(targetZone); if (targetZone->getLastVisibleFrame() == frameCount) { light->setAffectsVisibleZone(true); } // set culling frustum from the portal portalFrustum->addPortalCullingPlanes(p); // recurse into the target zone of the portal p->getTargetZone()->_checkLightAgainstPortals(light, frameCount, portalFrustum, p->getTargetPortal()); // remove the planes added by this portal portalFrustum->removePortalCullingPlanes(p); } } } break; case Light::LT_SPOTLIGHT: // spotlights - just check if within illumination range // Technically, we should check if the portal is within // the cone of illumination, but for now, we'll leave that // as a future optimisation. if (lightToPortal.length() <= light->getAttenuationRange()) { // if portal is quad portal it must be pointing towards the light if ((p->getType() == Portal::PORTAL_TYPE_QUAD && lightToPortal.dotProduct(p->getDerivedDirection()) < 0.0) || (p->getType() != Portal::PORTAL_TYPE_QUAD)) { if (!light->affectsZone(targetZone)) { light->addZoneToAffectedZonesList(targetZone); if (targetZone->getLastVisibleFrame() == frameCount) { light->setAffectsVisibleZone(true); } // set culling frustum from the portal portalFrustum->addPortalCullingPlanes(p); // recurse into the target zone of the portal p->getTargetZone()->_checkLightAgainstPortals(light, frameCount, portalFrustum, p->getTargetPortal()); // remove the planes added by this portal portalFrustum->removePortalCullingPlanes(p); } } } break; } } } } }