/// <summary> /// Test car physics on a plane (xy) with some dummy guard rail for /// simple colision checking. /// </summary> public static void TestCarPhysicsOnPlaneWithGuardRails() { PlaneRenderer plane = null; // Use a simple object to simulate our guard rails we have in the game. Model guardRailModel = null; TestGame.Start("TestCarPhysicsOnPlaneWithGuardRails", delegate { plane = new PlaneRenderer(Vector3.Zero, new Plane(new Vector3(0, 0, 1), 0), new Material("CityGround", "CityGroundNormal"), 500.0f); //new Material("Road", "RoadNormal"), 500.0f); guardRailModel = new Model("RoadColumnSegment"); // Put car 10m above the ground to test gravity and ground plane! SpeedyRacerManager.Player.SetCarPosition( new Vector3(0, 0, 10), new Vector3(0, 1, 0), new Vector3(0, 0, 1)); // Make sure we are not in free camera mode and can control the car SpeedyRacerManager.Player.FreeCamera = false; }, delegate { // Test slow computers by slowing down the framerate with Ctrl if (Input.Keyboard.IsKeyDown(Keys.LeftControl)) Thread.Sleep(75); SpeedyRacerManager.Player.SetGroundPlaneAndGuardRails( new Vector3(0, 0, 0), new Vector3(0, 0, 1), // Use our guardrails, always the same in this test! new Vector3(-10, 0, 0), new Vector3(-10, 10, 0), new Vector3(+10, 0, 0), new Vector3(+10, 10, 0)); Matrix carMatrix = SpeedyRacerManager.Player.UpdateCarMatrixAndCamera(); // Generate shadows, just the car does shadows ShaderEffect.shadowMapping.GenerateShadows( delegate { SpeedyRacerManager.CarModel.GenerateShadow(carMatrix); }); // Render shadows (on both the plane and the car) ShaderEffect.shadowMapping.RenderShadows( delegate { SpeedyRacerManager.CarModel.UseShadow(carMatrix); plane.UseShadow(); }); BaseGame.UI.RenderGameBackground(); // Show car and ground plane SpeedyRacerManager.CarModel.RenderCar(false, carMatrix); plane.Render(); // Just add brake tracks (we don't render the landscape here) SpeedyRacerManager.Landscape.RenderBrakeTracks(); guardRailModel.Render(new Vector3(-11.5f, 2.5f, 0)); BaseGame.MeshRenderManager.Render(); // Add shadows ShaderEffect.shadowMapping.ShowShadows(); SpeedyRacerManager.Player.DisplayPhysicsValuesAndHelp(); }); }
/// <summary> /// Create landscape. /// This constructor should only be called /// from the SpeedyRacer main class! /// </summary> /// <param name="setLevel">Level we want to load</param> internal Landscape() { #region Load map height data #if OBS_DOESNT_WORK_ON_XBOX360 // Ok, load map grid heights. We can't load this as a Bitmap // because we don't have the System.Drawing namespace in XNA. // We also can't use BitmapContent or PixelBitmapContent<Color> from // the Microsoft.XNA.Framework.Content.Pipeline namespace because // our content is not compatible with that (its just a texture). Texture2D tex = BaseGame.Content.Load<Texture2D>( Path.Combine(Directories.ContentDirectory, "LandscapeGridHeights")); /*old Texture2D.FromFile(BaseGame.Device, "Textures\\LandscapeGridHeights.png"); */ if (tex.Width != GridWidth || tex.Height != GridHeight) throw new Exception(tex.Name + " has the resolution of " + tex.Width + "x" + tex.Height + ", but for the landscape we need " + GridWidth + "x" + GridHeight + "!"); // With help of GetData we can get to the data. Color[] texData = new Color[GridWidth * GridHeight]; tex.GetData<Color>(texData, 0, GridWidth * GridHeight); //tst: Log.Write("Pixel 0, 0=" + texData[0]); #endif FileStream file = FileHelper.LoadGameContentFile( "Content\\"+LandscapeHeightsDataFilename); byte[] heights = new byte[GridWidth * GridHeight]; file.Read(heights, 0, GridWidth*GridHeight); file.Close(); mapHeights = new float[GridWidth, GridHeight]; #endregion #region Build tangent vertices // Build our tangent vertices for (int x = 0; x < GridWidth; x++) for (int y = 0; y < GridHeight; y++) { // Step 1: Calculate position int index = x + y * GridWidth; Vector3 pos = CalcLandscapePos(x, y, heights);//texData); mapHeights[x, y] = pos.Z; vertices[index].pos = pos; //if (x == 0) // Log.Write("vertices " + y + ": " + pos); // Step 2: Calculate all edge vectors (for normals and tangents) // This involves quite complicated optimizations and mathematics, // hard to explain with just a comment. Read my book :D Vector3 edge1 = pos - CalcLandscapePos(x, y + 1, heights);//texData); Vector3 edge2 = pos - CalcLandscapePos(x + 1, y, heights);//texData); Vector3 edge3 = pos - CalcLandscapePos(x - 1, y + 1, heights);//texData); Vector3 edge4 = pos - CalcLandscapePos(x + 1, y + 1, heights);//texData); Vector3 edge5 = pos - CalcLandscapePos(x - 1, y - 1, heights);//texData); // Step 3: Calculate normal based on the edges (interpolate // from 3 cross products we build from our edges). vertices[index].normal = Vector3.Normalize( Vector3.Cross(edge2, edge1) + Vector3.Cross(edge4, edge3) + Vector3.Cross(edge3, edge5)); // Step 4: Set tangent data, just use edge1 vertices[index].tangent = Vector3.Normalize(edge1); // Step 5: Set texture coordinates, use full 0.0f to 1.0f range! vertices[index].uv = new Vector2( //x / (float)(GridWidth - 1), //y / (float)(GridHeight - 1)); y / (float)(GridHeight - 1), x / (float)(GridWidth - 1)); } // for for (int) #endregion #region Smooth normals // Smooth all normals, first copy them over, then smooth everything Vector3[,] normalsForSmoothing = new Vector3[GridWidth, GridHeight]; for (int x = 0; x < GridWidth; x++) for (int y = 0; y < GridHeight; y++) { int index = x + y * GridWidth; normalsForSmoothing[x, y] = vertices[index].normal; } // for for (int) // Time to smooth to normals we just saved for (int x = 1; x < GridWidth - 1; x++) for (int y = 1; y < GridHeight - 1; y++) { int index = x + y * GridWidth; // Smooth 3x3 normals, but still use old normal to 40% (5 of 13) Vector3 normal = vertices[index].normal * 4; for (int xAdd = -1; xAdd <= 1; xAdd++) for (int yAdd = -1; yAdd <= 1; yAdd++) normal += normalsForSmoothing[x+xAdd, y+yAdd]; vertices[index].normal = Vector3.Normalize(normal); // Also recalculate tangent to let it stay 90 degrees on the normal Vector3 helperVector = Vector3.Cross( vertices[index].normal, vertices[index].tangent); vertices[index].tangent = Vector3.Cross( helperVector, vertices[index].normal); } // for for (int) #endregion #region Set vertex buffer // Set vertex buffer vertexBuffer = new VertexBuffer( BaseGame.Device, typeof(TangentVertex), vertices.Length, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic); vertexBuffer.SetData(vertices); #endregion #region Calc index buffer // Calc index buffer (Note: have to use uint, ushort is not sufficiant // in our case because we have MANY vertices ^^) uint[] indices = new uint[(GridWidth - 1) * (GridHeight - 1) * 6]; int currentIndex = 0; for (int x = 0; x < GridWidth - 1; x++) for (int y = 0; y < GridHeight - 1; y++) { // Set landscape data (Note: Right handed) indices[currentIndex + 0] = (uint)(x * GridHeight + y); indices[currentIndex + 2] = (uint)((x + 1) * GridHeight + (y + 1)); indices[currentIndex + 1] = (uint)((x + 1) * GridHeight + y); indices[currentIndex + 3] = (uint)((x + 1) * GridHeight + (y + 1)); indices[currentIndex + 5] = (uint)(x * GridHeight + y); indices[currentIndex + 4] = (uint)(x * GridHeight + (y + 1)); // Add indices currentIndex += 6; } // for for (int) #endregion #region Set index buffer indexBuffer = new IndexBuffer( BaseGame.Device, typeof(uint), (GridWidth - 1) * (GridHeight - 1) * 6, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic); indexBuffer.SetData(indices); #endregion #region Load track (and replay inside ReloadLevel method) ReloadLevel(); #endregion #region Add city planes // Just set one giant plane for the whole city! foreach (LandscapeObject obj in landscapeObjects) if (obj.IsBigBuilding) { cityPlane = new PlaneRenderer( obj.Position, new Plane(new Vector3(0, 0, 1), 0.1f), cityMat, Math.Min(obj.Position.X, obj.Position.Y));//); break; } // foreach if (obj.IsBigBuilding) #endregion }