/// <summary> /// Test if terrain intersects a sub-rect of terrain. /// </summary> private void RayIntersectsTerrain(Intersection.ObjectIntersection <QuadNode> ni, out TerrainIntersectionData terrainCollisionData) { // Get node QuadNode node = ni.Object; // Transform ray back to object space Ray ray; Matrix inverseTransform = Matrix.Invert(node.WorldMatrix); ray.Position = Vector3.Transform(core.Renderer.PointerRay.Position, inverseTransform); ray.Direction = Vector3.TransformNormal(core.Renderer.PointerRay.Direction, inverseTransform); int index; float height = 0; Vector2 position = Vector2.Zero; int loopCounter = 0; Vector3 step = ray.Direction * 1f; bool found = false; while (true) { // Step ray along direction ray.Position += step; // Break loop if running too long if (++loopCounter > 5000) { found = false; break; } // Get position position.X = ray.Position.X + node.Rectangle.X; position.Y = ray.Position.Z + node.Rectangle.Y; if (position.X < 0 || position.X > mapDimension - 1 || position.Y < 0 || position.Y > mapDimension - 1) { found = false; continue; } // If we've come this far then an intersection is likely found = true; // Get index into arrays index = (int)position.Y * (int)mapDimension + (int)position.X; // Get height of terrain at this position height = terrainData[index].W * mapHeight; if (ray.Position.Y <= height) { ray.Position -= step; break; } } // Exit if no intersection found if (!found) { terrainCollisionData.Distance = null; terrainCollisionData.WorldPosition = Vector3.Zero; terrainCollisionData.MapPosition = Vector2.Zero; return; } // Store map collision in 0,1 space terrainCollisionData.MapPosition.X = position.X / mapDimension; terrainCollisionData.MapPosition.Y = position.Y / mapDimension; // Store world collision in world space Vector3 worldPosition = new Vector3 { X = position.X, Y = height, Z = position.Y, }; terrainCollisionData.WorldPosition = Vector3.Transform(worldPosition, this.Matrix); // Set distance terrainCollisionData.Distance = Vector3.Distance(core.ActiveScene.Camera.Position, terrainCollisionData.WorldPosition); }
/// <summary> /// Draw this node and all child nodes. /// </summary> /// <param name="node">Node to draw.</param> /// <param name="caller">Entity calling the draw operation.</param> private void DrawNode(QuadNode node, BaseEntity caller) { if (node == null) { return; } // Calculate world matrix node.WorldMatrix = node.Matrix * this.matrix * Matrix.CreateTranslation(caller.Matrix.Translation); // Transform bounds for this node BoundingBox bounds; bounds.Min = Vector3.Transform(node.BoundingBox.Min, node.WorldMatrix); bounds.Max = Vector3.Transform(node.BoundingBox.Max, node.WorldMatrix); // Test node against frustum if (!bounds.Intersects(core.ActiveScene.Camera.BoundingFrustum)) { return; } // Recurse children foreach (QuadNode child in node.Children) { DrawNode(child, caller); } // Calculate sample scale Vector2 sampleScale; sampleScale.X = (float)leafDimension / (float)mapDimension; sampleScale.Y = sampleScale.X; // Only draw terrain grid for leaf nodes if (!node.HasChildren) { if (enablePicking) { // Test for node-ray intersection float?intersectDistance = core.Renderer.PointerRay.Intersects(bounds); if (intersectDistance != null) { // Add to intersection list Intersection.ObjectIntersection <QuadNode> ni = new Intersection.ObjectIntersection <QuadNode>(intersectDistance, node); pointerNodeIntersections.Add(ni); } } // Calculate sample offset Vector2 sampleOffset; sampleOffset.X = node.X / mapDimension; sampleOffset.Y = node.Y / mapDimension; // Set effect parameters for this quad terrainEffect_SampleScale.SetValue(sampleScale); terrainEffect_SampleOffset.SetValue(sampleOffset); terrainEffect_World.SetValue(node.WorldMatrix); // Apply effect terrainEffect.Techniques[0].Passes[0].Apply(); // Draw grid core.GraphicsDevice.SetVertexBuffer(grid.VertexBuffer); core.GraphicsDevice.Indices = grid.IndexBuffer; core.GraphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, grid.VertexBuffer.VertexCount, 0, grid.IndexBuffer.IndexCount / 3); } }