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> /// 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, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic); 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, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic); railIb.SetData(indices); #endregion }
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 SpeedyRacer 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(2.0f) *//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(2.0f * points[num].roadWidth) *//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 4, generate again if (randomObjNum >= 4) 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> /// 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); }