//----------------------------------------------------------------------------- // TrackColumns.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Text; using RacingGame.Graphics; using Model = RacingGame.Graphics.Model; using RacingGame.Landscapes; using RacingGame.Shaders; namespace RacingGame.Tracks { /// /// Track columns /// class TrackColumns : IDisposable { /// /// Column vertices, just a simple circle! /// readonly TangentVertex[] BaseColumnVertices = new TangentVertex[] { // 0 new TangentVertex( new Vector3(1, 0, 0), new Vector2(0.0f / 6.0f, 0.0f), new Vector3(1, 0, 0), new Vector3(0, 0, -1)), // 1 new TangentVertex( new Vector3(0.5f, 0.866025f, 0), new Vector2(1.0f / 6.0f, 0.0f), new Vector3(0.5f, 0.866025f, 0), new Vector3(0, 0, -1)), // 2 new TangentVertex( new Vector3(-0.5f, 0.866025f, 0), new Vector2(2.0f/ 6.0f, 0.0f), new Vector3(-0.5f, 0.866025f, 0), new Vector3(0, 0, -1)), // 3 new TangentVertex( new Vector3(-1, 0, 0), new Vector2(3.0f / 6.0f, 0.0f), new Vector3(-1, 0, 0), new Vector3(0, 0, -1)), // 4 new TangentVertex( new Vector3(-0.5f, -0.866025f, 0), new Vector2(4.0f / 6.0f, 0.0f), new Vector3(-0.5f, -0.866025f, 0), new Vector3(0, 0, -1)), // 5 new TangentVertex( new Vector3(0.5f, -0.866025f, 0), new Vector2(5.0f / 6.0f, 0.0f), new Vector3(0.5f, -0.866025f, 0), new Vector3(0, 0, -1)), // 6 (duplicated first to wrap around tex coords) new TangentVertex( new Vector3(1, 0, 0), new Vector2(6.0f / 6.0f, 0.0f), new Vector3(1, 0, 0), new Vector3(0, 0, -1)), }; /// /// Gap between the seperate piles. /// const float ColumnsDistance = 33.0f; /// /// Column rendering height above the landscape ground. /// const float ColumnGroundHeight = 1.0f; /// /// Minimium column height we need, else we skip generation! /// const float MinimumColumnHeight = 2.5f; /// /// Put columns a little bit on the inside of the road, /// we don't want them to come through the road. /// const float TopColumnSubHeight = 0.55f; /// /// Column position list. /// List columnPositions = new List(); /// /// Rail vertices. /// TangentVertex[] columnVertices = null; /// /// Vertex buffer of guard rail. /// VertexBuffer columnVb = null; /// /// Index buffer orf guard rail. /// IndexBuffer columnIb = null; /// /// Create track columns /// /// Points /// Landscape for getting the ground height public TrackColumns(List points, Landscape landscape) { if (landscape == null) return; float lastColumnsDistance = ColumnsDistance; List columnPointSpacesTop = new List(); List columnPointSpacesBottom = new List(); for (int num = 0; num < points.Count; num++) { // Distance of the current position to the next position float distance = Vector3.Distance( points[(num + 1) % points.Count].pos, points[num].pos); // Uniform calculation of the distance for the columns, // so it doesn't matter if there is a gap of 2 or 200 m // Have we reach or go over the ColumnsDistance? if (lastColumnsDistance - distance <= 0) { // Catmull interpolation, instead the linear interpolation, for a // better position calculation, especially in curves Vector3 p1 = points[num - 1 < 0 ? points.Count - 1 : num - 1].pos; Vector3 p2 = points[num].pos; Vector3 p3 = points[(num + 1) % points.Count].pos; Vector3 p4 = points[(num + 2) % points.Count].pos; Vector3 holderPoint = Vector3.CatmullRom(p1, p2, p3, p4, lastColumnsDistance / distance); // Just find out how much this point is pointing up float draft = Vector3.Dot(points[num].up, new Vector3(0, 0, 1)); // And don't add if height is too small! float columnHeight = holderPoint.Z - landscape.GetMapHeight(holderPoint.X, holderPoint.Y); // Store the position for this holder if (draft > 0.3f &&//< 0 MaxColumnGenerationAngel && columnHeight > MinimumColumnHeight) { columnPositions.Add(holderPoint); // The unit vectors for our local point space Vector3 right = points[num].right; Vector3 dir = points[num].dir; Vector3 up = points[num].up; // Create the coordinate system for the current point by the 3 // unit vectors. Matrix pointSpace = Matrix.Identity; pointSpace.M11 = right.X; pointSpace.M12 = right.Y; pointSpace.M13 = right.Z; pointSpace.M21 = dir.X; pointSpace.M22 = dir.Y; pointSpace.M23 = dir.Z; pointSpace.M31 = up.X; pointSpace.M32 = up.Y; pointSpace.M33 = up.Z; // Remember point space columnPointSpacesTop.Add(pointSpace); // Same for bottom, but don't use up vector (let it stay default) pointSpace = Matrix.Identity; Vector3 upVector = new Vector3(0, 0, 1); // Rebuild right vector (to make it 90 degree to our up vector) Vector3 rightVector = Vector3.Cross(dir, upVector); pointSpace.M11 = rightVector.X; pointSpace.M12 = rightVector.Y; pointSpace.M13 = rightVector.Z; pointSpace.M21 = dir.X; pointSpace.M22 = dir.Y; pointSpace.M23 = dir.Z; columnPointSpacesBottom.Add(pointSpace); } // We have just set a pile, the next pile will be set after // reaching the next holder gap. lastColumnsDistance += ColumnsDistance; } // The distance we have to cover until the next position. // We subtract our current distance from the remaining gap distance, // which will then be checked in the next loop. lastColumnsDistance -= distance; } columnVertices = new TangentVertex[ columnPositions.Count * BaseColumnVertices.Length * 2]; // Go through all columns for (int num = 0; num < columnPositions.Count; num++) { Vector3 pos = columnPositions[num]; // Find out the current landscape height here Vector3 bottomPos = new Vector3(pos.X, pos.Y, landscape.GetMapHeight(pos.X, pos.Y) + ColumnGroundHeight); Vector3 topPos = new Vector3(pos.X, pos.Y, pos.Z - TopColumnSubHeight); // Calculate top v tex coord for this column float topTexV = Vector3.Distance(topPos, bottomPos) / (MathHelper.Pi * 2); // Use the BaseColumnVertices twice, once for the bottom and then for the // top part of our generated column. for (int topBottom = 0; topBottom < 2; topBottom++) { // Go to all BaseColumnVertices for (int i = 0; i < BaseColumnVertices.Length; i++) { int vertIndex = num * BaseColumnVertices.Length * 2 + topBottom * BaseColumnVertices.Length + i; // For the top positions, modify them them to fit directly // on the bottom side of our road. Same for bottom, but don't // modify the z value Matrix transformMatrix = topBottom == 0 ? columnPointSpacesBottom[num] : columnPointSpacesTop[num]; // We don't have to transform the vertices much, just adjust // the z value and the v tex coord. columnVertices[vertIndex] = new TangentVertex( (topBottom == 0 ? bottomPos : topPos) + Vector3.Transform(BaseColumnVertices[i].pos, transformMatrix), BaseColumnVertices[i].U, topBottom == 0 ? 0 : topTexV, Vector3.Transform(BaseColumnVertices[i].normal, transformMatrix), Vector3.Transform(-BaseColumnVertices[i].tangent, transformMatrix)); } } // Also add the bottom position to the list of holders we want // to render later. if (landscape != null && // This is not really required, we can easily optimize this out. BaseGame.HighDetail) landscape.AddObjectToRender( "RoadColumnSegment", new Vector3(bottomPos.X, bottomPos.Y, bottomPos.Z - ColumnGroundHeight)); } // Create the vertex buffer from our vertices. // fix //columnVb = new VertexBuffer( // BaseGame.Device, // typeof(TangentVertex), // columnVertices.Length, // ResourceUsage.WriteOnly, // ResourceManagementMode.Automatic); columnVb = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), columnVertices.Length, BufferUsage.WriteOnly); columnVb.SetData(columnVertices); // Count of quads (polygons) we have for each column int quadPolysPerColumn = BaseColumnVertices.Length - 1; int[] indices = new int[(2 * 3 * quadPolysPerColumn) * columnPositions.Count]; // Current vertex index int vertexIndex = 0; // Helper variable, current index of the indices list int indicesIndex = 0; for (int num = 0; num < columnPositions.Count; num++) { // Set all quads of the column for (int j = 0; j < quadPolysPerColumn; j++) { indicesIndex = 3 * 2 * (num * quadPolysPerColumn + j); // 1. Polygon indices[indicesIndex] = vertexIndex + j; indices[indicesIndex + 1] = vertexIndex + 1 + BaseColumnVertices.Length + j; indices[indicesIndex + 2] = vertexIndex + 1 + j; // 2. Polygon indices[indicesIndex + 3] = indices[indicesIndex + 1]; indices[indicesIndex + 4] = indices[indicesIndex]; indices[indicesIndex + 5] = vertexIndex + BaseColumnVertices.Length + j; } // Go to next column vertexIndex += BaseColumnVertices.Length * 2; } // Create the index buffer from our indices. // fix //columnIb = new IndexBuffer( // BaseGame.Device, // typeof(int), // indices.Length, // ResourceUsage.WriteOnly, // ResourceManagementMode.Automatic); columnIb = new IndexBuffer( BaseGame.Device, typeof(int), indices.Length, BufferUsage.WriteOnly); columnIb.SetData(indices); } public void Dispose() { columnVb.Dispose(); columnIb.Dispose(); } /// /// Render /// public void Render(Material columnMaterial) { // We use tangent vertices for everything here // Restore the world matrix BaseGame.WorldMatrix = Matrix.Identity; // Render all columns ShaderEffect.normalMapping.Render( columnMaterial, "Specular20", new BaseGame.RenderHandler(RenderColumnVertices)); /*now done in landscape object rendering // And also render all the holders foreach (Vector3 pos in columnHolderPositions) { holderModel.Render(pos); } */ // Restore the world matrix BaseGame.WorldMatrix = Matrix.Identity; } /// /// Render column vertices /// private void RenderColumnVertices() { if (columnVertices == null) return; BaseGame.Device.SetVertexBuffer(columnVb); BaseGame.Device.Indices = columnIb; BaseGame.Device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, (BaseColumnVertices.Length - 1) * columnPositions.Count * 2); } } }