/// <summary> /// Add all models /// </summary> /// <param name="landscape">Landscape</param> /// <param name="parentMatrix">Parent matrix</param> public void AddAllModels(Landscape landscape, Matrix parentMatrix) { // Just add all models in our combi foreach (CombiObject obj in objects) landscape.AddObjectToRender(obj.modelName, obj.matrix * parentMatrix, false); }
/// <summary> /// Create guard rail /// </summary> /// <param name="points">Points of the road itself</param> /// <param name="mode">Mode, left or right</param> /// <param name="landscape">Landscape</param> public GuardRail(List<TrackVertex> points, Modes mode, Landscape landscape) { #region Generate guardrail points // 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; } // if (mode) else { railPoints[num] = points[pointNum].RightTrackVertex; // Move it a little inside the road railPoints[num].pos -= railPoints[num].right * InsideRoadDistance; } // else } // for (num) #endregion #region Generate vertex buffer 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); } // for (int) // 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 ps1.1 // to save performance (few thousand objects per track less) BaseGame.CanUsePS20 && 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; } // if (lastHolderGap) // 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; } // for (num) // Create the vertex buffer from our vertices. railVb = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), railVertices.Length, BufferUsage.WriteOnly); railVb.SetData(railVertices); #endregion #region GenerateIndexBuffer // 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]; } // for (int) vertexIndex += GuardRailVertices.Length; } // for (num) // Create the index buffer from our indices. railIb = new IndexBuffer( BaseGame.Device, typeof(int), indices.Length, BufferUsage.WriteOnly); railIb.SetData(indices); #endregion }
/// <summary> /// Create track columns /// </summary> /// <param name="points">Points</param> /// <param name="landscape">Landscape for getting the ground height</param> public TrackColumns(List<TrackVertex> points, Landscape landscape) { if (landscape == null) return; #region Find out column positions float lastColumnsDistance = ColumnsDistance; List<Matrix> columnPointSpacesTop = new List<Matrix>(); List<Matrix> columnPointSpacesBottom = new List<Matrix>(); 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; } #endregion #region Generate vertex buffer 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); #endregion #region GenerateIndexBuffer // 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); #endregion }
private void GenerateTunnelsAndLandscapeObjects( List<TrackData.RoadHelper> roadHelpers, List<TrackData.NeutralObject> neutralObjects, Landscape landscape) { #region Find out where the tunnels are // Go through all tunnel helpers along the road, // everything close enough (<25) is interessting for us. int helperStartedNum = -1; TrackData.RoadHelper.HelperType remType = TrackData.RoadHelper.HelperType.Reset; for (int num = 0; num < points.Count; num++) { Vector3 pos = points[num].pos; foreach (TrackData.RoadHelper roadHelper in roadHelpers) { float dist = Vector3.Distance(roadHelper.pos, pos); if (dist < 25.0f) { if (helperStartedNum >= 0) { helperPositions.Add(new RoadHelperPosition( remType, helperStartedNum, num)); // Reset? if (roadHelper.type == TrackData.RoadHelper.HelperType.Reset) helperStartedNum = -1; else { // Start new part helperStartedNum = num; remType = roadHelper.type; } // else } // if (helperStartedNum) else { helperStartedNum = num; remType = roadHelper.type; } // else // Remove this roadHelper (don't use it again)! roadHelpers.Remove(roadHelper); break; } // if (dist) } // foreach (roadHelper) } // for (num) // Still a helper open? Then close it close before the end! if (helperStartedNum > 0) helperPositions.Add(new RoadHelperPosition( remType, helperStartedNum, points.Count - 3)); #endregion #region Copy over neutral objects for landscape rendering if (landscape != null) { for (int num = 0; num < neutralObjects.Count; num++) { TrackData.NeutralObject obj = neutralObjects[num]; landscape.AddObjectToRender(obj.modelName, obj.matrix, false); } // for (num) } // if (landscape) #endregion }
/// <summary> /// Initializes and loads some content, previously referred to as the /// "car stuff". /// </summary> private void LoadResources() { LoadEvent("Models...", null); // Load models carModel = new Model("Car"); carSelectionPlate = new Model("CarSelectionPlate"); LoadEvent("Landscape...", null); // Load landscape landscape = new Landscape(Level.Beginner); LoadEvent("Textures...", null); // Load textures, first one is grabbed from the imported one through // the car.x model, the other two are loaded seperately. carTextures = new Texture[3]; carTextures[0] = new Texture("RacerCar"); carTextures[1] = new Texture("RacerCar2"); carTextures[2] = new Texture("RacerCar3"); colorSelectionTexture = new Texture("ColorSelection"); brakeTrackMaterial = new Material("track"); LoadEvent("All systems go!", null); Thread.Sleep(1000); }
/// <summary> /// Load /// </summary> /// <param name="inputPoints">Input points</param> /// <param name="widthHelpers">Width helpers</param> /// <param name="roadHelpers">Road helpers</param> /// <param name="neutralObjects">Neutral objects</param> /// <param name="landscape">Landscape</param> protected void Load(Vector3[] inputPoints, List<TrackData.WidthHelper> widthHelpers, List<TrackData.RoadHelper> roadHelpers, List<TrackData.NeutralObject> neutralObjects, Landscape landscape) { #region Kill all previously loaded data points.Clear(); helperPositions.Clear(); // Kill all loaded objects if (landscape != null) landscape.KillAllLoadedObjects(); #endregion #region Make sure we got valid data if (inputPoints == null || inputPoints.Length < 3) throw new ArgumentException("inputPoints is invalid, we need at "+ "least 3 valid input points to generate a TrackLine."); #endregion #region Check if all points are ABOVE the landscape if (landscape != null) { // Go through all spline points for (int num = 0; num < inputPoints.Length; num++) { // Get landscape height here float landscapeHeight = landscape.GetMapHeight( inputPoints[num].X, inputPoints[num].Y) + // add little to fix ground errors MinimumLandscapeDistance * 2.25f; // And make sure we are always above it! if (inputPoints[num].Z < landscapeHeight) inputPoints[num].Z = landscapeHeight; } // for (num) // Second pass, check 24 interpolation points between all inputPoints for (int num = 0; num < inputPoints.Length; num++) for (int iter = 1; iter < 25; iter++) { float iterPercent = iter / 25.0f; float iterHeight = inputPoints[num].Z * (1 - iterPercent) + inputPoints[(num + 1) % inputPoints.Length].Z * iterPercent; // Check 2x2 points (in all directions) to make sure // we don't go through the landscape at the sides for (int x = 0; x < 2; x++) for (int y = 0; y < 2; y++) { // Also get height at middle to next pos float landscapeHeight = landscape.GetMapHeight( -5.0f + 10.0f * x + inputPoints[num].X * (1 - iterPercent) + inputPoints[(num + 1) % inputPoints.Length].X * iterPercent, -5.0f + 10.0f * y + inputPoints[num].Y * (1 - iterPercent) + inputPoints[(num + 1) % inputPoints.Length].Y * iterPercent)+ // add little to fix ground errors MinimumLandscapeDistance * 1.6f;// 1.5f;//1.25f; // Increase both positions if this point is under the landscape if (iterHeight < landscapeHeight) { float increaseHeight = landscapeHeight - iterHeight; inputPoints[num].Z += increaseHeight; inputPoints[(num + 1) % inputPoints.Length].Z += increaseHeight; } // if (iterHeight) } // for } // for for (iter) } // if (landscape) //Log.Write("Input points: " + StringHelper.WriteArrayData(inputPoints)); #endregion #region Search for any loopings indicated by 2 points above each other // Go through all spline points (ignore first and last 3, this // makes it easier to remove points and add new ones). for (int num = 1; num < inputPoints.Length-3; num++) { // X/Y distance has to be 4 times smaller than Z distance Vector3 distVec = inputPoints[num + 1] - inputPoints[num]; float xyDist = (float)Math.Sqrt( distVec.X*distVec.X+distVec.Y*distVec.Y); float zDist = Math.Abs(distVec.Z); // Also check if next point is down again. Vector3 distVec2 = inputPoints[num + 2] - inputPoints[num + 1]; if (zDist / 2 > xyDist && Math.Abs(distVec.Z + distVec2.Z) < zDist / 2) { // Find out which direction we are going Vector3 dir = inputPoints[num] - inputPoints[num - 1]; dir.Normalize(); Vector3 upVec = new Vector3(0, 0, 1); Vector3 rightVec = Vector3.Cross(dir, upVec); // Matrix build helper matrix to rotate our looping points Matrix rotMatrix = new Matrix( rightVec.X, rightVec.Y, rightVec.Z, 0, dir.X, dir.Y, dir.Z, 0, upVec.X, upVec.Y, upVec.Z, 0, 0, 0, 0, 1); // Ok do a looping with zDist as height. // Start with the current point, loop around and end with the // point after the looping. We will remove the current and the // next 2 points, but add 9 new points instead for our smooth loop. // See LoopingPoints for the looping itself. Vector3 startLoopPos = inputPoints[num]; Vector3 endLoopPos = inputPoints[num + 2]; // Insert 7 new points (9 new points, but we reuse // start, middle and end points which are num, num+1 and num+2, // plus an additional point after the looping to keep the road // straight!) Vector3[] remInputPoints = (Vector3[])inputPoints.Clone(); inputPoints = new Vector3[inputPoints.Length + 7]; // Copy everything over for (int copyNum = 0; copyNum < remInputPoints.Length; copyNum++) if (copyNum < num) inputPoints[copyNum] = remInputPoints[copyNum]; else inputPoints[copyNum + 7] = remInputPoints[copyNum]; // Ok, now we can add our loop for (int loopNum = 0; loopNum < LoopingPoints.Length; loopNum++) { // Interpolate between start and end pos to land at the end pos! float loopPercent = loopNum / (float)(LoopingPoints.Length - 1); inputPoints[num + loopNum] = startLoopPos * (1 - loopPercent) + endLoopPos * loopPercent + zDist * Vector3.Transform(LoopingPoints[loopNum], rotMatrix); } // for (loopNum) // Add extra point to keep the road straight Vector3 newRoadDir = inputPoints[num + 10] - inputPoints[num + 8]; // Don't go more than zDist * 2 units away! if (newRoadDir.Length() > zDist * 2) { newRoadDir.Normalize(); newRoadDir = newRoadDir * zDist; inputPoints[num + 9] = inputPoints[num + 8] + newRoadDir; } // if (newRoadDir.Length) else // Just add an interpolation point inputPoints[num + 9] = (inputPoints[num + 8] + inputPoints[num + 10])/2.0f; // Advance 10 points until we check for the next loop num += 10; // That's it, good work everyone ^^ } // if (zDist) } // for (num) #endregion #region Generate all points with help of catmull rom splines // Generate all points with help of catmull rom splines for (int num = 0; num < inputPoints.Length; num++) { // Get the 4 required points for the catmull rom spline Vector3 p1 = inputPoints[num-1 < 0 ? inputPoints.Length-1 : num-1]; Vector3 p2 = inputPoints[num]; Vector3 p3 = inputPoints[(num + 1) % inputPoints.Length]; Vector3 p4 = inputPoints[(num + 2) % inputPoints.Length]; // Calculate number of iterations we use here based // on the distance of the 2 points we generate new points from. float distance = Vector3.Distance(p2, p3); int numberOfIterations = (int)(NumberOfIterationsPer100Meters * (distance / 100.0f)); if (numberOfIterations <= 0) numberOfIterations = 1; Vector3 lastPos = p1; for (int iter = 0; iter < numberOfIterations; iter++) { TrackVertex newVertex = new TrackVertex( Vector3.CatmullRom(p1, p2, p3, p4, iter / (float)numberOfIterations)); /*sucks, too bumpy! // Warning: Can make the road too bumpy, for that reason we smooth! // Get landscape height here if (landscape != null) { float landscapeHeight = landscape.GetMapHeight( newVertex.pos.X, newVertex.pos.Y) + MinimumLandscapeDistance; // Check if point is ABOVE the landscape! if (newVertex.pos.Z < landscapeHeight) newVertex.pos.Z = //landscapeHeight; lastPos.Z * 0.9f + newVertex.pos.Z * 0.1f; lastPos = newVertex.pos; if (lastPos.Z < landscapeHeight) lastPos.Z = landscapeHeight; } // if (landscape) else //*/ lastPos = newVertex.pos; points.Add(newVertex); } // for (iter) } // for (num) #endregion #region Generate up vectors, very important for our road building // Pre up vectors are used to first generate all optimal up vectors // for the track, but this is not useful for driving because we need // the road to point up always except for loopings. List<Vector3> preUpVectors = new List<Vector3>(); // Now generate all up vectors, first pass does optimal up vectors. Vector3 defaultUpVec = new Vector3(0, 0, 1); Vector3 lastUpVec = defaultUpVec; for (int num = 0; num < points.Count; num++) { // Get direction we are driving in at this point, // interpolate with help of last and next points. Vector3 dir = points[(num + 1) % points.Count].pos - points[num - 1 < 0 ? points.Count - 1 : num - 1].pos; dir.Normalize(); // Now calculate the optimal up vector for this point Vector3 middlePoint = (points[(num + 1) % points.Count].pos + points[num - 1 < 0 ? points.Count - 1 : num - 1].pos) / 2.0f; Vector3 optimalUpVector = middlePoint - points[num].pos; if (optimalUpVector.Length() < 0.0001f) optimalUpVector = lastUpVec; optimalUpVector.Normalize(); // Store the optimalUpVectors in the preUpVectors list preUpVectors.Add(optimalUpVector); // Also save dir vector points[num].dir = dir; // And remember the last upVec in case the road is going straight ahead lastUpVec = optimalUpVector; } // for (num) // Interpolate the first up vector for a smoother road at the start pos preUpVectors[0] = preUpVectors[preUpVectors.Count - 1] + preUpVectors[1]; preUpVectors[0].Normalize(); #endregion #region Interpolate the up vectors and also add the dir and right vectors // Second pass, interpolated precalced values and apply our logic :) //preUpVectors[0] = lastUpVec = Vector3.Lerp(defaultUpVec, preUpVectors[0], 1.5f * CurveFactor * UpFactorCorrector); //lastUpVec = preUpVectors[0]; Vector3 lastUpVecUnmodified = lastUpVec;// defaultUpVec; Vector3 lastRightVec = Vector3.Zero; for (int num = 0; num < points.Count; num++) { // Grab dir vector (could be calculated here too) Vector3 dir = points[num].dir; // First of all interpolate the preUpVectors Vector3 upVec = //single input: preUpVectors[num]; Vector3.Zero; for (int smoothNum = -NumberOfUpSmoothValues / 2; smoothNum <= NumberOfUpSmoothValues / 2; smoothNum++) upVec += preUpVectors[(num + points.Count + smoothNum) % points.Count]; upVec.Normalize(); // Find out if this road piece is upside down and if we are // moving up or down. This is VERY important for catching loopings. bool upsideDown = upVec.Z < -0.25f && lastUpVecUnmodified.Z < -0.05f; bool movingUp = dir.Z > 0.75f; bool movingDown = dir.Z < -0.75f; //float changeAngle2 = // GetAngleBetweenVectors(lastUpVec, upVec); //if (num < 100) // Log.Write("changeAngel2=" + changeAngle2); // Mix in the last vector to make curves weaker upVec = Vector3.Lerp(lastUpVec, upVec, CurveFactor); upVec.Normalize(); // Store the last value to check for loopings. lastUpVecUnmodified = upVec; // Don't mix in default up if we head up or are upside down! // Its very useful to know if we move up or down to fix the // problematic areas at loopings by pointing stuff correct right away. if (movingUp) lastUpVec = Vector3.Lerp(upVec, -defaultUpVec, UpFactorCorrector); else if (movingDown) lastUpVec = Vector3.Lerp(upVec, defaultUpVec, UpFactorCorrector); else if (upsideDown) lastUpVec = Vector3.Lerp(upVec, -defaultUpVec, UpFactorCorrector); else lastUpVec = Vector3.Lerp(upVec, defaultUpVec, UpFactorCorrector); // If we are very close to the ground, make the road point up more! if (//upsideDown == false && landscape != null) { // Get landscape height here float landscapeHeight = landscape.GetMapHeight( points[num].pos.X, points[num].pos.Y); // If point is close to the landscape, let everything point up more if (points[num].pos.Z - landscapeHeight < MinimumLandscapeDistance * 4) lastUpVec = Vector3.Lerp(upVec, defaultUpVec, 1.75f * UpFactorCorrector); } // if (upsideDown == false &&) // And finally calculate rightVectors with just a cross product. // Used to render the track later. Vector3 rightVec = Vector3.Cross(dir, upVec); rightVec.Normalize(); points[num].right = rightVec; //*tst // Recalculate up vector with help of right and dir. // This makes the up vector to always point up 90 degrees. upVec = Vector3.Cross(rightVec, dir); upVec.Normalize(); points[num].up = upVec; //*/ //// Make sure we never rotate the road more than a few degrees //if (lastRightVec.Length() > 0) //{ // float changeAngle = // GetAngleBetweenVectors(lastRightVec, rightVec); // if (num < 100) // Log.Write("changeAngel=" + changeAngle); //} // if (lastRightVec.Length) //// Remember right vec for comparison in the next frame. //lastRightVec = rightVec; } // for (num) #endregion #region Smooth up vectors! lastUpVec = points[0].up; for (int num = 0; num < points.Count; num++) preUpVectors[num] = points[num].up; for (int num = 0; num < points.Count; num++) { // Interpolate up vectors again Vector3 upVec = Vector3.Zero; for (int smoothNum = -NumberOfUpSmoothValues; smoothNum <= NumberOfUpSmoothValues; smoothNum++) { upVec += preUpVectors[(num + points.Count + smoothNum) % points.Count]; } // for (smoothNum) upVec.Normalize(); points[num].up = upVec; // Also rebuild right vector Vector3 dir = points[num].dir; points[num].right = Vector3.Cross(dir, upVec); /*suxx // Grab dir and up vector Vector3 dir = points[num].dir; Vector3 upVec = points[num].up; /*suxx // Compare with previous up vector float changeAngle = GetAngleBetweenVectors(lastUpVec, upVec); /*tst bool upsideDown = upVec.Z < -0.25f && lastUpVecUnmodified.Z < -0.05f; bool movingUp = dir.Z > 0.75f; bool movingDown = dir.Z < -0.75f; lastUpVecUnmodified = upVec; if (Math.Abs(changeAngle) > 0.02f && upsideDown == false && movingUp == false && movingDown == false) { points[num].up = Vector3.SmoothStep(lastUpVec, upVec, 0.33f);//.25f); // Also rebuild right vector points[num].right = Vector3.Cross(dir, points[num].up); } // if (Math.Abs) */ /*suxx //if (Math.Abs(changeAngle) > 0.02f) { points[num].up = Vector3.SmoothStep(lastUpVec, upVec, 0.05f);//.25f); // Also rebuild right vector points[num].right = Vector3.Cross(dir, points[num].up); } // if (Math.Abs) lastUpVec = upVec; */ //if (num < 100) // Log.Write("changeAngel=" + changeAngle); } // for (num) #endregion AdjustRoadWidths(widthHelpers); GenerateUTextureCoordinates(); GenerateTunnelsAndLandscapeObjects( roadHelpers, neutralObjects, landscape); }
/// <summary> /// Load /// </summary> /// <param name="colladaTrack">Collada track</param> /// <param name="landscape">Landscape</param> protected void Load(TrackData colladaTrack, Landscape landscape) { Load(colladaTrack.TrackPoints.ToArray(), colladaTrack.WidthHelpers, colladaTrack.RoadHelpers, colladaTrack.NeutralsObjects, landscape); }
/// <summary> /// Create track line /// </summary> /// <param name="inputPoints">Input Points, will form a closed curve /// </param> public TrackLine(Vector3[] inputPoints, List<TrackData.WidthHelper> widthHelpers, List<TrackData.RoadHelper> roadHelpers, List<TrackData.NeutralObject> neutralObjects, Landscape landscape) { Load(inputPoints, widthHelpers, roadHelpers, neutralObjects, landscape); }
/// <summary> /// Create track line /// </summary> /// <param name="inputPointsFromColladaTrack"> /// Input points from collada track</param> public TrackLine(TrackData inputPointsFromColladaTrack, Landscape landscape) : this(inputPointsFromColladaTrack.TrackPoints.ToArray(), inputPointsFromColladaTrack.WidthHelpers, inputPointsFromColladaTrack.RoadHelpers, inputPointsFromColladaTrack.NeutralsObjects, landscape) { }
private void GenerateObjectsForTrack(Landscape landscape) { #region Generate palms and laterns for the road // Auto generate palms and laterns at the side of our road. float lastGap = 0;//PalmAndLaternGap; int generatedNum = 0; for (int num = 0; num < points.Count; num++) { bool palms = false; bool laterns = false; // Check if there are any palms or laterns here foreach (RoadHelperPosition helper in helperPositions) if (num >= helper.startNum && num <= helper.endNum) { if (helper.type == TrackData.RoadHelper.HelperType.Palms) palms = true; else if (helper.type == TrackData.RoadHelper.HelperType.Laterns) laterns = true; } // foreach if (helper.startNum) // No palms or laterns here? if (palms == false && laterns == false) // Then skip continue; // Distance of the current position to the next position float distance = Vector3.Distance( points[(num + 1) % points.Count].pos, points[num].pos); // Have we reach or go over the holder gap ? if (lastGap - distance <= 0) { // The unit vectors for our local point space Vector3 right = points[num].right; Vector3 dir = points[num].dir; Vector3 up = points[num].up; // Find out if this is a looping bool upsideDown = up.Z < +0.05f; bool movingUp = dir.Z > 0.65f; bool movingDown = dir.Z < -0.65f; if (upsideDown || movingUp || movingDown) // Skip generation here! continue; // Create the coordinate system for the current point by the 3 unit // vectors. Matrix pointSpace = Matrix.Identity; pointSpace.Right = right; pointSpace.Up = dir; pointSpace.Forward = -up; // 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 objPoint = Vector3.CatmullRom(p1, p2, p3, p4, lastGap / distance); generatedNum++; // Store the calculated render matrix for this holder pile object if (landscape != null) { if (palms) { // Check height, skip palm generation if height is more than 11m if (objPoint.Z - landscape.GetMapHeight(objPoint.X, objPoint.Y) < 11)//15) { int randomNum = RandomHelper.GetRandomInt(4); // Less propability for small palm if (randomNum == 3) randomNum = RandomHelper.GetRandomInt(4); landscape.AddObjectToRender( // Random palms randomNum == 0 ? "AlphaPalm" : randomNum == 1 ? "AlphaPalm2" : randomNum == 2 ? "AlphaPalm3" : "AlphaPalmSmall", // Scale them up a little Matrix.CreateScale(1.25f) * // Randomly rotate palms Matrix.CreateRotationZ( RandomHelper.GetRandomFloat(0, MathHelper.Pi * 2)) * // Put left/right Matrix.CreateTranslation(right * (generatedNum % 2 == 0 ? 0.6f : -0.6f) * points[num].roadWidth * TrackVertex.RoadWidthScale) * // Put below the landscape to make it stick to the ground Matrix.CreateTranslation(new Vector3(0, 0, -50)) * // the ordinary transformation to the current point space //unused: pointSpace * // And finally we calculate to correct position where the palm // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); } // if } // if (palms) else { landscape.AddObjectToRender( // Random palms or laterns? "Laterne", // Rotate laterns fixed left/right Matrix.CreateRotationZ( generatedNum % 2 == 0 ? MathHelper.Pi : 0.0f) * // Put left/right Matrix.CreateTranslation(new Vector3( (generatedNum % 2 == 0 ? 0.5f : -0.5f) * points[num].roadWidth * TrackVertex.RoadWidthScale - 0.35f, 0, -0.2f)) * // the ordinary transformation to the current point space pointSpace * // At last we calculate to correct position where the latern // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); } // else } // if (landscape) // We have just set a pile, the next pile will be set after // reaching the next holder gap. lastGap += PalmAndLaternGap; } // if (lastHolderGap) // 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. lastGap -= distance; } // for (num) #endregion #region Generate signs and checkpoints // Add the goal and start light models always! if (landscape != null) { Vector3 startRight = points[0].right; Vector3 startDir = points[0].dir; Vector3 startUp = points[0].up; Matrix startPointSpace = Matrix.Identity; startPointSpace.Right = startRight; startPointSpace.Up = startDir; startPointSpace.Forward = -startUp; landscape.AddObjectToRender( // Use RacingGame banners "Banner6", // Scale them up to fit on the road Matrix.CreateScale(points[0].roadWidth) * // Scale up 1.1, but compensate 1.2f done in landscape.AddObject Matrix.CreateScale(1.051f) * Matrix.CreateTranslation(new Vector3(0, -5.1f, 0)) * // the ordinary transformation to the current point space startPointSpace * // Add the correct position where the goal is Matrix.CreateTranslation(points[0].pos), // Enable this for shadow map generation true); landscape.AddObjectToRender( // All 3 modes are handled and updated in BasePlayer class "StartLight3", Matrix.CreateScale(1.1f) * // Put startlight 6 meters away, and on the right road side! Matrix.CreateTranslation(new Vector3( points[0].roadWidth * TrackVertex.RoadWidthScale * 0.50f - 0.3f, 6, -0.2f)) * // the ordinary transformation to the current point space startPointSpace * // Add the correct position where the goal is Matrix.CreateTranslation(points[0].pos), // Enable this for shadow map generation true); } // if (landscape) // Make sure we don't reuse any of the old checkpoint positions. checkpointSegmentPositions.Clear(); // Auto generate checkpoints every 500 meters. lastGap = CheckpointGap; float signGap = SignGap; // Don't add another one near the end! for (int num = 0; num < points.Count - 24; num++) { // Distance of the current position to the next position float distance = Vector3.Distance( points[(num + 1) % points.Count].pos, points[num].pos); // The unit vectors for our local point space Vector3 right = points[num].right; Vector3 dir = points[num].dir; Vector3 up = points[num].up; // Find out if this is a looping bool upsideDown = up.Z < +0.05f; bool movingUp = dir.Z > 0.65f; bool movingDown = dir.Z < -0.65f; if (upsideDown || movingUp || movingDown) // Skip generation here! continue; // Create the coordinate system for the current point by the 3 unit // vectors. Matrix pointSpace = Matrix.Identity; pointSpace.Right = right; pointSpace.Up = dir; pointSpace.Forward = -up; // 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; // Have we reach or go over the holder gap ? if (lastGap - distance <= 0 && landscape != null) { Vector3 objPoint = Vector3.CatmullRom(p1, p2, p3, p4, lastGap / distance); // Store the calculated render matrix for this holder pile object int randomNum = RandomHelper.GetRandomInt(6); landscape.AddObjectToRender( // Random banners randomNum == 0 ? "Banner" : randomNum == 1 ? "Banner2" : randomNum == 2 ? "Banner3" : randomNum == 3 ? "Banner4" : randomNum == 4 ? "Banner5" : "Banner6", // Scale them up to fit on the road Matrix.CreateScale(points[num].roadWidth) * Matrix.CreateTranslation(new Vector3(0, 0, -0.1f)) * // the ordinary transformation to the current point space pointSpace * // And finally we calculate to correct position where the palm // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); // Remember this segment for easier checking later. checkpointSegmentPositions.Add(num); // We have just set a pile, the next pile will be set after // reaching the next holder gap. lastGap += CheckpointGap; } // if (lastHolderGap) else if (signGap - distance <= 0 && num >= 25 && landscape != null) { Vector3 objPoint = Vector3.CatmullRom(p1, p2, p3, p4, signGap / distance); // Find out how curvy this point is by going back 25 points. Vector3 backPos = points[(num - 25) % points.Count].pos; // Calculate virtualBackPos as if the road were straight bool loopingAhead = points[(num + 60) % points.Count].up.Z < 0.15f; // Calc angle Vector3 angleVec = Vector3.Normalize(backPos - points[num].pos); float roadAngle = Vector3Helper.GetAngleBetweenVectors( angleVec, Vector3.Normalize(-points[num].dir)); // If road goes to the left, use negative angle value! if (Vector3.Distance(points[num].right, angleVec) < Vector3.Distance(-points[num].right, angleVec)) roadAngle = -roadAngle; // Now compare, if the backPos is more than 12 meters down, // add a warning sign. if (loopingAhead)//backPos.Z > virtualBackPos.Z + 20) { landscape.AddObjectToRender( "SignWarning", //Matrix.CreateRotationZ(-MathHelper.Pi/2.0f) * // Put it on the right side Matrix.CreateTranslation(new Vector3( points[num].roadWidth * TrackVertex.RoadWidthScale * 0.5f - 0.1f, 0, -0.25f)) * // the ordinary transformation to the current point space pointSpace * // And finally we calculate to correct position where the obj // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); } // if (backPos.Z) // Else check if the angle less than -24 degrees (pi/7.5) else if (roadAngle < -MathHelper.Pi / 7.5f) { // Show right road sign landscape.AddObjectToRender( "SignCurveRight", Matrix.CreateRotationZ(MathHelper.Pi / 2.0f) * // Put it on the left side Matrix.CreateTranslation(new Vector3( -points[num].roadWidth * TrackVertex.RoadWidthScale * 0.5f - 0.15f, 0, -0.25f)) * // the ordinary transformation to the current point space pointSpace * // And finally we calculate to correct position where the obj // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); } // else if // Same for other side else if (roadAngle > MathHelper.Pi / 7.5f) { // Show right road sign landscape.AddObjectToRender( "SignCurveLeft", Matrix.CreateRotationZ(-MathHelper.Pi / 2.0f) * // Put it on the right side Matrix.CreateTranslation(new Vector3( points[num].roadWidth * TrackVertex.RoadWidthScale * 0.5f - 0.15f, 0, -0.25f)) * // the ordinary transformation to the current point space pointSpace * // And finally we calculate to correct position where the obj // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); } // else if // Also generate banner signs if roadAngle is at least 18 degrees else if (roadAngle < -MathHelper.Pi / 10.0f || roadAngle > MathHelper.Pi / 10.0f || // Randomly generate sign RandomHelper.GetRandomInt(9) == 4) { // Also mix in random curve signs, this is still a curve int rndValue = RandomHelper.GetRandomInt(3); // Randomize again if not that curvy here if (rndValue == 0 && Math.Abs(roadAngle) < MathHelper.Pi / 24) rndValue = RandomHelper.GetRandomInt(3); else if (Math.Abs(roadAngle) < MathHelper.Pi / 20 && RandomHelper.GetRandomInt(2) == 1) roadAngle *= -1; // Show right road sign landscape.AddObjectToRender( rndValue == 0 ? (roadAngle > 0 ? "SignCurveLeft" : "SignCurveRight") : (rndValue == 1 ? "Sign" : "Sign2"), Matrix.CreateRotationZ( (roadAngle > 0 ? -1 : 1) * MathHelper.Pi / 2.0f) * // Put it on the left side Matrix.CreateTranslation(new Vector3( (roadAngle > 0 ? 1 : -1) * points[num].roadWidth * TrackVertex.RoadWidthScale * 0.5f - (rndValue == 0 ? 0.15f : 0.005f), 0, -0.25f)) * // the ordinary transformation to the current point space pointSpace * // And finally we calculate to correct position where the obj // reaches exactly the holder gap Matrix.CreateTranslation(objPoint), // Enable this for shadow map generation true); } // else if // We have just set a sign (or not), check for next sign after gap. signGap += SignGap; } // else if // 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. lastGap -= distance; signGap -= distance; } // for (num) #endregion #region Add random landscape objects to fill our level up // Randomly generate, but don't collide with existing objects // or the track! for (int num = 0; num < points.Count; num += 2) { if (landscape != null) { // Get landscape height here float landscapeHeight = landscape.GetMapHeight(points[num].pos.X, points[num].pos.Y); // Skip object generation at great heights! if (points[num].pos.Z - landscapeHeight > 60.0f) continue; } // if // The unit vectors for our local point space Vector3 right = points[num].right; Vector3 dir = points[num].dir; Vector3 up = points[num].up; // Find out if this is a looping bool upsideDown = up.Z < +0.05f; bool movingUp = dir.Z > 0.65f; bool movingDown = dir.Z < -0.65f; if (upsideDown || movingUp || movingDown) // Skip generation here! continue; // Reduce number of landscape objects dramatically if not using // highest game settings. int randomMaxPropability = BaseGame.CanUsePS30 ? 2 ://3 : //6 : BaseGame.CanUsePS20 ? 5 : //9 : 9;//13; // Reduce to half as much objects if high detail is off. if (BaseGame.HighDetail == false) randomMaxPropability *= 2; // Generate stuff in 20% of the cases if (RandomHelper.GetRandomInt(randomMaxPropability) == 0 && landscape != null) { // Get random name int randomObjNum = RandomHelper.GetRandomInt( landscape.autoGenerationNames.Length); // If above 6, generate again if (randomObjNum >= 6) randomObjNum = RandomHelper.GetRandomInt( landscape.autoGenerationNames.Length); // Don't generate so many casinos if (randomObjNum == landscape.autoGenerationNames.Length-1 && RandomHelper.GetRandomInt(3) < 2) randomObjNum = RandomHelper.GetRandomInt( landscape.autoGenerationNames.Length); // Ok, generate float distance = RandomHelper.GetRandomFloat(26, 88);//33, 88); // For casinos make sure the object is far enough away. if (randomObjNum == landscape.autoGenerationNames.Length - 1) distance += 20; bool side = RandomHelper.GetRandomInt(2) == 0; float rotation = RandomHelper.GetRandomFloat(0, MathHelper.Pi * 2); landscape.AddObjectToRender( landscape.autoGenerationNames[randomObjNum], rotation, points[num].pos, points[num].right, distance * (side ? 1 : -1)); } // if (RandomHelper.GetRandomInt) } // for (num) #endregion }
/// <summary> /// Dispose /// </summary> /// <param name="someObject">Some object</param> public static void Dispose(ref Landscape someObject) { if (someObject != null) someObject.Dispose(); someObject = null; }
/// <summary> /// Reload /// </summary> /// <param name="setTrackName">Track name</param> /// <param name="landscape">Landscape</param> public void Reload(string setTrackName, Landscape landscape) { // Do we need to load the base track again? // Always reload! Else we might mess up the checkpoints, etc. base.Load(TrackData.Load(setTrackName), landscape); GenerateVerticesAndObjects(landscape); }
/// <summary> /// Create track /// </summary> /// <param name="setTrackName">Track name to load</param> /// <param name="landscape">Landscape to check if we are above it</param> public Track(string setTrackName, Landscape landscape) : base(TrackData.Load(setTrackName), landscape) { GenerateVerticesAndObjects(landscape); }
/// <summary> /// Generate vertices and objects /// </summary> private void GenerateVerticesAndObjects(Landscape landscape) { #region Generate the road vertices // Each road segment gets 5 points: // left, left middle, middle, right middle, right. // The reason for this is that we would bad triangle errors if the // road gets wider and wider. This happens because we need to render // quad, but we can only render triangles, which often have different // orientations, which makes the road very bumpy. This still happens // with 8 polygons instead of 2, but it is much better this way. // Another trick is not to do so much iterations in TrackLine, which // causes this problem. Better to have a not so round track, but at // least the road up/down itself is smooth. // The last point is duplicated (see TrackLine) because we have 2 sets // of texture coordinates for it (begin block, end block). // So for the index buffer we only use points.Count-1 blocks. roadVertices = new TangentVertex[points.Count * 5]; // Current texture coordinate for the roadway (in direction of movement) for (int num = 0; num < points.Count; num++) { // Get vertices with help of the properties in the TrackVertex class. // For the road itself we only need vertices for the left and right // side, which are vertex number 0 and 1. roadVertices[num * 5 + 0] = points[num].RightTangentVertex; roadVertices[num * 5 + 1] = points[num].MiddleRightTangentVertex; roadVertices[num * 5 + 2] = points[num].MiddleTangentVertex; roadVertices[num * 5 + 3] = points[num].MiddleLeftTangentVertex; roadVertices[num * 5 + 4] = points[num].LeftTangentVertex; } // for (num) roadVb = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), roadVertices.Length, BufferUsage.WriteOnly); roadVb.SetData(roadVertices); // Also calculate all indices, we have 8 polygons for each segment with // 3 vertices each. We got 1 segment less than points because the // last point is duplicated (different tex coords). int[] indices = new int[(points.Count - 1) * 8 * 3]; int vertexIndex = 0; for (int num = 0; num < points.Count - 1; num++) { // We only use 3 vertices (and the next 3 vertices), // but we have to construct all 24 indices for our 4 polygons. for (int sideNum = 0; sideNum < 4; sideNum++) { // Each side needs 2 polygons. // 1. Polygon indices[num * 24 + 6 * sideNum + 0] = vertexIndex + sideNum; indices[num * 24 + 6 * sideNum + 1] = vertexIndex + 5 + 1 + sideNum; indices[num * 24 + 6 * sideNum + 2] = vertexIndex + 5 + sideNum; // 2. Polygon indices[num * 24 + 6 * sideNum + 3] = vertexIndex + 5 + 1 + sideNum; indices[num * 24 + 6 * sideNum + 4] = vertexIndex + sideNum; indices[num * 24 + 6 * sideNum + 5] = vertexIndex + 1 + sideNum; } // for (num) // Go to the next 5 vertices vertexIndex += 5; } // for (num) // Set road back index buffer roadIb = new IndexBuffer( BaseGame.Device, typeof(int), indices.Length, BufferUsage.WriteOnly); roadIb.SetData(indices); #endregion #region Generate the road back vertices // We need 4 vertices per cross-section edge of the road back hull roadBackVertices = new TangentVertex[points.Count * 4]; for (int num = 0; num < points.Count; num++) { // Left side of the road roadBackVertices[num * 4 + 0] = points[num].LeftTangentVertex; roadBackVertices[num * 4 + 0].uv = new Vector2( roadBackVertices[num * 4 + 0].U * RoadBackHullTextureWidthFactor, 0.0f); // Left lower side of the road roadBackVertices[num * 4 + 1] = points[num].BottomLeftSideTangentVertex; roadBackVertices[num * 4 + 1].uv = new Vector2( roadBackVertices[num * 4 + 0].U * RoadBackHullTextureWidthFactor, RoadBackSideTextureHeight); // Right lower side of the road roadBackVertices[num * 4 + 2] = points[num].BottomRightSideTangentVertex; roadBackVertices[num * 4 + 2].uv = new Vector2( roadBackVertices[num * 4 + 0].U * RoadBackHullTextureWidthFactor, 1.0f - RoadBackSideTextureHeight); // Right side of the road roadBackVertices[num * 4 + 3] = points[num].RightTangentVertex; roadBackVertices[num * 4 + 3].uv = new Vector2( roadBackVertices[num * 4 + 3].U * RoadBackHullTextureWidthFactor, 1.0f); } // for (num) // Set road back vertex buffer roadBackVb = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), roadBackVertices.Length, BufferUsage.WriteOnly); roadBackVb.SetData(roadBackVertices); // Also calculate all indices, we have 6 polygons for each segment with // 3 vertices each. We got 1 segment less than points because the // last point is duplicated (different tex coords). int[] backIndices = new int[(points.Count-1) * 6 * 3]; vertexIndex = 0; for (int num = 0; num < points.Count-1; num++) { // We only use 4 vertices (and the next 4 vertices), // but we have to construct all 18 indices for our 6 polygons. for (int sideNum = 0; sideNum < 3; sideNum++) { // Each side needs 2 polygons. // 1. Polygon backIndices[num * 18 + 6 * sideNum + 0] = vertexIndex + sideNum; backIndices[num * 18 + 6 * sideNum + 1] = vertexIndex + 5 + sideNum; backIndices[num * 18 + 6 * sideNum + 2] = vertexIndex + 4 + sideNum; // 2. Polygon backIndices[num * 18 + 6 * sideNum + 3] = vertexIndex + 5 + sideNum; backIndices[num * 18 + 6 * sideNum + 4] = vertexIndex + sideNum; backIndices[num * 18 + 6 * sideNum + 5] = vertexIndex + 1 + sideNum; } // for (num) // Go to the next 4 vertices vertexIndex += 4; } // for (num) // Set road back index buffer roadBackIb = new IndexBuffer( BaseGame.Device, typeof(int), backIndices.Length, BufferUsage.WriteOnly); roadBackIb.SetData(backIndices); #endregion #region Generate the road tunnel vertices // Only generate tunnels for the parts were we want to have tunnels for. int totalTunnelLength = 0; foreach (RoadHelperPosition tunnelPos in helperPositions) if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel) totalTunnelLength += 1+(tunnelPos.endNum - tunnelPos.startNum); // Lets use 4 vertices per segment, we could improve that later // by adding more vertices for a round tunnel. roadTunnelVertices = new TangentVertex[totalTunnelLength * 4]; vertexIndex = 0; foreach (RoadHelperPosition tunnelPos in helperPositions) if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel) for (int num = tunnelPos.startNum; num <= tunnelPos.endNum; num++) { // Left side of the road roadTunnelVertices[vertexIndex + 0] = points[num].LeftTangentVertex; roadTunnelVertices[vertexIndex + 0].uv = new Vector2( roadTunnelVertices[vertexIndex + 0].U * RoadTunnelTextureWidthFactor, 0.0f); // Left top side of the road roadTunnelVertices[vertexIndex + 1] = points[num].TunnelTopLeftSideTangentVertex; roadTunnelVertices[vertexIndex + 1].uv = new Vector2( roadTunnelVertices[vertexIndex + 1].U * RoadTunnelTextureWidthFactor, RoadTunnelSideTextureHeight); // Right top side of the road roadTunnelVertices[vertexIndex + 2] = points[num].TunnelTopRightSideTangentVertex; roadTunnelVertices[vertexIndex + 2].uv = new Vector2( roadTunnelVertices[vertexIndex + 2].U * RoadTunnelTextureWidthFactor, 1.0f - RoadTunnelSideTextureHeight); // Right side of the road roadTunnelVertices[vertexIndex + 3] = points[num].RightTangentVertex; roadTunnelVertices[vertexIndex + 3].uv = new Vector2( roadTunnelVertices[vertexIndex + 3].U * RoadTunnelTextureWidthFactor, 1.0f); // Adjust normals for the 2 lower points roadTunnelVertices[vertexIndex + 0].normal *= -1; roadTunnelVertices[vertexIndex + 3].normal *= -1; roadTunnelVertices[vertexIndex + 0].tangent *= -1; roadTunnelVertices[vertexIndex + 3].tangent *= -1; vertexIndex += 4; } // foreach for (num) // Set road back vertex buffer if (roadTunnelVertices.Length > 0) { roadTunnelVb = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), roadTunnelVertices.Length, BufferUsage.WriteOnly); roadTunnelVb.SetData(roadTunnelVertices); // Also calculate all indices, we have 6 polygons for each segment with // 3 vertices each. We got 1 segment less than points because the // last point is duplicated (different tex coords). int totalIndices = 0; foreach (RoadHelperPosition tunnelPos in helperPositions) if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel) totalIndices += (tunnelPos.endNum - tunnelPos.startNum); roadTunnelIndices = new int[totalIndices * 6 * 3]; vertexIndex = 0; int tunnelIndex = 0; foreach (RoadHelperPosition tunnelPos in helperPositions) if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel) { for (int num = tunnelPos.startNum; num < tunnelPos.endNum; num++) { // We only use 4 vertices (and the next 4 vertices), // but we have to construct all 18 indices for our 6 polygons. for (int sideNum = 0; sideNum < 3; sideNum++) { // Each side needs 2 polygons. // Note: This polygons are rendered with culling off because // we want to see the inside and outside of the tunnel. // 1. Polygon roadTunnelIndices[tunnelIndex + 0] = vertexIndex + sideNum; roadTunnelIndices[tunnelIndex + 2] = vertexIndex + 4 + sideNum; roadTunnelIndices[tunnelIndex + 1] = vertexIndex + 5 + sideNum; // 2. Polygon roadTunnelIndices[tunnelIndex + 3] = vertexIndex + 5 + sideNum; roadTunnelIndices[tunnelIndex + 5] = vertexIndex + 1 + sideNum; roadTunnelIndices[tunnelIndex + 4] = vertexIndex + sideNum; tunnelIndex += 6; } // for (sideNum) // Go to the next 4 vertices vertexIndex += 4; } // for (num) // Skip 4 vertices till the next tunnel vertexIndex += 4; } // foreach (tunnelPos) // Set road back index buffer roadTunnelIb = new IndexBuffer( BaseGame.Device, typeof(int), roadTunnelIndices.Length, BufferUsage.WriteOnly); roadTunnelIb.SetData(roadTunnelIndices); } // if #endregion #region Generate guard rails leftRail = new GuardRail(points, GuardRail.Modes.Left, landscape); rightRail = new GuardRail(points, GuardRail.Modes.Right, landscape); #endregion #region Generate columns columns = new TrackColumns(points, landscape); #endregion GenerateObjectsForTrack(landscape); }
/// <summary> /// Load /// </summary> /// <param name="trackData">track</param> /// <param name="landscape">Landscape</param> protected void Load(TrackData trackData, Landscape landscape) { Load(trackData.TrackPoints.ToArray(), trackData.WidthHelpers, trackData.RoadHelpers, trackData.NeutralsObjects, landscape); }
/// <summary> /// Load car stuff /// </summary> protected override void Initialize() { base.Initialize(); // Load models carModel = new Model("Car"); carSelectionPlate = new Model("CarSelectionPlate"); // Load landscape landscape = new Landscape(Level.Beginner); // Load textures, first one is grabbed from the imported one through // the car.x model, the other two are loaded seperately. carTextures = new Texture[3]; carTextures[0] = new Texture("RacerCar%0"); carTextures[1] = new Texture("RacerCar2"); carTextures[2] = new Texture("RacerCar3"); colorSelectionTexture = new Texture("ColorSelection"); brakeTrackMaterial = new Material("track"); //For testing: //Directly start the game and load a certain level, this code is // only used in the debug mode. Remove it when you are don with // testing! If you press Esc you will also quit the game and not // just end up in the menu again. //gameScreens.Clear(); //gameScreens.Push(new GameScreen()); }