public bool ClosestRayIntersection( Ray ray, Matrix4 transform, float ignoreDistance, out Vector3 intersection ) { var intersects = false; intersection = Vector3.Zero; var minDistSquared = float.MaxValue; var ignoreDistanceSquared = ignoreDistance*ignoreDistance; var where = Vector3.Zero; // Iterate over the triangles for ( var i = 0; i < this.triangles.Count; i++ ) { var t = this.triangles[ i ]; if ( RayIntersectsTriangle( ray, t.Vertices, ref transform, ref where ) ) { float distSquared = ( where - ray.Origin ).LengthSquared; if ( distSquared > ignoreDistanceSquared && distSquared < minDistSquared ) { minDistSquared = distSquared; intersection = where; intersects = true; } } } return intersects; }
public abstract void FindNodes( Ray t, ref List<PCZSceneNode> list, List<Portal> visitedPortals, bool includeVisitors, bool recurseThruPortals, PCZSceneNode exclude );
/// <summary> /// Creates a query to return objects found along the ray. /// </summary> /// <param name="ray">Ray to use for the intersection query.</param> /// <returns>A specialized implementation of RaySceneQuery for this scene manager.</returns> public virtual RaySceneQuery CreateRayQuery( Ray ray ) { return this.CreateRayQuery( ray, 0xffffffff ); }
public void GetCameraToViewportRay( float screenX, float screenY, out Ray ray ) { var inverseVP = ( _projectionMatrix*_viewMatrix ).Inverse(); #if !AXIOM_NO_VIEWPORT_ORIENTATIONMODE // We need to convert screen point to our oriented viewport (temp solution) Real tX = screenX; Real a = (int)OrientationMode*System.Math.PI*0.5f; screenX = System.Math.Cos( a )*( tX - 0.5f ) + System.Math.Sin( a )*( screenY - 0.5f ) + 0.5f; screenY = System.Math.Sin( a )*( tX - 0.5f ) + System.Math.Cos( a )*( screenY - 0.5f ) + 0.5f; if ( ( ( (int)OrientationMode ) & 1 ) == 1 ) { screenY = 1.0f - screenY; } #endif Real nx = ( 2.0f*screenX ) - 1.0f; Real ny = 1.0f - ( 2.0f*screenY ); var nearPoint = new Vector3( nx, ny, -1.0f ); // Use midPoint rather than far point to avoid issues with infinite projection var midPoint = new Vector3( nx, ny, 0.0f ); // Get ray origin and ray target on near plane in world space var rayOrigin = inverseVP*nearPoint; var rayTarget = inverseVP*midPoint; var rayDirection = rayTarget - rayOrigin; rayDirection.Normalize(); ray = new Ray( rayOrigin, rayDirection ); }
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; } } // 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 ); } } } } }
private static Intersection intersect( Ray one, AxisAlignedBox two ) { // Null box? if ( two.IsNull ) { return Intersection.OUTSIDE; } // Infinite box? if ( two.IsInfinite ) { return Intersection.INTERSECT; } bool inside = true; Vector3 twoMin = two.Minimum; Vector3 twoMax = two.Maximum; Vector3 origin = one.Origin; Vector3 dir = one.Direction; var maxT = new Vector3( -1, -1, -1 ); int i = 0; for ( i = 0; i < 3; i++ ) { if ( origin[ i ] < twoMin[ i ] ) { inside = false; if ( dir[ i ] > 0 ) { maxT[ i ] = ( twoMin[ i ] - origin[ i ] )/dir[ i ]; } } else if ( origin[ i ] > twoMax[ i ] ) { inside = false; if ( dir[ i ] < 0 ) { maxT[ i ] = ( twoMax[ i ] - origin[ i ] )/dir[ i ]; } } } if ( inside ) { return Intersection.INTERSECT; } int whichPlane = 0; if ( maxT[ 1 ] > maxT[ whichPlane ] ) { whichPlane = 1; } if ( maxT[ 2 ] > maxT[ whichPlane ] ) { whichPlane = 2; } if ( ( ( (int)maxT[ whichPlane ] ) & 0x80000000 ) != 0 ) { return Intersection.OUTSIDE; } for ( i = 0; i < 3; i++ ) { if ( i != whichPlane ) { float f = origin[ i ] + maxT[ whichPlane ]*dir[ i ]; if ( f < ( twoMin[ i ] - 0.00001f ) || f > ( twoMax[ i ] + 0.00001f ) ) { return Intersection.OUTSIDE; } } } return Intersection.INTERSECT; }
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 ); } } }
public PixelBox CalculateLightMap( Rectangle rect, Rectangle extraTargetRect, ref Rectangle outFinalRect ) { // as well as calculating the lighting changes for the area that is // dirty, we also need to calculate the effect on casting shadow on // other areas. To do this, we project the dirt rect by the light direction // onto the minimum height var lightVec = TerrainGlobalOptions.LightMapDirection; var widenedRect = new Rectangle(); WidenRectByVector( lightVec, rect, ref widenedRect ); //merge in the extra area (e.g. from neighbours) widenedRect.Merge( extraTargetRect ); // widenedRect now contains terrain point space version of the area we // need to calculate. However, we need to calculate in lightmap image space var terrainToLightmapScale = (float)this.mLightmapSizeActual/(float)this.mSize; widenedRect.Left = (long)( widenedRect.Left*terrainToLightmapScale ); widenedRect.Right = (long)( widenedRect.Right*terrainToLightmapScale ); widenedRect.Top = (long)( widenedRect.Top*terrainToLightmapScale ); widenedRect.Bottom = (long)( widenedRect.Bottom*terrainToLightmapScale ); //clamp widenedRect.Left = Utility.Max( 0L, widenedRect.Left ); widenedRect.Top = Utility.Max( 0L, widenedRect.Top ); widenedRect.Right = Utility.Min( (long)this.mLightmapSizeActual, widenedRect.Right ); widenedRect.Bottom = Utility.Min( (long)this.mLightmapSizeActual, widenedRect.Bottom ); outFinalRect = widenedRect; // allocate memory (L8) var pData = new byte[widenedRect.Width*widenedRect.Height]; var pDataPtr = BufferBase.Wrap( pData ); var pixbox = new PixelBox( (int)widenedRect.Width, (int)widenedRect.Height, 1, PixelFormat.L8, pDataPtr ); var heightPad = ( MaxHeight - MinHeight )*1.0e-3f; for ( var y = widenedRect.Top; y < widenedRect.Bottom; ++y ) { for ( var x = widenedRect.Left; x < widenedRect.Right; ++x ) { var litVal = 1.0f; // convert to terrain space (not points, allow this to go between points) var Tx = (float)x/(float)( this.mLightmapSizeActual - 1 ); var Ty = (float)y/(float)( this.mLightmapSizeActual - 1 ); // get world space point // add a little height padding to stop shadowing self var wpos = Vector3.Zero; GetPosition( Tx, Ty, GetHeightAtTerrainPosition( Tx, Ty ) + heightPad, ref wpos ); wpos += Position; // build ray, cast backwards along light direction var ray = new Ray( wpos, -lightVec ); // Cascade into neighbours when casting, but don't travel further // than world size var rayHit = RayIntersects( ray, true, this.mWorldSize ); if ( rayHit.Key ) { litVal = 0.0f; } // encode as L8 // invert the Y to deal with image space var storeX = x - widenedRect.Left; var storeY = widenedRect.Bottom - y - 1; using ( var wrap = pDataPtr + ( ( storeY*widenedRect.Width ) + storeX ) ) { var pStore = (ITypePointer<byte>)wrap; pStore[ 0 ] = (byte)( litVal*255.0 ); } } } pDataPtr.Dispose(); return pixbox; }
/// <see cref="Terrain.RayIntersects(Ray, bool, Real)"/> public KeyValuePair<bool, Vector3> RayIntersects( Ray ray, bool cascadeToNeighbours ) { return RayIntersects( ray, cascadeToNeighbours, 0 ); }
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 ); }
/// <see cref="Terrain.RayIntersects(Ray, bool, Real)"/> public KeyValuePair<bool, Vector3> RayIntersects( Ray ray ) { return RayIntersects( ray, false, 0 ); }
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 ); }
// Given line pq and ccw triangle abc, return whether line pierces triangle. If // so, also return the barycentric coordinates (u,v,w) of the intersection point protected bool RayIntersectsTriangle( Ray ray, Vector3[] Vertices, ref Matrix4 transform, ref Vector3 where ) { // Place the end beyond any conceivable triangle, 1000 meters away var a = transform*Vertices[ 0 ]; var b = transform*Vertices[ 1 ]; var c = transform*Vertices[ 2 ]; var start = ray.Origin; var end = start + ray.Direction*1000000f; var pq = end - start; var pa = a - start; var pb = b - start; var pc = c - start; // Test if pq is inside the edges bc, ca and ab. Done by testing // that the signed tetrahedral volumes, computed using scalar triple // products, are all positive float u = pq.Cross( pc ).Dot( pb ); if ( u < 0.0f ) { return false; } float v = pq.Cross( pa ).Dot( pc ); if ( v < 0.0f ) { return false; } float w = pq.Cross( pb ).Dot( pa ); if ( w < 0.0f ) { return false; } var denom = 1.0f/( u + v + w ); // Finally fill in the intersection point where = ( u*a + v*b + w*c )*denom; return true; }
public void FindNodesIn( Ray r, ref List<PCZSceneNode> list, PCZone startZone, PCZSceneNode exclude ) { var visitedPortals = new List<Portal>(); if ( null != startZone ) { // start in startzone, and recurse through portals if necessary startZone.FindNodes( r, ref list, visitedPortals, true, true, exclude ); } else { foreach ( PCZone zone in this.zones ) { zone.FindNodes( r, ref list, visitedPortals, false, false, exclude ); } } }
public Terrain RaySelectNeighbour( Ray ray, Real distanceLimit ) { var modifiedRay = new Ray( ray.Origin, ray.Direction ); // Move back half a square - if we're on the edge of the AABB we might // miss the intersection otherwise; it's ok for everywhere else since // we want the far intersection anyway modifiedRay.Origin = modifiedRay.GetPoint( -this.mWorldSize/this.mSize*0.5f ); // transform into terrain space var tPos = Vector3.Zero; var tDir = Vector3.Zero; ConvertPosition( Space.WorldSpace, modifiedRay.Origin, Space.TerrainSpace, ref tPos ); ConvertDirection( Space.WorldSpace, modifiedRay.Direction, Space.TerrainSpace, ref tDir ); // Discard rays with no lateral component if ( Utility.RealEqual( tDir.x, 0.0f, 1e-4 ) && Utility.RealEqual( tDir.y, 0.0f, 1e-4 ) ) { return null; } var terrainRay = new Ray( tPos, tDir ); // Intersect with boundary planes // Only collide with the positive (exit) side of the plane, because we may be // querying from a point outside ourselves if we've cascaded more than once var dist = Real.MaxValue; IntersectResult intersectResult; if ( tDir.x < 0.0f ) { intersectResult = Utility.Intersects( terrainRay, new Plane( Vector3.UnitX, Vector3.Zero ) ); if ( intersectResult.Hit && intersectResult.Distance < dist ) { dist = intersectResult.Distance; } } else if ( tDir.x > 0.0f ) { intersectResult = Utility.Intersects( terrainRay, new Plane( Vector3.NegativeUnitX, Vector3.UnitX ) ); if ( intersectResult.Hit && intersectResult.Distance < dist ) { dist = intersectResult.Distance; } } if ( tDir.y < 0.0f ) { intersectResult = Utility.Intersects( terrainRay, new Plane( Vector3.UnitY, Vector3.Zero ) ); if ( intersectResult.Hit && intersectResult.Distance < dist ) { dist = intersectResult.Distance; } } else if ( tDir.y > 0.0f ) { intersectResult = Utility.Intersects( terrainRay, new Plane( Vector3.NegativeUnitY, Vector3.UnitY ) ); if ( intersectResult.Hit && intersectResult.Distance < dist ) { dist = intersectResult.Distance; } } // discard out of range if ( dist*this.mWorldSize > distanceLimit ) { return null; } var terrainIntersectPos = terrainRay.GetPoint( dist ); var x = terrainIntersectPos.x; var y = terrainIntersectPos.y; var dx = tDir.x; var dy = tDir.y; // Never return diagonal directions, we will navigate those recursively anyway if ( Utility.RealEqual( x, 1.0f, 1e-4f ) && dx > 0 ) { return GetNeighbour( NeighbourIndex.East ); } else if ( Utility.RealEqual( x, 0.0f, 1e-4f ) && dx < 0 ) { return GetNeighbour( NeighbourIndex.West ); } else if ( Utility.RealEqual( y, 1.0f, 1e-4f ) && dy > 0 ) { return GetNeighbour( NeighbourIndex.North ); } else if ( Utility.RealEqual( y, 0.0f, 1e-4f ) && dy < 0 ) { return GetNeighbour( NeighbourIndex.South ); } return null; }
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; }
/// <see cref="Terrain.RaySelectNeighbour(Ray, Real)"/> public Terrain RaySelectNeighbour( Ray ray ) { return RaySelectNeighbour( ray, 0 ); }
public override bool FrameRenderingQueued( FrameEventArgs evt ) { if ( this.mode != Mode.Normal ) { // fire ray Ray ray; ray = TrayManager.GetCursorRay( Camera ); var rayResult = this.terrainGroup.RayIntersects( ray ); if ( rayResult.Hit ) { this.editMarker.IsVisible = true; this.editNode.Position = rayResult.Position; // figure out which terrains this affects List<Axiom.Components.Terrain.Terrain> terrainList; var brushSizeWorldSpace = TerrainWorldSize*this.brushSizeTerrainSpace; var sphere = new Sphere( rayResult.Position, brushSizeWorldSpace ); this.terrainGroup.SphereIntersects( sphere, out terrainList ); foreach ( var ti in terrainList ) { DoTerrainModify( ti, rayResult.Position, evt.TimeSinceLastFrame ); } } else { this.editMarker.IsVisible = false; } } if ( !this.fly ) { // clamp to terrain var camPos = Camera.Position; var ray = new Ray( new Vector3( camPos.x, this.terrainPos.y + 10000, camPos.z ), Vector3.NegativeUnitY ); TerrainGroup.RayResult rayResult = this.terrainGroup.RayIntersects( ray ); Real distanceAboveTerrain = 50; Real fallSpeed = 300; Real newy = camPos.y; if ( rayResult.Hit ) { if ( camPos.y > rayResult.Position.y + distanceAboveTerrain ) { this.fallVelocity += evt.TimeSinceLastFrame*20; this.fallVelocity = Utility.Min( this.fallVelocity, fallSpeed ); newy = camPos.y - this.fallVelocity*evt.TimeSinceLastFrame; } newy = Utility.Max( rayResult.Position.y + distanceAboveTerrain, newy ); Camera.Position = new Vector3( camPos.x, newy, camPos.z ); } } if ( this.heightUpdateCountDown > 0 ) { this.heightUpdateCountDown -= evt.TimeSinceLastFrame; if ( this.heightUpdateCountDown <= 0 ) { this.terrainGroup.Update(); this.heightUpdateCountDown = 0; } } if ( this.terrainGroup.IsDerivedDataUpdateInProgress ) { TrayManager.MoveWidgetToTray( this.infoLabel, TrayLocation.Top, 0 ); this.infoLabel.Show(); if ( this.terrainsImported ) { this.infoLabel.Caption = "Building terrain, please wait..."; } else { this.infoLabel.Caption = "Updating textures, patience..."; } } else { TrayManager.RemoveWidgetFromTray( this.infoLabel ); this.infoLabel.Hide(); if ( this.terrainsImported ) { SaveTerrains( true ); this.terrainsImported = false; } } return base.FrameRenderingQueued( evt ); }
/// <summary> /// Creates a query to return objects found along the ray. /// </summary> /// <param name="ray">Ray to use for the intersection query.</param> /// <returns>A specialized implementation of RaySceneQuery for this scene manager.</returns> public override RaySceneQuery CreateRayQuery( Ray ray ) { return CreateRayQuery( ray, 0xffffffff ); }
public void _findNodes( Ray t, ref List<PCZSceneNode> list, PCZSceneNode exclude, bool includeVisitors, bool full ) { if ( !full ) { AxisAlignedBox obox; _getCullBounds( out obox ); Intersection isect = intersect( t, obox ); if ( isect == Intersection.OUTSIDE ) { return; } full = ( isect == Intersection.INSIDE ); } foreach ( PCZSceneNode on in this.nodeList.Values ) { if ( on != exclude && ( on.HomeZone == this.zone || includeVisitors ) ) { if ( full ) { // make sure the node isn't already on the list list.Add( on ); } else { Intersection nsect = intersect( t, on.WorldAABB ); if ( nsect != Intersection.OUTSIDE ) { // make sure the node isn't already on the list list.Add( on ); } } } } Octree child; if ( ( child = this.Children[ 0, 0, 0 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 1, 0, 0 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 0, 1, 0 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 1, 1, 0 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 0, 0, 1 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 1, 0, 1 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 0, 1, 1 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } if ( ( child = this.Children[ 1, 1, 1 ] ) != null ) { child._findNodes( t, ref list, exclude, includeVisitors, full ); } }
/// <summary> /// Creates a query to return objects found along the ray. /// </summary> /// <param name="ray">Ray to use for the intersection query.</param> /// <returns>A specialized implementation of RaySceneQuery for this scene manager.</returns> public override RaySceneQuery CreateRayQuery( Ray ray, uint mask ) { var query = new TerrainRaySceneQuery( this ); query.Ray = ray; query.QueryMask = mask; return query; }
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 ); } }
/// <summary> /// /// </summary> /// <param name="ray"></param> /// <returns></returns> public RayResult RayIntersects(Ray ray) { return RayIntersects(ray, 0); }
/// <summary> /// Creates a RaySceneQuery for this scene manager. /// </summary> /// <remarks> /// This method creates a new instance of a query object for this scene manager, /// looking for objects which fall along a ray. See SceneQuery and RaySceneQuery /// for full details. /// </remarks> /// <param name="ray">Details of the ray which describes the region for this query.</param> /// <param name="mask">The query mask to apply to this query; can be used to filter out certain objects; see SceneQuery for details.</param> public override RaySceneQuery CreateRayQuery( Ray ray, uint mask ) { var q = new BspRaySceneQuery( this ); q.Ray = ray; q.QueryMask = mask; return q; }
/// <summary> /// /// </summary> /// <param name="ray"></param> /// <param name="distanceLimit"></param> /// <returns></returns> public RayResult RayIntersects(Ray ray, float distanceLimit) { long curr_x = 0, curr_z = 0; ConvertWorldPositionToTerrainSlot(ray.Origin, out curr_x, out curr_z); TerrainSlot slot = GetTerrainSlot(curr_x, curr_z); RayResult result = new RayResult(false, null, Vector3.Zero); Vector3 tmp, localRayDir, centreOrigin, offset; tmp = localRayDir = centreOrigin = offset = Vector3.Zero; ConvertTerrainSlotToWorldPosition(curr_x, curr_z, out centreOrigin); offset = ray.Origin - centreOrigin; localRayDir = ray.Direction; switch (Alignment) { case Alignment.Align_X_Y: float t1 = localRayDir.x, t2 = localRayDir.z; Swap(t1, t2); localRayDir = new Vector3(t1, localRayDir.y, t2); t1 = offset.x; t2 = offset.z; Swap(t1, t2); localRayDir = new Vector3(t1, offset.y, t2); break; case Alignment.Align_Y_Z: // x = z, z = y, y = -x tmp.x = localRayDir.z; tmp.z = localRayDir.y; tmp.y = -localRayDir.x; localRayDir = tmp; tmp.x = offset.z; tmp.z = offset.y; tmp.y = -offset.x; offset = tmp; break; case Alignment.Align_X_Z: // already in X/Z but values increase in -Z localRayDir.z = -localRayDir.z; offset.z = -offset.z; break; } offset /= _terrainWorldSize; offset = new Vector3(offset.x +0.5f,offset.y + 0.5f, offset.z +0.5f); Vector3 inc = new Vector3(Math.Utility.Abs(localRayDir.x), Math.Utility.Abs(localRayDir.y), Math.Utility.Abs(localRayDir.z)); long xdir = localRayDir.x > 0.0f ? 1 : -1; long zdir = localRayDir.z > 0.0f ? 1 : -1; // We're always counting from 0 to 1 regardless of what direction we're heading if (xdir < 0) offset.x = 1.0f - offset.x; if (zdir < 0) offset.z = 1.0f - offset.z; // find next slot bool keepSearching = true; int numGaps = 0; while (keepSearching) { if (Math.Utility.RealEqual(inc.x, 0.0f) && Math.Utility.RealEqual(inc.z, 0.0f)) keepSearching = false; while ((slot != null || slot.Instance != null) && keepSearching) { ++numGaps; /// if we don't find any filled slot in 6 traversals, give up if (numGaps > 6) { keepSearching = false; break; } // find next slot Vector3 oldoffset = offset; while (offset.x < 1.0f && offset.z < 1.0f) offset += inc; if (offset.x >= 1.0f && offset.z >= 1.0f) { // We crossed a corner, need to figure out which we passed first Real diffz = 1.0f - oldoffset.z; Real diffx = 1.0f - oldoffset.x; Real distz = diffz / inc.z; Real distx = diffx / inc.x; if (distx < distz) { curr_x += xdir; offset.x -= 1.0f; } else { curr_z += zdir; offset.z -= 1.0f; } } else if (offset.x >= 1.0f) { curr_x += xdir; offset.x -= 1.0f; } else if (offset.z >= 1.0f) { curr_z += zdir; offset.z -= 1.0f; } if (distanceLimit > 0) { Vector3 worldPos; ConvertTerrainSlotToWorldPosition(curr_x, curr_z, out worldPos); if (ray.Origin.Distance(worldPos) > distanceLimit) { keepSearching = false; break; } } slot = GetTerrainSlot(curr_x, curr_z); } if (slot != null && slot.Instance != null) { numGaps = 0; // don't cascade into neighbours KeyValuePair<bool, Vector3> raypair = slot.Instance.RayIntersects(ray, false, distanceLimit); if (raypair.Key) { keepSearching = false; result.Hit = true; result.Terrain = slot.Instance; result.Position = raypair.Value; break; } else { // not this one, trigger search for another slot slot = null; } } } return result; }
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 ); } } } } }
protected override void OnFrameStarted( object source, FrameEventArgs evt ) { buildingTranslate = new Vector3( 0, 0, 0 ); if ( input.IsKeyPressed( KeyCodes.U ) ) { buildingTranslate = new Vector3( 0, -10, 0 ); } if ( input.IsKeyPressed( KeyCodes.I ) ) { buildingTranslate = new Vector3( 0, 10, 0 ); } if ( input.IsKeyPressed( KeyCodes.LeftShift ) || input.IsKeyPressed( KeyCodes.RightShift ) ) { mMoveSpeed = 150; } else { mMoveSpeed = 15; } // test the ray scene query by showing bounding box of whatever the camera is pointing directly at // (takes furthest hit) Ray updateRay = new Ray(); updateRay.Origin = camera.ParentSceneNode.Position; updateRay.Direction = camera.ParentSceneNode.Orientation * Vector3.NegativeUnitZ; raySceneQuery.Ray = updateRay; PCZone zone = ( (PCZSceneNode)( camera.ParentSceneNode ) ).HomeZone; ( (PCZRaySceneQuery)raySceneQuery ).StartZone = zone; ( (PCZRaySceneQuery)raySceneQuery ).ExcludeNode = camera.ParentSceneNode; raySceneQuery.Execute( l ); base.OnFrameStarted( source, evt ); }
/// <summary> /// Creates a query to return objects found along the ray. /// </summary> /// <param name="ray">Ray to use for the intersection query.</param> /// <param name="mask"></param> /// <returns>A specialized implementation of RaySceneQuery for this scene manager.</returns> public virtual RaySceneQuery CreateRayQuery( Ray ray, uint mask ) { DefaultRaySceneQuery query = new DefaultRaySceneQuery( this ); query.Ray = ray; query.QueryMask = mask; return query; }
// 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; }