protected virtual void ProcessLeaf( BspNode leaf, Ray tracingRay, float maxDistance, float traceDistance ) { //Check ray against objects foreach ( MovableObject obj in leaf.Objects.Values ) { // Skip this object if collision not enabled if ( ( obj.QueryFlags & queryMask ) == 0 ) { continue; } //Test object as bounding box IntersectResult result = tracingRay.Intersects( obj.GetWorldBoundingBox() ); // if the result came back positive and intersection point is inside // the node, fire the event handler if ( result.Hit && result.Distance <= maxDistance ) { this.listener.OnQueryResult( obj, result.Distance + traceDistance ); } } var boundedVolume = new PlaneBoundedVolume( PlaneSide.Positive ); BspBrush intersectBrush = null; float intersectBrushDist = float.PositiveInfinity; if ( ( QueryTypeMask & (ulong)SceneQueryTypeMask.WorldGeometry ) != 0 ) { // Check ray against brushes if ( ( QueryTypeMask & (ulong)SceneQueryTypeMask.WorldGeometry ) != 0 ) { for ( int brushPoint = 0; brushPoint < leaf.SolidBrushes.Length; brushPoint++ ) { BspBrush brush = leaf.SolidBrushes[ brushPoint ]; if ( brush == null ) { continue; } boundedVolume.planes = brush.Planes; IntersectResult result = tracingRay.Intersects( boundedVolume ); // if the result came back positive and intersection point is inside // the node, check if this brush is closer if ( result.Hit && result.Distance <= maxDistance ) { if ( result.Distance < intersectBrushDist ) { intersectBrushDist = result.Distance; intersectBrush = brush; } } } if ( intersectBrush != null ) { this.listener.OnQueryResult( intersectBrush.Fragment, intersectBrushDist + traceDistance ); this.StopRayTracing = true; } } } if ( intersectBrush != null ) { this.listener.OnQueryResult( intersectBrush.Fragment, intersectBrushDist + traceDistance ); this.StopRayTracing = true; } }
public override void FindNodes( Ray t, ref List<PCZSceneNode> list, List<Portal> visitedPortals, bool includeVisitors, bool recurseThruPortals, PCZSceneNode exclude ) { // if this zone has an enclosure, check against the enclosure AABB first if ( null != mEnclosureNode ) { IntersectResult nsect = t.Intersects( mEnclosureNode.WorldAABB ); if ( !nsect.Hit ) { // AABB of zone does not intersect t, just return. return; } } // check nodes at home in this zone foreach ( PCZSceneNode pczsn in mHomeNodeList ) { if ( pczsn != exclude ) { // make sure node is not already in the list (might have been added in another // zone it was visiting) if ( !list.Contains( pczsn ) ) { IntersectResult nsect = t.Intersects( pczsn.WorldAABB ); if ( nsect.Hit ) { list.Add( pczsn ); } } } } if ( includeVisitors ) { // check visitor nodes foreach ( PCZSceneNode pczsn in mVisitorNodeList ) { if ( pczsn != exclude ) { // make sure node is not already in the list (might have been added in another // zone it was visiting) if ( !list.Contains( pczsn ) ) { IntersectResult nsect = t.Intersects( pczsn.WorldAABB ); if ( nsect.Hit ) { list.Add( pczsn ); } } } } } // if asked to, recurse through portals if ( recurseThruPortals ) { foreach ( Portal portal in mPortals ) { // check portal versus boundign box if ( portal.intersects( t ) ) { // make sure portal hasn't already been recursed through if ( !visitedPortals.Contains( portal ) ) { // save portal to the visitedPortals list visitedPortals.Add( portal ); // recurse into the connected zone portal.getTargetZone().FindNodes( t, ref list, visitedPortals, includeVisitors, recurseThruPortals, exclude ); } } } } }
public override void FindNodes( Ray t, ref List<PCZSceneNode> list, List<Portal> visitedPortals, bool includeVisitors, bool recurseThruPortals, PCZSceneNode exclude ) { // if this zone has an enclosure, check against the enclosure AABB first if ( null != mEnclosureNode ) { IntersectResult nsect = t.Intersects( mEnclosureNode.WorldAABB ); if ( !nsect.Hit ) { // AABB of zone does not intersect t, just return. return; } } // use the Octree to more efficiently find nodes intersecting the ray rootOctree._findNodes( t, ref list, exclude, includeVisitors, false ); // if asked to, recurse through portals if ( recurseThruPortals ) { foreach ( Portal portal in mPortals ) { // check portal versus boundign box if ( portal.intersects( t ) ) { // make sure portal hasn't already been recursed through if ( !visitedPortals.Contains( portal ) ) { // save portal to the visitedPortals list visitedPortals.Add( portal ); // recurse into the connected zone portal.getTargetZone().FindNodes( t, ref list, visitedPortals, includeVisitors, recurseThruPortals, exclude ); } } } } }
protected virtual void ProcessNode( BspNode node, Ray tracingRay, float maxDistance, float traceDistance ) { // check if ray already encountered a solid brush if ( this.StopRayTracing ) { return; } if ( node.IsLeaf ) { ProcessLeaf( node, tracingRay, maxDistance, traceDistance ); return; } IntersectResult result = tracingRay.Intersects( node.SplittingPlane ); if ( result.Hit ) { if ( result.Distance < maxDistance ) { if ( node.GetSide( tracingRay.Origin ) == PlaneSide.Negative ) { ProcessNode( node.BackNode, tracingRay, result.Distance, traceDistance ); Vector3 splitPoint = tracingRay.Origin + tracingRay.Direction*result.Distance; ProcessNode( node.FrontNode, new Ray( splitPoint, tracingRay.Direction ), maxDistance - result.Distance, traceDistance + result.Distance ); } else { ProcessNode( node.FrontNode, tracingRay, result.Distance, traceDistance ); Vector3 splitPoint = tracingRay.Origin + tracingRay.Direction*result.Distance; ProcessNode( node.BackNode, new Ray( splitPoint, tracingRay.Direction ), maxDistance - result.Distance, traceDistance + result.Distance ); } } else { ProcessNode( node.GetNextNode( tracingRay.Origin ), tracingRay, maxDistance, traceDistance ); } } else { ProcessNode( node.GetNextNode( tracingRay.Origin ), tracingRay, maxDistance, traceDistance ); } }
public bool CalculateCurrentLod( Camera cam, Real cFactor ) { this.mSelfOrChildRendered = false; //check children first. int childrenRenderedOut = 0; if ( !IsLeaf ) { for ( int i = 0; i < 4; ++i ) { if ( this.mChildren[ i ].CalculateCurrentLod( cam, cFactor ) ) { ++childrenRenderedOut; } } } if ( childrenRenderedOut == 0 ) { // no children were within their LOD ranges, so we should consider our own Vector3 localPos = cam.DerivedPosition - this.mLocalCentre - this.mTerrain.Position; Real dist; if ( TerrainGlobalOptions.UseRayBoxDistanceCalculation ) { // Get distance to this terrain node (to closest point of the box) // head towards centre of the box (note, box may not cover mLocalCentre because of height) var dir = this.mAABB.Center - localPos; dir.Normalize(); var ray = new Ray( localPos, dir ); var intersectRes = ray.Intersects( this.mAABB ); // ray will always intersect, we just want the distance dist = intersectRes.Distance; } else { // distance to tile centre dist = localPos.Length; // deduct half the radius of the box, assume that on average the // worst case is best approximated by this dist -= ( this.mBoundingRadius*0.5f ); } // Do material LOD var material = Material; LodStrategy str = material.LodStrategy; Real lodValue = str.GetValue( this.mMovable, cam ); // Get the index at this biased depth this.mMaterialLodIndex = (ushort)material.GetLodIndex( lodValue ); // For each LOD, the distance at which the LOD will transition *downwards* // is given by // distTransition = maxDelta * cFactor; int lodLvl = 0; this.mCurrentLod = -1; foreach ( LodLevel i in this.mLodLevels ) { // If we have no parent, and this is the lowest LOD, we always render // this is the 'last resort' so to speak, we always enoucnter this last if ( lodLvl + 1 == this.mLodLevels.Count && this.mParent == null ) { CurrentLod = lodLvl; this.mSelfOrChildRendered = true; this.mLodTransition = 0; } else { //check the distance LodLevel ll = i; // Calculate or reuse transition distance Real distTransition; if ( Utility.RealEqual( cFactor, ll.LastCFactor ) ) { distTransition = ll.LastTransitionDist; } else { distTransition = ll.MaxHeightDelta*cFactor; ll.LastCFactor = cFactor; ll.LastTransitionDist = distTransition; } if ( dist < distTransition ) { // we're within range of this LOD CurrentLod = lodLvl; this.mSelfOrChildRendered = true; if ( this.mTerrain.IsMorphRequired ) { // calculate the transition percentage // we need a percentage of the total distance for just this LOD, // which means taking off the distance for the next higher LOD // which is either the previous entry in the LOD list, // or the largest of any children. In both cases these will // have been calculated before this point, since we process // children first. Distances at lower LODs are guaranteed // to be larger than those at higher LODs Real distTotal = distTransition; if ( IsLeaf ) { // Any higher LODs? if ( !i.Equals( this.mLodLevels[ 0 ] ) ) { int prev = lodLvl - 1; distTotal -= this.mLodLevels[ prev ].LastTransitionDist; } } else { // Take the distance of the lowest LOD of child LodLevel childLod = this.mChildWithMaxHeightDelta.GetLodLevel( (ushort)( this.mChildWithMaxHeightDelta.LodCount - 1 ) ); distTotal -= childLod.LastTransitionDist; } // fade from 0 to 1 in the last 25% of the distance Real distMorphRegion = distTotal*0.25f; Real distRemain = distTransition - dist; this.mLodTransition = 1.0f - ( distRemain/distMorphRegion ); this.mLodTransition = System.Math.Min( 1.0f, this.mLodTransition ); this.mLodTransition = System.Math.Max( 0.0f, this.mLodTransition ); // Pass both the transition % and target LOD (GLOBAL current + 1) // this selectively applies the morph just to the // vertices which would drop out at this LOD, even // while using the single shared vertex data this.mRend.SetCustomParameter( Terrain.LOD_MORPH_CUSTOM_PARAM, new Vector4( this.mLodTransition, this.mCurrentLod + this.mBaseLod + 1, 0, 0 ) ); } //end if // since LODs are ordered from highest to lowest detail, // we can stop looking now break; } //end if } //end else ++lodLvl; } //end for each } //end if else { // we should not render ourself this.mCurrentLod = -1; this.mSelfOrChildRendered = true; if ( childrenRenderedOut < 4 ) { // only *some* children decided to render on their own, but either // none or all need to render, so set the others manually to their lowest for ( int i = 0; i < 4; ++i ) { TerrainQuadTreeNode child = this.mChildren[ i ]; if ( !child.IsSelfOrChildrenRenderedAtCurrentLod ) { child.CurrentLod = child.LodCount - 1; child.LodTransition = 1.0f; } } } //(childRenderedCount < 4) } // (childRenderedCount == 0) return this.mSelfOrChildRendered; }
public void WidenRectByVector( Vector3 vec, Rectangle inRect, Real minHeight, Real maxHeight, ref Rectangle outRect ) { outRect = inRect; var p = new Plane(); switch ( Alignment ) { case Alignment.Align_X_Y: p.Redefine( Vector3.UnitZ, new Vector3( 0, 0, vec.z < 0.0f ? minHeight : maxHeight ) ); break; case Alignment.Align_X_Z: p.Redefine( Vector3.UnitY, new Vector3( 0, vec.y < 0.0f ? minHeight : maxHeight, 0 ) ); break; case Alignment.Align_Y_Z: p.Redefine( Vector3.UnitX, new Vector3( vec.x < 0.0f ? minHeight : maxHeight, 0, 0 ) ); break; } var verticalVal = vec.Dot( p.Normal ); if ( Utility.RealEqual( verticalVal, 0.0f ) ) { return; } var corners = new Vector3[4]; var startHeight = verticalVal < 0.0f ? maxHeight : minHeight; GetPoint( inRect.Left, inRect.Top, startHeight, ref corners[ 0 ] ); GetPoint( inRect.Right - 1, inRect.Top, startHeight, ref corners[ 1 ] ); GetPoint( inRect.Left, inRect.Bottom - 1, startHeight, ref corners[ 2 ] ); GetPoint( inRect.Right - 1, inRect.Bottom - 1, startHeight, ref corners[ 3 ] ); for ( int i = 0; i < 4; ++i ) { var ray = new Ray( corners[ i ] + this.mPos, vec ); var rayHit = ray.Intersects( p ); if ( rayHit.Hit ) { var pt = ray.GetPoint( rayHit.Distance ); // convert back to terrain point var terrainHitPos = Vector3.Zero; GetTerrainPosition( pt, ref terrainHitPos ); // build rectangle which has rounded down & rounded up values // remember right & bottom are exclusive var mergeRect = new Rectangle( (long)terrainHitPos.x*( this.mSize - 1 ), (long)terrainHitPos.y*( this.mSize - 1 ), (long)( terrainHitPos.x*(float)( this.mSize - 1 ) + 0.5f ) + 1, (long)( terrainHitPos.y*(float)( this.mSize - 1 ) + 0.5f ) + 1 ); outRect.Merge( mergeRect ); } } }
protected KeyValuePair<bool, Vector3> CheckQuadIntersection( int x, int z, Ray ray ) { // build the two planes belonging to the quad's triangles Vector3 v1 = new Vector3( x, this.mHeightData[ z + this.mSize*x ], z ), v2 = new Vector3( x + 1, this.mHeightData[ z + this.mSize*( x + 1 ) ], z ), v3 = new Vector3( x, this.mHeightData[ ( z + 1 ) + this.mSize*x ], z + 1 ), v4 = new Vector3( x + 1, this.mHeightData[ ( z + 1 ) + this.mSize*( x + 1 ) ], z + 1 ); Plane p1 = new Plane(), p2 = new Plane(); var oddRow = false; if ( z%2 != 0 ) { /* odd 3---4 | \ | 1---2 */ p1.Redefine( v2, v4, v3 ); p2.Redefine( v1, v2, v3 ); oddRow = true; } else { /* even 3---4 | / | 1---2 */ p1.Redefine( v1, v2, v4 ); p2.Redefine( v1, v4, v3 ); } // Test for intersection with the two planes. // Then test that the intersection points are actually // still inside the triangle (with a small error margin) // Also check which triangle it is in var planeInt = ray.Intersects( p1 ); if ( planeInt.Hit ) { var where = ray.GetPoint( planeInt.Distance ); var rel = where - v1; if ( rel.x >= -0.01 && rel.x <= 1.01 && rel.z >= -0.01 && rel.z <= 1.01 // quad bounds && ( ( rel.x >= rel.z && !oddRow ) || ( rel.x >= ( 1 - rel.z ) && oddRow ) ) ) // triangle bounds { return new KeyValuePair<bool, Vector3>( true, where ); } } planeInt = ray.Intersects( p2 ); if ( planeInt.Hit ) { var where = ray.GetPoint( planeInt.Distance ); var rel = where - v1; if ( rel.x >= -0.01 && rel.x <= 1.01 && rel.z >= -0.01 && rel.z <= 1.01 // quad bounds && ( ( rel.x <= rel.z && !oddRow ) || ( rel.x <= ( 1 - rel.z ) && oddRow ) ) ) // triangle bounds { return new KeyValuePair<bool, Vector3>( true, where ); } } return new KeyValuePair<bool, Vector3>( false, Vector3.Zero ); }
public KeyValuePair<bool, Vector3> RayIntersects( Ray ray, bool cascadeToNeighbours, Real distanceLimit ) { KeyValuePair<bool, Vector3> Result; // first step: convert the ray to a local vertex space // we assume terrain to be in the x-z plane, with the [0,0] vertex // at origin and a plane distance of 1 between vertices. // This makes calculations easier. var rayOrigin = ray.Origin - Position; var rayDirection = ray.Direction; // change alignment Vector3 tmp; switch ( Alignment ) { case Alignment.Align_X_Y: Utility.Swap<Real>( ref rayOrigin.y, ref rayOrigin.z ); Utility.Swap<Real>( ref rayDirection.y, ref rayDirection.z ); break; case Alignment.Align_Y_Z: // x = z, z = y, y = -x tmp.x = rayOrigin.z; tmp.z = rayOrigin.y; tmp.y = -rayOrigin.x; rayOrigin = tmp; tmp.x = rayDirection.z; tmp.z = rayDirection.y; tmp.y = -rayDirection.x; rayDirection = tmp; break; case Alignment.Align_X_Z: // already in X/Z but values increase in -Z rayOrigin.z = -rayOrigin.z; rayDirection.z = -rayDirection.z; break; } // readjust coordinate origin rayOrigin.x += this.mWorldSize/2; rayOrigin.z += this.mWorldSize/2; // scale down to vertex level rayOrigin.x /= this.mScale; rayOrigin.z /= this.mScale; rayDirection.x /= this.mScale; rayDirection.z /= this.mScale; rayDirection.Normalize(); var localRay = new Ray( rayOrigin, rayDirection ); // test if the ray actually hits the terrain's bounds var maxHeight = MaxHeight; var minHeight = MinHeight; var aabb = new AxisAlignedBox( new Vector3( 0, minHeight, 0 ), new Vector3( this.mSize, maxHeight, this.mSize ) ); var aabbTest = localRay.Intersects( aabb ); if ( !aabbTest.Hit ) { if ( cascadeToNeighbours ) { var neighbour = RaySelectNeighbour( ray, distanceLimit ); if ( neighbour != null ) { return neighbour.RayIntersects( ray, cascadeToNeighbours, distanceLimit ); } } return new KeyValuePair<bool, Vector3>( false, new Vector3() ); } // get intersection point and move inside var cur = localRay.GetPoint( aabbTest.Distance ); // now check every quad the ray touches var quadX = Utility.Min( Utility.Max( (int)( cur.x ), 0 ), (int)this.mSize - 2 ); var quadZ = Utility.Min( Utility.Max( (int)( cur.z ), 0 ), (int)this.mSize - 2 ); var flipX = ( rayDirection.x < 0 ? 0 : 1 ); var flipZ = ( rayDirection.z < 0 ? 0 : 1 ); var xDir = ( rayDirection.x < 0 ? -1 : 1 ); var zDir = ( rayDirection.z < 0 ? -1 : 1 ); Result = new KeyValuePair<bool, Vector3>( true, Vector3.Zero ); var dummyHighValue = (Real)this.mSize*10000; while ( cur.y >= ( minHeight - 1e-3 ) && cur.y <= ( maxHeight + 1e-3 ) ) { if ( quadX < 0 || quadX >= (int)this.mSize - 1 || quadZ < 0 || quadZ >= (int)this.mSize - 1 ) { break; } Result = CheckQuadIntersection( quadX, quadZ, localRay ); if ( Result.Key ) { break; } // determine next quad to test var xDist = Utility.RealEqual( rayDirection.x, 0.0f ) ? dummyHighValue : ( quadX - cur.x + flipX )/rayDirection.x; var zDist = Utility.RealEqual( rayDirection.z, 0.0f ) ? dummyHighValue : ( quadZ - cur.z + flipZ )/rayDirection.z; if ( xDist < zDist ) { quadX += xDir; cur += rayDirection*xDist; } else { quadZ += zDir; cur += rayDirection*zDist; } } var resVec = Vector3.Zero; if ( Result.Key ) { // transform the point of intersection back to world space resVec = Result.Value; resVec.x *= this.mScale; resVec.z *= this.mScale; resVec.x -= this.mWorldSize/2; resVec.z -= this.mWorldSize/2; switch ( Alignment ) { case Alignment.Align_X_Y: Utility.Swap<Real>( ref resVec.y, ref resVec.z ); break; case Alignment.Align_Y_Z: // z = x, y = z, x = -y tmp.x = -rayOrigin.y; tmp.y = rayOrigin.z; tmp.z = rayOrigin.x; rayOrigin = tmp; break; case Alignment.Align_X_Z: resVec.z = -resVec.z; break; } resVec += Position; } else if ( cascadeToNeighbours ) { var neighbour = RaySelectNeighbour( ray, distanceLimit ); if ( neighbour != null ) { Result = neighbour.RayIntersects( ray, cascadeToNeighbours, distanceLimit ); } } return new KeyValuePair<bool, Vector3>( Result.Key, resVec ); }
// Check if a portal intersects a ray // NOTE: Kinda using my own invented routine here for quad portals... Better do a lot of testing! public bool intersects( Ray ray ) { // Only check if portal is open if ( mOpen ) { if ( mType == PORTAL_TYPE.PORTAL_TYPE_QUAD ) { // since ogre doesn't have built in support for a quad, I'm going to first // find the intersection point (if any) of the ray and the portal plane. Then // using the intersection point, I take the cross product of each side of the portal // (0,1,intersect), (1,2, intersect), (2,3, intersect), and (3,0,intersect). If // all 4 cross products have vectors pointing in the same direction, then the // intersection point is within the portal, otherwise it is outside. IntersectResult result = ray.Intersects( mDerivedPlane ); if ( result.Hit ) { // the ray intersects the plane, now walk around the edges Vector3 isect = ray.GetPoint( result.Distance ); Vector3 cross, vect1, vect2; Vector3 cross2, vect3, vect4; vect1 = mDerivedCorners[ 1 ] - mDerivedCorners[ 0 ]; vect2 = isect - mDerivedCorners[ 0 ]; cross = vect1.Cross( vect2 ); vect3 = mDerivedCorners[ 2 ] - mDerivedCorners[ 1 ]; vect4 = isect - mDerivedCorners[ 1 ]; cross2 = vect3.Cross( vect4 ); if ( cross.Dot( cross2 ) < 0 ) { return false; } vect1 = mDerivedCorners[ 3 ] - mDerivedCorners[ 2 ]; vect2 = isect - mDerivedCorners[ 2 ]; cross = vect1.Cross( vect2 ); if ( cross.Dot( cross2 ) < 0 ) { return false; } vect1 = mDerivedCorners[ 0 ] - mDerivedCorners[ 3 ]; vect2 = isect - mDerivedCorners[ 3 ]; cross = vect1.Cross( vect2 ); if ( cross.Dot( cross2 ) < 0 ) { return false; } // all cross products pointing same way, so intersect // must be on the inside of the portal! return true; } return false; } else if ( mType == PORTAL_TYPE.PORTAL_TYPE_AABB ) { AxisAlignedBox aabb = new AxisAlignedBox( mDerivedCorners[ 0 ], mDerivedCorners[ 1 ] ); IntersectResult result = ray.Intersects( aabb ); return result.Hit; } else // sphere { IntersectResult result = ray.Intersects( mDerivedSphere ); return result.Hit; } } return false; }