/** Indicates whether or not this zone requires zone-specific data for * each scene node */ /* Add a portal to the zone */ public override void AddPortal( Portal newPortal ) { if ( null != newPortal ) { // make sure portal is unique (at least in this zone) if ( mPortals.Contains( newPortal ) ) { throw new AxiomException( "A portal with the name " + newPortal.getName() + "already exists. DefaultZone._addPortal" ); } // add portal to portals list mPortals.Add( newPortal ); // tell the portal which zone it's currently in newPortal.setCurrentHomeZone( this ); } }
/* Recursively check for intersection of the given scene node * with zone portals. If the node touches a portal, then the * connected zone is assumed to be touched. The zone adds * the node to its node list and the node adds the zone to * its visiting zone list. * * NOTE: This function assumes that the home zone of the node * is correct. The function "_updateHomeZone" in PCZSceneManager * takes care of this and should have been called before * this function. */ public override void CheckNodeAgainstPortals( PCZSceneNode pczsn, Portal ignorePortal ) { if ( pczsn == mEnclosureNode || pczsn.AllowToVisit == false ) { // don't do any checking of enclosure node versus portals return; } PCZone connectedZone; foreach ( Portal portal in mPortals ) { if ( portal != ignorePortal && portal.intersects( pczsn ) != PortalIntersectResult.NO_INTERSECT ) { connectedZone = portal.getTargetZone(); if ( connectedZone != pczsn.HomeZone && !pczsn.IsVisitingZone( connectedZone ) ) { pczsn.AddZoneToVisitingZonesMap( connectedZone ); connectedZone.AddNode( pczsn ); connectedZone.CheckNodeAgainstPortals( pczsn, portal.getTargetPortal() ); } } } }
/* Remove a portal from the zone */ public abstract void RemovePortal( Portal portal );
/** (recursive) check the given light against all portals in the zone */ public abstract void CheckLightAgainstPortals( PCZLight light, ulong frameCount, PCZFrustum portalFrustum, Portal ignorePortal );
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <param name="culledBy"> /// The <see cref="FrustumPlane"/> that the Portal is in. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible( Portal portal, out FrustumPlane culledBy ) { culledBy = FrustumPlane.None; // if portal isn't open, it's not visible if ( !portal.IsOpen ) { return false; } // check the extra frustum first if ( !this.extraCullingFrustum.IsObjectVisible( portal ) ) { return false; } // if portal is of type AABB or Sphere, then use simple bound check against planes if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB ) { var aabb = new AxisAlignedBox( portal.getDerivedCorner( 0 ), portal.getDerivedCorner( 1 ) ); return base.IsObjectVisible( aabb, out culledBy ); } else if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE ) { return base.IsObjectVisible( portal.getDerivedSphere(), out culledBy ); } // check if the portal norm is facing the camera Vector3 cameraToPortal = portal.getDerivedCP() - DerivedPosition; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = cameraToPortal.Dot( portalDirection ); if ( dotProduct > 0 ) { // portal is faced away from camera return false; } // check against regular frustum planes bool visible_flag; if ( null != CullFrustum ) { // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. for ( int plane = 1; plane < 6; ++plane ) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ( (FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0 ) { continue; } // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = CullFrustum.FrustumPlanes[ plane ].GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view if ( culledBy != FrustumPlane.None ) { culledBy = (FrustumPlane)plane; } return false; } } } else { // Make any pending updates to the calculated frustum planes UpdateFrustumPlanes(); // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. // BUGBUG: This can cause a false positive situation when a portal is // behind the camera but close. This could be fixed by having another // culling plane at the camera location with normal same as camera direction. for ( int plane = 1; plane < 6; ++plane ) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ( (FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0 ) { continue; } // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = _planes[ plane ].GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view if ( culledBy != FrustumPlane.None ) { culledBy = (FrustumPlane)plane; } return false; } } } // no plane culled all the portal points and the norm // was facing the camera, so this portal is visible return true; }
// remove extra culling planes created from the given portal // NOTE: This should only be used during visibility traversal (backing out of a recursion) public void RemovePortalCullingPlanes( Portal portal ) { this.extraCullingFrustum.RemovePortalCullingPlanes( portal ); }
// Create a portal instance public Portal CreatePortal( String name, PORTAL_TYPE type ) { var newPortal = new Portal( name, type ); this.portals.Add( newPortal ); return newPortal; }
// calculate culling planes from portal and frustum // origin and add to list of culling planes // NOTE: returns 0 if portal was completely culled by existing planes // returns > 0 if culling planes are added (# is planes added) public int AddPortalCullingPlanes( Portal portal ) { int addedcullingplanes = 0; // If portal is of type aabb or sphere, add a plane which is same as frustum // origin plane (ie. redundant). We do this because we need the plane as a flag // to prevent infinite recursion if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB || portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE ) { PCPlane newPlane = GetUnusedCullingPlane(); newPlane.SetFromAxiomPlane( mOriginPlane ); newPlane.Portal = portal; mActiveCullingPlanes.Add( newPlane ); addedcullingplanes++; return addedcullingplanes; } // For portal Quads: Up to 4 planes can be added by the sides of a portal quad. // Each plane is created from 2 corners (world space) of the portal and the // frustum origin (world space). int i, j; PlaneSide pt0_side, pt1_side; bool visible; for ( i = 0; i < 4; i++ ) { // first check if both corners are outside of one of the existing planes j = i + 1; if ( j > 3 ) { j = 0; } visible = true; foreach ( PCPlane plane in mActiveCullingPlanes ) { pt0_side = plane.GetSide( portal.getDerivedCorner( i ) ); pt1_side = plane.GetSide( portal.getDerivedCorner( j ) ); if ( pt0_side == PlaneSide.Negative && pt1_side == PlaneSide.Negative ) { // the portal edge was actually completely culled by one of culling planes visible = false; } } if ( visible ) { // add the plane created from the two portal corner points and the frustum location // to the culling plane PCPlane newPlane = GetUnusedCullingPlane(); if ( projType == Projection.Orthographic ) // use camera direction if projection is orthographic. { newPlane.Redefine( portal.getDerivedCorner( j ) + mOriginPlane.Normal, portal.getDerivedCorner( j ), portal.getDerivedCorner( i ) ); } else { newPlane.Redefine( mOrigin, portal.getDerivedCorner( j ), portal.getDerivedCorner( i ) ); } newPlane.Portal = portal; mActiveCullingPlanes.Add( newPlane ); addedcullingplanes++; } } // if we added ANY planes from the quad portal, we should add the plane of the // portal itself as an additional culling plane. if ( addedcullingplanes > 0 ) { PCPlane newPlane = GetUnusedCullingPlane(); newPlane.Redefine( portal.getDerivedCorner( 2 ), portal.getDerivedCorner( 1 ), portal.getDerivedCorner( 0 ) ); newPlane.Portal = portal; mActiveCullingPlanes.Add( newPlane ); addedcullingplanes++; } return addedcullingplanes; }
/* This function check if *this* portal "crossed over" the other portal. */ public bool crossedPortal( Portal otherPortal ) { // Only check if portal is open if ( otherPortal.mOpen ) { // we model both portals as line swept spheres (mPrevDerivedCP to mDerivedCP). // intersection test is then between the capsules. // BUGBUG! This routine needs to check for case where one or both objects // don't move - resulting in simple sphere tests // BUGBUG! If one (or both) portals are aabb's this is REALLY not accurate. Capsule portalCapsule, otherPortalCapsule; portalCapsule = new Capsule(); portalCapsule.Set( this.getPrevDerivedCP(), this.getDerivedCP(), this.getRadius() ); otherPortalCapsule = new Capsule(); otherPortalCapsule.Set( otherPortal.mPrevDerivedCP, otherPortal.mDerivedCP, otherPortal.mRadius ); if ( portalCapsule.Intersects( otherPortalCapsule ) ) { // the portal intersected the other portal at some time from last frame to this frame. // Now check if this portal "crossed" the other portal switch ( otherPortal.Type ) { case PORTAL_TYPE.PORTAL_TYPE_QUAD: // a crossing occurs if the "side" of the final position of this portal compared // to the final position of the other portal is negative AND the initial position // of this portal compared to the initial position of the other portal is non-negative // NOTE: This function assumes that this portal is the smaller portal potentially crossing // over the otherPortal which is larger. if ( otherPortal.getDerivedPlane().GetSide( mDerivedCP ) == PlaneSide.Negative && otherPortal.getPrevDerivedPlane().GetSide( mPrevDerivedCP ) != PlaneSide.Negative ) { // crossing occurred! return true; } break; case PORTAL_TYPE.PORTAL_TYPE_AABB: { // for aabb's we check if the center point went from being inside to being outside // the aabb (or vice versa) for crossing. AxisAlignedBox aabb = new AxisAlignedBox( otherPortal.getDerivedCorner( 0 ), otherPortal.getDerivedCorner( 1 ) ); //bool previousInside = aabb.contains(mPrevDerivedCP); bool currentInside = aabb.Contains( mDerivedCP ); if ( otherPortal.getDerivedDirection() == Vector3.UnitZ ) { // portal norm is "outward" pointing, look for going from outside to inside //if (previousInside == false && if ( currentInside == true ) { return true; } } else { // portal norm is "inward" pointing, look for going from inside to outside //if (previousInside == true && if ( currentInside == false ) { return true; } } } break; case PORTAL_TYPE.PORTAL_TYPE_SPHERE: { // for spheres we check if the center point went from being inside to being outside // the sphere surface (or vice versa) for crossing. //Real previousDistance2 = mPrevDerivedCP.squaredDistance(otherPortal->getPrevDerivedCP()); Real currentDistance2 = mDerivedCP.DistanceSquared( otherPortal.getDerivedCP() ); Real mRadius2 = System.Math.Sqrt( otherPortal.getRadius() ); if ( otherPortal.getDerivedDirection() == Vector3.UnitZ ) { // portal norm is "outward" pointing, look for going from outside to inside //if (previousDistance2 >= mRadius2 && if ( currentDistance2 < mRadius2 ) { return true; } } else { // portal norm is "inward" pointing, look for going from inside to outside //if (previousDistance2 < mRadius2 && if ( currentDistance2 >= mRadius2 ) { return true; } } } break; } } } // there was no crossing of the portal by this portal. It might be touching // the other portal (but we don't care currently) return false; }
public PCPlane() { mPortal = null; }
// Set the Portal the Portal connects to public void setTargetPortal( Portal p ) { mTargetPortal = p; }
public Portal( string name, PORTAL_TYPE type ) { mType = type; mName = name; mTargetZone = null; mCurrentHomeZone = null; mNewHomeZone = null; mTargetPortal = null; mNode = null; mRadius = 0.0; mDirection = Math.Vector3.UnitZ; mLocalsUpToDate = false; mDerivedSphere = new Sphere(); mDerivedPlane = new Plane(); // set prevWorldTransform to a zero'd out matrix prevWorldTransform = Math.Matrix4.Zero; // default to open mOpen = true; switch ( mType ) { default: case PORTAL_TYPE.PORTAL_TYPE_QUAD: mCorners = new Vector3[ 4 ]; mDerivedCorners = new Vector3[ 4 ]; break; case PORTAL_TYPE.PORTAL_TYPE_AABB: mCorners = new Vector3[ 2 ]; mDerivedCorners = new Vector3[ 2 ]; break; case PORTAL_TYPE.PORTAL_TYPE_SPHERE: mCorners = new Vector3[ 2 ]; mDerivedCorners = new Vector3[ 2 ]; break; } }
// check if portal is close to another portal. // Note, both portals are assumed to be stationary // and DerivedCP is the current position. // this function is INTENTIONALLY NOT EXACT because // it is used by PCZSM::connectPortalsToTargetZonesByLocation // which is a utility function to link up nearby portals // public bool closeTo( Portal otherPortal ) { // only portals of the same type can be "close to" each other. if ( mType != otherPortal.Type ) { return false; } bool close = false; switch ( mType ) { default: case PORTAL_TYPE.PORTAL_TYPE_QUAD: { // quad portals must be within 1/4 sphere of each other Sphere quarterSphere1 = mDerivedSphere; quarterSphere1.Radius = quarterSphere1.Radius * 0.25f; Sphere quarterSphere2 = otherPortal.getDerivedSphere(); quarterSphere2.Radius = quarterSphere2.Radius * 0.25f; close = quarterSphere1.Intersects( quarterSphere2 ); } break; case PORTAL_TYPE.PORTAL_TYPE_AABB: // NOTE: AABB's must match perfectly if ( mDerivedCP == otherPortal.getDerivedCP() && mCorners[ 0 ] == otherPortal.getCorner( 0 ) && mCorners[ 1 ] == otherPortal.getCorner( 1 ) ) { close = true; } break; case PORTAL_TYPE.PORTAL_TYPE_SPHERE: // NOTE: Spheres must match perfectly if ( mDerivedCP == otherPortal.getDerivedCP() && mRadius == otherPortal.getRadius() ) { close = true; } break; } return close; }
// Set the Portal the Portal connects to public void setTargetPortal( Portal p ) { this.mTargetPortal = p; }
// delete a portal instance by pointer public void DestroyPortal( Portal p ) { // remove the portal from it's target portal Portal targetPortal = p.getTargetPortal(); if ( null != targetPortal ) { targetPortal.setTargetPortal( null ); // 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 ( null != homeZone ) { // inform zone of portal change. Do here since PCZone is abstract homeZone.PortalsUpdated = true; homeZone.RemovePortal( p ); } // remove the portal from the master portal list this.portals.Remove( p ); }
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible( Portal portal ) { // if portal isn't open, it's not visible if ( !portal.IsOpen ) { return false; } // if the frustum has no planes, just return true if ( mActiveCullingPlanes.Count == 0 ) { return true; } // check if this portal is already in the list of active culling planes (avoid // infinite recursion case) foreach ( PCPlane plane in mActiveCullingPlanes ) { if ( plane.Portal == portal ) { return false; } } // if portal is of type AABB or Sphere, then use simple bound check against planes if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB ) { AxisAlignedBox aabb = new AxisAlignedBox(); aabb.SetExtents( portal.getDerivedCorner( 0 ), portal.getDerivedCorner( 1 ) ); return IsObjectVisible( aabb ); } else if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE ) { return IsObjectVisible( portal.getDerivedSphere() ); } // check if the portal norm is facing the frustum Vector3 frustumToPortal = portal.getDerivedCP() - mOrigin; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = frustumToPortal.Dot( portalDirection ); if ( dotProduct > 0 ) { // portal is faced away from Frustum return false; } // check against frustum culling planes bool visible_flag; // Check originPlane if told to if ( mUseOriginPlane ) { // set the visible flag to false visible_flag = false; // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = mOriginPlane.GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then the origin plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view return false; } } // For each active culling plane, see if all portal points are on the negative // side. If so, the portal is not visible foreach ( PCPlane plane in mActiveCullingPlanes ) { visible_flag = false; // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = plane.GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view return false; } } // no plane culled all the portal points and the norm // was facing the frustum, so this portal is visible return true; }
public void SetFromAxiomPlane( Plane axiomPlane ) { this.plane = new Plane( this.plane ); this.mPortal = null; }
// remove culling planes created from the given portal public void RemovePortalCullingPlanes( Portal portal ) { for ( int i = 0; i < mActiveCullingPlanes.Count; i++ ) { PCPlane plane = mActiveCullingPlanes[ i ]; if ( plane.Portal == portal ) { mCullingPlaneReservoir.Add( plane ); mActiveCullingPlanes.Remove( plane ); } } }
public PCPlane() { this.mPortal = null; }
// calculate extra culling planes from portal and camera // origin and add to list of extra culling planes // NOTE: returns 0 if portal was completely culled by existing planes // returns > 0 if culling planes are added (# is planes added) public int AddPortalCullingPlanes( Portal portal ) { // add the extra culling planes from the portal return this.extraCullingFrustum.AddPortalCullingPlanes( portal ); }
public PCPlane( Plane plane ) { this.plane = new Plane( plane ); this.mPortal = null; }
/* Add a portal to the zone */ public abstract void AddPortal( Portal portal );
public PCPlane( Vector3 rkNormal, Vector3 rkPoint ) { this.plane = new Plane( rkNormal, rkPoint ); this.mPortal = null; }
/** (recursive) check the given node against all portals in the zone */ public abstract void CheckNodeAgainstPortals( PCZSceneNode pczsn, Portal ignorePortal );
public PCPlane( Vector3 rkPoint0, Vector3 rkPoint1, Vector3 rkPoint2 ) { this.plane = new Plane( rkPoint0, rkPoint1, rkPoint2 ); this.mPortal = null; }
public virtual Portal FindMatchingPortal( Portal portal ) { // look through all the portals in zone2 for a match foreach ( Portal portal2 in this.mPortals ) { //portal2 = pi2; //portal2->updateDerivedValues(); if ( portal2.getTargetZone() == null && portal2.closeTo( portal ) && portal2.getDerivedDirection().Dot( portal.getDerivedDirection() ) < -0.9 ) { // found a match! return portal2; } } return null; }
public override void RemovePortal( Portal portal ) { if ( null != portal ) { mPortals.Remove( portal ); } }
/* Remove a portal from the zone (does not erase the portal object, just removes reference) */ public override void RemovePortal( Portal removePortal ) { if ( null != removePortal && mPortals.Contains( removePortal ) ) { mPortals.Remove( removePortal ); } }
public override void CheckNodeAgainstPortals( PCZSceneNode pczsn, Portal ignorePortal ) { if ( pczsn == mEnclosureNode || pczsn.AllowToVisit == false ) { // don't do any checking of enclosure node versus portals return; } PCZone connectedZone; foreach ( Portal p in mPortals ) { //Check if the portal intersects the node if ( p != ignorePortal && p.intersects( pczsn ) != PortalIntersectResult.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.HomeZone && !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() ); } } } }
/** (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 */ public override void CheckLightAgainstPortals( PCZLight light, ulong frameCount, PCZFrustum portalFrustum, Portal ignorePortal ) { foreach ( Portal p in mPortals ) { //Portal * p = *it; if ( p != ignorePortal ) { // calculate the direction vector from light to portal Vector3 lightToPortal = p.getDerivedCP() - light.GetDerivedPosition(); if ( portalFrustum.IsObjectVisible( p ) ) { // portal is facing the light, but some light types need to // check illumination radius too. PCZone targetZone = p.getTargetZone(); switch ( light.Type ) { case LightType.Point: // point lights - just check if within illumination range if ( lightToPortal.Length <= light.AttenuationRange ) { // if portal is quad portal it must be pointing towards the light if ( ( p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD && lightToPortal.Dot( p.getDerivedDirection() ) < 0.0 ) || ( p.Type != PORTAL_TYPE.PORTAL_TYPE_QUAD ) ) { if ( !light.AffectsZone( targetZone ) ) { light.AddZoneToAffectedZonesList( targetZone ); if ( targetZone.LastVisibleFrame == frameCount ) { light.AffectsVisibleZone = 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 LightType.Directional: // directionals have infinite range, so just make sure // the direction is facing the portal if ( lightToPortal.Dot( light.DerivedDirection ) >= 0.0 ) { // if portal is quad portal it must be pointing towards the light if ( ( p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD && lightToPortal.Dot( p.getDerivedDirection() ) < 0.0 ) || ( p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD ) ) { if ( !light.AffectsZone( targetZone ) ) { light.AddZoneToAffectedZonesList( targetZone ); if ( targetZone.LastVisibleFrame == frameCount ) { light.AffectsVisibleZone = 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 LightType.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.AttenuationRange ) { // if portal is quad portal it must be pointing towards the light if ( ( p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD && lightToPortal.Dot( p.getDerivedDirection() ) < 0.0 ) || ( p.Type != PORTAL_TYPE.PORTAL_TYPE_QUAD ) ) { if ( !light.AffectsZone( targetZone ) ) { light.AddZoneToAffectedZonesList( targetZone ); if ( targetZone.LastVisibleFrame == frameCount ) { light.AffectsVisibleZone = 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; } } } } }
/** (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 */ public override void CheckLightAgainstPortals(PCZLight light, ulong frameCount, PCZFrustum portalFrustum, Portal ignorePortal) { foreach (Portal p in mPortals) { //Portal * p = *it; if (p != ignorePortal) { // calculate the direction vector from light to portal Vector3 lightToPortal = p.getDerivedCP() - light.GetDerivedPosition(); if (portalFrustum.IsObjectVisible(p)) { // portal is facing the light, but some light types need to // check illumination radius too. PCZone targetZone = p.getTargetZone(); switch (light.Type) { case LightType.Point: // point lights - just check if within illumination range if (lightToPortal.Length <= light.AttenuationRange) { // if portal is quad portal it must be pointing towards the light if ((p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD && lightToPortal.Dot(p.getDerivedDirection()) < 0.0) || (p.Type != PORTAL_TYPE.PORTAL_TYPE_QUAD)) { if (!light.AffectsZone(targetZone)) { light.AddZoneToAffectedZonesList(targetZone); if (targetZone.LastVisibleFrame == frameCount) { light.AffectsVisibleZone = 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 LightType.Directional: // directionals have infinite range, so just make sure // the direction is facing the portal if (lightToPortal.Dot(light.DerivedDirection) >= 0.0) { // if portal is quad portal it must be pointing towards the light if ((p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD && lightToPortal.Dot(p.getDerivedDirection()) < 0.0) || (p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD)) { if (!light.AffectsZone(targetZone)) { light.AddZoneToAffectedZonesList(targetZone); if (targetZone.LastVisibleFrame == frameCount) { light.AffectsVisibleZone = 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 LightType.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.AttenuationRange) { // if portal is quad portal it must be pointing towards the light if ((p.Type == PORTAL_TYPE.PORTAL_TYPE_QUAD && lightToPortal.Dot(p.getDerivedDirection()) < 0.0) || (p.Type != PORTAL_TYPE.PORTAL_TYPE_QUAD)) { if (!light.AffectsZone(targetZone)) { light.AddZoneToAffectedZonesList(targetZone); if (targetZone.LastVisibleFrame == frameCount) { light.AffectsVisibleZone = 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; } } } } }