//----------------------------------------------------------------------------- // GuardRail.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 RacingGame.Shaders; using Model = RacingGame.Graphics.Model; using RacingGame.Landscapes; namespace RacingGame.Tracks { /// /// Guard rail /// class GuardRail : IDisposable { /// /// Guard rail vertices, determinted with help of the unit test below! /// readonly TangentVertex[] GuardRailVertices = new TangentVertex[] { // 0 new TangentVertex( new Vector3(10, 0, -105), new Vector2(0.0f, 1 - 0.442877f), new Vector3(-0.382683f, 0, -0.923880f), new Vector3(0, -1, 0)), // 1 new TangentVertex( new Vector3(20, 0, -105), new Vector2(0.0f, 1 - 0.432881f), new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 2 new TangentVertex( new Vector3(-10, 0, -75), new Vector2(0.0f, 1 - 0.402893f), new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 3 new TangentVertex( new Vector3(-10, 0, -45), new Vector2(0.0f, 1 - 0.372905f), new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 4 new TangentVertex( new Vector3(20, 0, -15), new Vector2(0.0f, 1 - 0.342917f), new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 5 new TangentVertex( new Vector3(20, 0, 15), new Vector2(0.0f, 1 - 0.312929f), new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 6 new TangentVertex( new Vector3(-10, 0, 45), new Vector2(0.0f, 1 - 0.282941f), new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 7 new TangentVertex( new Vector3(-10, 0, 75), new Vector2(0.0f, 1 - 0.252953f), new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 8 new TangentVertex( new Vector3(20, 0, 105), new Vector2(0.0f, 1 - 0.222965f), new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 9 new TangentVertex( new Vector3(10, 0, 105), new Vector2(0.0f, 1 - 0.212969f), new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 10 new TangentVertex( new Vector3(-20, 0, 75), new Vector2(0.0f, 1 - 0.182981f), new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 11 new TangentVertex( new Vector3(-20, 0, 45), new Vector2(0.0f, 1 - 0.152993f), new Vector3(-0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 12 new TangentVertex( new Vector3(10, 0, 15), new Vector2(0.0f, 1 - 0.123005f), new Vector3(-0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 13 new TangentVertex( new Vector3(10, 0, -15), new Vector2(0.0f, 1 - 0.093017f), new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 14 new TangentVertex( new Vector3(-20, 0, -45), new Vector2(0.0f, 1 - 0.063029f), new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)), // 15 new TangentVertex( new Vector3(-20, 0, -75), new Vector2(0.0f, 1 - 0.033041f), new Vector3(-0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)), // 16 (duplicated first to wrap around tex coords) new TangentVertex( new Vector3(10, 0, -105), new Vector2(0.0f, 1 - 0.003053f), new Vector3(-0.382683f, 0, -0.923880f), new Vector3(0, -1, 0)), }; /// /// Downscale factor for the vertices. /// const float CorrectionScale = 0.0019f; /// /// Gap between the seperate piles. /// const float HolderGap = 15.0f; /// /// Guard rail rendering height (little bit above the ground). /// const float GuardRailHeight = 1.35f * 1.5f * 0.425f; /// /// Put guardrails a little bit on the inside of the road. /// public const float InsideRoadDistance = 0.5f; /// /// Correction vector to put holder pile at the correct position. /// static readonly Vector3 HolderPileCorrectionVector = new Vector3(0.225f, 0, 0); /// /// We got 2 modes for a guard rail, left and right, depending /// on with side of the road we want to have it. /// public enum Modes { Left, Right, } /// /// Rail points for the whole guard rail. /// TrackVertex[] railPoints = null; /// /// Rail vertices. /// TangentVertex[] railVertices = null; /// /// Vertex buffer of guard rail. /// VertexBuffer railVb = null; /// /// Index buffer orf guard rail. /// IndexBuffer railIb = null; /*now done in track class! /// /// Matrix list for each of the holder pile objects we have to render. /// List holderPileMatrices = new List(); */ /// /// Create guard rail /// /// Points of the road itself /// Mode, left or right /// Landscape public GuardRail(List points, Modes mode, Landscape landscape) { // First generate a list of points at the side of the road where // we are going to generate all the guard rail vertices. // Note: We use only half as much points as points provides! railPoints = new TrackVertex[points.Count / 2 + 1]; for (int num = 0; num < railPoints.Length; num++) { // Make sure we have a closed line, we might have to skip the // last points entry because we devided through 2. int pointNum = num * 2; if (pointNum >= points.Count - 1) pointNum = points.Count - 1; // Just copy the points over and manipulate the position and the // right vector depending on which side of the guard rail we are // going to generate here. if (mode == Modes.Left) { railPoints[num] = points[pointNum].LeftTrackVertex; // Invert the direction and right vector for the left side // This makes everything point in the other road direction! railPoints[num].right = -railPoints[num].right; railPoints[num].dir = -railPoints[num].dir; // Move it a little inside the road railPoints[num].pos -= railPoints[num].right * InsideRoadDistance; } else { railPoints[num] = points[pointNum].RightTrackVertex; // Move it a little inside the road railPoints[num].pos -= railPoints[num].right * InsideRoadDistance; } } railVertices = new TangentVertex[railPoints.Length * GuardRailVertices.Length]; // Current texture coordinate for the guardrail in our current direction. float uTexValue = 0.5f; float lastHolderGap = 0;//HolderGap; for (int num = 0; num < railPoints.Length; num++) { // The unit vectors for our local point space Vector3 right = railPoints[num].right; Vector3 dir = railPoints[num].dir; Vector3 up = railPoints[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; Vector3 localPos = railPoints[num].pos; // Adjust the position for the guardrail, put it up a little. localPos += up * GuardRailHeight; // Set the beginning- or ending point for the guardrail around the // current spline position for (int i = 0; i < GuardRailVertices.Length; i++) { // Transform each of our guardrail points by our local point space // and translate it to our current position. Vector3 pos = Vector3.Transform( GuardRailVertices[i].pos * CorrectionScale, pointSpace * Matrix.CreateTranslation(localPos)); // Also transform our normal and tangent data Vector3 normal = Vector3.TransformNormal( // Left side needs inverted normals (side is inverted) (mode == Modes.Left ? -1 : 1) * GuardRailVertices[i].normal, pointSpace); Vector3 tangent = Vector3.TransformNormal( -GuardRailVertices[i].tangent, //GuardRailVertices[i].normal, //new Vector3(0, 0, -1), pointSpace); // Store vertex railVertices[num * GuardRailVertices.Length + i] = new TangentVertex(pos, uTexValue, GuardRailVertices[i].V, normal, tangent); } // Distance of the current position to the next position float distance = Vector3.Distance( railPoints[(num + 1) % railPoints.Length].pos, railPoints[num].pos); // Uniform calculation of the texture coordinates for the guardrail, // so it doesn't matter if there is a gap of 2 or 200 m // -> through "1 / HolderGap" we guarantee that the drilling // (from the texture) is always set in the front of the pile, // no matter which holder gap is set // Note: Only display a holder for every 3 texture loops. uTexValue += (1 / HolderGap) * distance * 2.0f; // Have we reach or go over the holder gap ? if (lastHolderGap - distance <= 0) { // Catmull interpolation, instead the linear interpolation, for a // better position calculation, especially in curves Vector3 p1 = railPoints[num - 1 < 0 ? railPoints.Length - 1 : num - 1].pos; Vector3 p2 = railPoints[num].pos; Vector3 p3 = railPoints[(num + 1) % railPoints.Length].pos; Vector3 p4 = railPoints[(num + 2) % railPoints.Length].pos; Vector3 holderPoint = Vector3.CatmullRom(p1, p2, p3, p4, lastHolderGap / distance); // Store the calculated render matrix for this holder pile object if (landscape != null && // Completely ignore all guard rails for low detail // to save performance (few thousand objects per track less) BaseGame.HighDetail) landscape.AddObjectToRender( "GuardRailHolder", // Fix scaling a little //Matrix.CreateScale(0.9f) * Matrix.CreateScale(1.125f) * // First the translation to get the pile to the back of the // guardrail and not in the middle Matrix.CreateTranslation(HolderPileCorrectionVector) * // the ordinary transformation to the current point space pointSpace * // at least we calculate to correct position where the pile // reaches exactly the holder gap Matrix.CreateTranslation(holderPoint), // Optimize performance: Set this to false, // but then we won't have shadows for the holder piles. false);//true); // We have just set a pile, the next pile will be set after // reaching the next holder gap. lastHolderGap += HolderGap; } // 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. lastHolderGap -= distance; } // Create the vertex buffer from our vertices. //railVb = new VertexBuffer( // BaseGame.Device, // typeof(TangentVertex), // railVertices.Length, // ResourceUsage.WriteOnly, // ResourceManagementMode.Automatic); railVb = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), railVertices.Length, BufferUsage.WriteOnly); railVb.SetData(railVertices); // Count of quads (polygons) which creates the current guardrail segment int quadPolysPerStrip = GuardRailVertices.Length - 1; int[] indices = new int[(2 * 3 * quadPolysPerStrip) * (railPoints.Length - 1)]; // Current vertex index int vertexIndex = 0; // Helper variable, current index of the indices list int indicesIndex = 0; for (int num = 0; num < railPoints.Length - 1; num++) { // Set all quads of the guardrail for (int j = 0; j < quadPolysPerStrip; j++) { indicesIndex = 3 * 2 * (num * quadPolysPerStrip + j); // 1. Polygon indices[indicesIndex] = vertexIndex + j; indices[indicesIndex + 1] = vertexIndex + 1 + j; indices[indicesIndex + 2] = vertexIndex + 1 + GuardRailVertices.Length + j; // 2. Polygon indices[indicesIndex + 3] = indices[indicesIndex + 2]; indices[indicesIndex + 4] = vertexIndex + GuardRailVertices.Length + j; indices[indicesIndex + 5] = indices[indicesIndex]; } vertexIndex += GuardRailVertices.Length; } // Create the index buffer from our indices. //railIb = new IndexBuffer( // BaseGame.Device, // typeof(int), // indices.Length, // ResourceUsage.WriteOnly, // ResourceManagementMode.Automatic); railIb = new IndexBuffer( BaseGame.Device, typeof(int), indices.Length, BufferUsage.WriteOnly); railIb.SetData(indices); } public void Dispose() { railVb.Dispose(); railIb.Dispose(); } /// /// Render /// public void Render(Material guardRailMaterial) { // We use tangent vertices for everything here // Restore the world matrix BaseGame.WorldMatrix = Matrix.Identity; // Render the complete guardrail ShaderEffect.normalMapping.Render( guardRailMaterial, "Specular20", new BaseGame.RenderHandler(RenderGuardRailVertices)); /*done in track class // And also render all the holder piles foreach (Matrix mat in holderPileMatrices) { holderModel.Render(mat); } */ // Restore the world matrix BaseGame.WorldMatrix = Matrix.Identity; } /// /// Render guard rail vertices /// private void RenderGuardRailVertices() { BaseGame.Device.SetVertexBuffer(railVb); BaseGame.Device.Indices = railIb; BaseGame.Device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, (GuardRailVertices.Length - 1) * (railPoints.Length - 1) * 2); } /// /// Generate shadow /// public void GenerateShadow() { // Just render out the guard rails (world matrix is already set) RenderGuardRailVertices(); } /// /// Use shadow /// public void UseShadow() { // Receive shadow on the guard rails RenderGuardRailVertices(); } } }