using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; namespace SemiColinGames { public sealed class TreeScene : IScene { const int MAX_SEGMENTS = 1000; const int MAX_VERTICES = MAX_SEGMENTS * 6; // 2 triangles per segment private readonly Color backgroundColor = Color.SkyBlue; private readonly GraphicsDevice graphics; private readonly BasicEffect basicEffect; private VertexPositionColor[] vertices; private VertexBuffer vertexBuffer; public TreeScene(GraphicsDevice graphics) { this.graphics = graphics; basicEffect = new BasicEffect(graphics) { World = Matrix.CreateTranslation(0, 0, 0), View = Matrix.CreateLookAt(Vector3.Backward, Vector3.Zero, Vector3.Up), VertexColorEnabled = true, Projection = Matrix.CreateOrthographicOffCenter( -1920 / 2, 1920 / 2, -1080 / 4, 1080 * 3 / 4, -1, 1) }; vertices = new VertexPositionColor[MAX_VERTICES]; vertexBuffer = new VertexBuffer( graphics, typeof(VertexPositionColor), MAX_VERTICES, BufferUsage.WriteOnly); } ~TreeScene() { Dispose(); } public void Dispose() { vertexBuffer.Dispose(); GC.SuppressFinalize(this); } public struct Trapezoid { public Vector2 p1, p2, p3, p4; public void Rotate(float angle) { p1 = p1.Rotate(angle); p2 = p2.Rotate(angle); p3 = p3.Rotate(angle); p4 = p4.Rotate(angle); } public void Translate(Vector2 position) { p1 = Vector2.Add(p1, position); p2 = Vector2.Add(p2, position); p3 = Vector2.Add(p3, position); p4 = Vector2.Add(p4, position); } } public class TreeNode { // Ideal orientation, relative to its parent. public readonly float Orientation; // Orientation in world space. public float WorldOrientation; public readonly float Length; public readonly float InWidth; public readonly float OutWidth; public readonly List Children; // Position of in-vertex in world space. public Vector2 Position; public TreeNode(float orientation, float length, float inWidth, float outWidth) : this(orientation, length, inWidth, outWidth, new List()) { } public TreeNode(float orientation, float length, float inWidth, float outWidth, TreeNode child) : this(orientation, length, inWidth, outWidth, new List() { child }) { } public TreeNode(float orientation, float length, float inWidth, float outWidth, TreeNode child1, TreeNode child2) : this(orientation, length, inWidth, outWidth, new List() { child1, child2 }) { } public TreeNode(float orientation, float length, float inWidth, float outWidth, TreeNode child1, TreeNode child2, TreeNode child3) : this(orientation, length, inWidth, outWidth, new List() { child1, child2, child3 }) { } public TreeNode(float orientation, float length, float inWidth, float outWidth, List children) { Orientation = orientation; WorldOrientation = orientation; Length = length; InWidth = inWidth; OutWidth = outWidth; Children = children; Position = Vector2.Zero; } } public void Draw(bool isRunningSlowly, IWorld iworld, bool paused) { var tree = new TreeNode(0.0f, 100, 10, 6, new TreeNode(0.0f, 100, 10, 6, new TreeNode(-0.2f, 100, 10, 6, new TreeNode(-0.3f, 100, 6, 4, new TreeNode(-0.1f, 100, 6, 4, new TreeNode(-0.3f, 150, 4, 2), new TreeNode(0.2f, 200, 4, 2), new TreeNode(0.5f, 100, 4, 2))), new TreeNode(0.5f, 100, 6, 4, new TreeNode(-0.1f, 100, 6, 4, new TreeNode(-0.1f, 150, 4, 2), new TreeNode(0.2f, 200, 4, 2)))))); graphics.Clear(backgroundColor); var segments = new List(); LinkedList queue = new LinkedList(); queue.AddLast(tree); Debug.WriteLine("---------------------"); while (queue.Count > 0) { TreeNode parent = queue.First.Value; queue.RemoveFirst(); Vector2 outVector = new Vector2(0, parent.Length).Rotate(parent.WorldOrientation); Vector2 outPosition = Vector2.Add(parent.Position, outVector); outVector.Normalize(); Vector2 wind = new Vector2(1.0f, 0.0f); float windAmount = Vector2.Dot(wind, outVector); Debug.WriteLine("" + windAmount); // We want a trapezoid with 4 points. A is the in position, B is the out position. // The TreeNode.Length is the distance from A to B. // // We come up with the points relative to A being the origin, then rotate the trapezoid // by its orientation, and translate the result to A's actual position. // // 3---B---4 <-- length = outWidth // / | \ // / | \ // 1------A------2 <-- length = inWidth // This fudge factor lengthens the sides a bit longer to prevent small discontinuities // in the rendered result. // TODO: remove this sideLengthFudge. float sideLengthFudge = 1.05f; Trapezoid t = new Trapezoid(); t.p1 = new Vector2(-parent.InWidth, 0); t.p2 = new Vector2(parent.InWidth, 0); t.p3 = new Vector2(-parent.OutWidth, parent.Length * sideLengthFudge); t.p4 = new Vector2(parent.OutWidth, parent.Length * sideLengthFudge); t.Rotate(parent.WorldOrientation); t.Translate(parent.Position); segments.Add(t); foreach (TreeNode child in parent.Children) { child.Position = outPosition; float orientation = parent.WorldOrientation + child.Orientation; child.WorldOrientation = orientation; queue.AddLast(child); } } Color color = Color.SaddleBrown; for (int i = 0; i < segments.Count; i++) { Trapezoid t = segments[i]; vertices[i * 6] = new VertexPositionColor(new Vector3(t.p1.X, t.p1.Y, 0), color); vertices[i * 6 + 1] = new VertexPositionColor(new Vector3(t.p2.X, t.p2.Y, 0), color); vertices[i * 6 + 2] = new VertexPositionColor(new Vector3(t.p3.X, t.p3.Y, 0), color); vertices[i * 6 + 3] = new VertexPositionColor(new Vector3(t.p2.X, t.p2.Y, 0), color); vertices[i * 6 + 4] = new VertexPositionColor(new Vector3(t.p3.X, t.p3.Y, 0), color); vertices[i * 6 + 5] = new VertexPositionColor(new Vector3(t.p4.X, t.p4.Y, 0), color); } graphics.SetVertexBuffer(vertexBuffer); vertexBuffer.SetData(vertices); foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { pass.Apply(); graphics.DrawPrimitives(PrimitiveType.TriangleList, 0, segments.Count * 2); } } } }