private void spawnDigParticle(Vector3 pos) { if (CoopPeerStarter.DedicatedHost) { return; } Vector3 pos2 = pos; pos2.y = Terrain.activeTerrain.SampleHeight(pos) + Terrain.activeTerrain.transform.position.y; if (this.IsOnSnow()) { PoolManager.Pools["Particles"].Spawn(LocalPlayer.ScriptSetup.events.snowFootParticle.transform, pos2, Quaternion.identity); return; } int prominantTextureIndex = TerrainHelper.GetProminantTextureIndex(base.transform.position); if (prominantTextureIndex == 6 || prominantTextureIndex == 1) { PoolManager.Pools["Particles"].Spawn(LocalPlayer.ScriptSetup.events.leafFootParticle.transform, pos2, Quaternion.identity); } else if (prominantTextureIndex == 3 || prominantTextureIndex == 0) { PoolManager.Pools["Particles"].Spawn(LocalPlayer.ScriptSetup.events.dustFootParticle.transform, pos2, Quaternion.identity); } else if (prominantTextureIndex == 4) { PoolManager.Pools["Particles"].Spawn(LocalPlayer.ScriptSetup.events.sandFootParticle.transform, pos2, Quaternion.identity); } }
public void InitializeHeightsAndNormals() { // Create a procedural height field. int numberOfSamples = 1025; float[] heights = new float[numberOfSamples * numberOfSamples]; var creator = new ProceduralTerrainCreator(RandomHelper.Random.Next(), 256, _noiseMu); creator.CreateTerrain( 7777, 9999, _noiseWidth * _terrainTile.CellSize, _noiseWidth * _terrainTile.CellSize, 0, _noiseHeight, 8, heights, numberOfSamples, numberOfSamples); float tileSize = (numberOfSamples - 1) * _terrainTile.CellSize; _terrainTile.OriginX = -tileSize / 2; _terrainTile.OriginZ = -tileSize / 2; Debug.Assert(Numeric.IsZero(_terrainTile.OriginX % _terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); Debug.Assert(Numeric.IsZero(_terrainTile.OriginZ % _terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); //TerrainHelper.SmoothTexture(heights, numberOfSamples, numberOfSamples, 1e10f); // Rebuild height texture. Texture2D heightTexture = _terrainTile.HeightTexture; TerrainHelper.CreateHeightTexture( _graphicsService.GraphicsDevice, heights, numberOfSamples, numberOfSamples, false, ref heightTexture); // Rebuild normal texture. Texture2D normalTexture = _terrainTile.NormalTexture; TerrainHelper.CreateNormalTexture( _graphicsService.GraphicsDevice, heights, numberOfSamples, numberOfSamples, _terrainTile.CellSize, false, ref normalTexture); _terrainTile.HeightTexture = heightTexture; _terrainTile.NormalTexture = normalTexture; // Set the height field data for collision detection. var heightField = (HeightField)_rigidBody.Shape; heightField.OriginX = _terrainTile.OriginX; heightField.OriginZ = _terrainTile.OriginZ; heightField.WidthX = tileSize; heightField.WidthZ = tileSize; heightField.SetSamples(heights, heightTexture.Width, heightTexture.Height); heightField.Invalidate(); TerrainNode.Terrain.Invalidate(); }
/// <summary> /// Applies the damage to affected chunks. /// </summary> /// <param name="damage">Damage.</param> private void ApplyDamage(List <IntPoint> damage) { long minX = long.MaxValue, minY = long.MaxValue, maxX = long.MinValue, maxY = long.MinValue; for (int i = 0; i < damage.Count; ++i) { if (damage[i].X < minX) { minX = damage[i].X; } if (damage[i].X > maxX) { maxX = damage[i].X; } if (damage[i].Y < minY) { minY = damage[i].Y; } if (damage[i].Y > maxY) { maxY = damage[i].Y; } } minX = Helper.NegDivision(minX, Chunk.SIZE); maxX = Helper.NegDivision(maxX, Chunk.SIZE); minY = Helper.NegDivision(minY, Chunk.SIZE); maxY = Helper.NegDivision(maxY, Chunk.SIZE); var clipper = new Clipper(); for (int y = (int)minY; y <= maxY; ++y) { for (int x = (int)minX; x <= maxX; ++x) { var chunk = GetChunk(TerrainHelper.PackCoordinates(x, y)); clipper.Clear(); clipper.AddPolygon(new List <IntPoint>() { new IntPoint(chunk.Left, chunk.Top), new IntPoint(chunk.Left + Chunk.SIZE, chunk.Top), new IntPoint(chunk.Left + Chunk.SIZE, chunk.Top + Chunk.SIZE), new IntPoint(chunk.Left, chunk.Top + Chunk.SIZE) }, PolyType.ptSubject); clipper.AddPolygon(damage, PolyType.ptClip); clipper.AddPolygons(chunk.Damage, PolyType.ptClip); clipper.Execute(ClipType.ctIntersection, chunk.Damage, PolyFillType.pftNonZero, PolyFillType.pftNonZero); chunk.Recalculate = true; if (ActiveChunks.Contains(chunk)) { Task.Run(() => { PlaceChunk(chunk); }); } } } }
private string GetLandEventForPosition() { if (this.IsOnSnow()) { this.isSnow = true; return(this.landSnow); } if (this.surfaceDetector.Surface != UnderfootSurfaceDetector.SurfaceType.None) { return(this.landDefault); } if (LocalPlayer.FpCharacter.IsInWater()) { return(this.landWater); } int prominantTextureIndex = TerrainHelper.GetProminantTextureIndex(base.transform.position); if (prominantTextureIndex == 1) { return(this.landMud); } if (prominantTextureIndex != 4) { return(this.landDefault); } return(this.landSand); }
public void GetTerrainForHeight_Returns_Lowland_When_Height_Is_From_145_To_170(byte height) { var terrainHelper = new TerrainHelper(); ITerrain terrain = terrainHelper.GetTerrainForHeight(height); Assert.IsInstanceOf <Lowland>(terrain); }
protected override void RenderTopCornerFace(TileDrawInfo drawInfo, int index) { float height = this.height + tileHeight; FastColour colour = lineMode ? new FastColour(150, 150, 150) : TerrainHelper.GetColour(drawInfo.CurrentTile.TerrainId); if (!lineMode) { vertices[index] = new Vertex(x + halfTileWidth, height, y, colour); vertices[index + 1] = new Vertex(x, z2, y + halfTileLength, colour); vertices[index + 2] = new Vertex(x + halfTileWidth, height, y + tileWidth, colour); vertices[index + 3] = new Vertex(x + halfTileWidth, height, y, colour); vertices[index + 4] = new Vertex(x + tileWidth, z1, y + halfTileLength, colour); vertices[index + 5] = new Vertex(x + halfTileWidth, height, y + tileWidth, colour); } else { vertices[index] = new Vertex(x + halfTileWidth, height, y, colour); vertices[index + 1] = new Vertex(x, z2, y + halfTileLength, colour); vertices[index + 2] = new Vertex(x + halfTileWidth, height, y + tileWidth, colour); vertices[index + 3] = new Vertex(x + tileWidth, z1, y + halfTileLength, colour); } drawInfo.TotalTriangles += 2; }
protected override void RenderStretchedFlatFace(TileDrawInfo drawInfo, int index) { FastColour colour = lineMode ? new FastColour(150, 150, 150) : TerrainHelper.GetColour(drawInfo.CurrentTile.TerrainId); if (!lineMode) { vertices[index] = new Vertex(x, z1, y + 0.5f, colour); vertices[index + 1] = new Vertex(x + 0.5f, z1, y, colour); vertices[index + 2] = new Vertex(x + stretchY, z2, y + stretchY - halfTileLength, colour); vertices[index + 3] = new Vertex(x + stretchY, z2, y + stretchY - halfTileLength, colour); vertices[index + 4] = new Vertex(x + stretchY - halfTileWidth, z2, y + stretchY, colour); vertices[index + 5] = new Vertex(x, z1, y + 0.5f, colour); } else { vertices[index] = new Vertex(x, z1, y + 0.5f, colour); vertices[index + 1] = new Vertex(x + 0.5f, z1, y, colour); vertices[index + 2] = new Vertex(x + stretchY, z2, y + stretchY - halfTileLength, colour); vertices[index + 3] = new Vertex(x + stretchY - halfTileWidth, z2, y + stretchY, colour); } drawInfo.TotalTriangles += 2; }
void Start() { cam = Camera.main.gameObject. GetComponent <CameraTest>(); TerrainHelper.SetTerrainData( GetComponent <MeshFilter>().mesh); }
/// <summary> /// Calculate render and physics data. /// </summary> public void Calculate(GameWorld world) { var clipper = new Clipper(); List <List <IntPoint> > output = new List <List <IntPoint> >(); // Calculate difference clipper.AddPolygons(Polygons, PolyType.ptSubject); clipper.AddPolygons(Cavities, PolyType.ptClip); clipper.AddPolygons(Damage, PolyType.ptClip); clipper.Execute(ClipType.ctDifference, output, PolyFillType.pftNonZero, PolyFillType.pftNonZero); Hulls.Clear(); output.ForEach(o => { Vector2[] points = new Vector2[o.Count]; int index = 0; o.ForEach(p => points[index++] = new Vector2(p.X, p.Y)); Hulls.Add(new Penumbra.Hull(points)); }); // Triangulate background for rendering { var triangulation = TerrainHelper.Triangulate(Polygons); TriangulatedBackgroundVertices = new VertexPositionColor[triangulation.Count * 3]; int index = 0; for (int i = 0; i < triangulation.Count; ++i) { for (int b = 0; b < 3; ++b) { TriangulatedBackgroundVertices[index++] = new VertexPositionColor( new Vector3(triangulation[i][b].X, triangulation[i][b].Y, 0), world.Planet.BackgroundSoilTint); } } } // Triangulate foreground for rendering { var triangulation = TerrainHelper.Triangulate(output); TriangulatedForegroundVertices = new VertexPositionColor[triangulation.Count * 3]; int index = 0; for (int i = 0; i < triangulation.Count; ++i) { for (int b = 0; b < 3; ++b) { TriangulatedForegroundVertices[index++] = new VertexPositionColor( new Vector3(triangulation[i][b].X, triangulation[i][b].Y, 0), world.Planet.SoilTint); } } } // Convert to Farseer data PhysicsOutput.Clear(); output.ForEach(p => { PhysicsOutput.Add(TerrainHelper.PolygonToVertices(p)); }); }
public string GetFootstepForPosition() { this.isSnow = false; switch (this.surfaceDetector.Surface) { case UnderfootSurfaceDetector.SurfaceType.None: if (this.IsOnSnow()) { this.isSnow = true; return(this.footstepSnow); } switch (TerrainHelper.GetProminantTextureIndex(base.transform.position)) { case 0: return(this.footstepDefault); case 1: return(this.footstepMud); case 2: case 3: case 5: case 7: return(this.footstepRock); case 4: return(this.footstepSand); case 6: return(this.footstepLeaves); default: return(this.footstepDefault); } break; case UnderfootSurfaceDetector.SurfaceType.Wood: return(this.footstepWood); case UnderfootSurfaceDetector.SurfaceType.Rock: return(this.footstepRock); case UnderfootSurfaceDetector.SurfaceType.Carpet: return(this.footstepCarpet); case UnderfootSurfaceDetector.SurfaceType.Dirt: return(this.footstepDefault); case UnderfootSurfaceDetector.SurfaceType.Metal: return(this.footstepMetal); default: return(this.footstepDefault); } }
void RenderTopCornerFace(TileDrawInfo drawInfo, Vertex[] vertices, int index) { FastColour colour = TerrainHelper.GetColour(drawInfo.CurrentTile.TerrainId); vertices[index] = new Vertex(X + 0.5f, height, Y, colour); vertices[index + 1] = new Vertex(X, z2, Y + 0.5f, colour); vertices[index + 2] = new Vertex(X + 0.5f, height, Y + tileWidth, colour); vertices[index + 3] = new Vertex(X + 0.5f, height, Y, colour); vertices[index + 4] = new Vertex(X + tileWidth, z1, Y + 0.5f, colour); vertices[index + 5] = new Vertex(X + 0.5f, height, Y + tileWidth, colour); drawInfo.TotalTriangles += 2; }
protected override void RenderBottomCornerFace(TileDrawInfo drawInfo, int index) { FastColour colour = TerrainHelper.GetColour(drawInfo.CurrentTile.TerrainId); vertices[index] = new Vertex(x + 0.5f, height, y, colour); vertices[index + 1] = new Vertex(x, z2, y + 0.5f, colour); vertices[index + 2] = new Vertex(x + 0.5f, height, y + tileWidth, colour); vertices[index + 3] = new Vertex(x + 0.5f, height, y, colour); vertices[index + 4] = new Vertex(x + tileWidth, z1, y + 0.5f, colour); vertices[index + 5] = new Vertex(x + 0.5f, height, y + tileWidth, colour); drawInfo.TotalTriangles += 2; }
public void spawnTree(Hex hex, float heightSeed) { GameObject newTree = Instantiate(treePrefab); Vector2 treePosition = Random.insideUnitCircle * this.getHexSize(); treePosition += HexMathHelper.hexToWorldCoords(hex.getPosition(), this.getHexSize()); float spawnHeight = TerrainHelper.getHeight(heightSeed, treePosition, hex, this.getHexSize(), elevatedHeightmap, mountainHeightmap); float yScale = Random.Range(0.05f, 0.08f); Vector3 newScale = newTree.transform.localScale; newScale.y = yScale; newTree.transform.localScale = newScale; newTree.transform.position = new Vector3(treePosition.x, treePosition.y, spawnHeight - 0.12f); }
protected override void RenderTopCornerFaceRotated(TileDrawInfo drawInfo, int index) { float height = this.height + tileHeight; FastColour colour = TerrainHelper.GetColour(drawInfo.CurrentTile.TerrainId); vertices[index] = new Vertex(x, height, y + 0.5f, colour); vertices[index + 1] = new Vertex(x + 0.5f, z1, y, colour); vertices[index + 2] = new Vertex(x + tileWidth, height, y + 0.5f, colour); vertices[index + 3] = new Vertex(x + tileWidth, height, y + 0.5f, colour); vertices[index + 4] = new Vertex(x + 0.5f, z2, y + tileWidth, colour); vertices[index + 5] = new Vertex(x, height, y + 0.5f, colour); drawInfo.TotalTriangles += 2; }
// Modify the terrain height values to add the road to the terrain. private void ClampTerrainToRoad() { // We have to manipulate the height and normal texture of each tile which contains the road. foreach (var tile in _terrainObject.TerrainNode.Terrain.Tiles) { // Get the current height texture of the tile and extract the heights into a float array. var heightTexture = tile.HeightTexture; float[] heights = TerrainHelper.GetTextureLevelSingle(heightTexture, 0); // Create a temporary height field. var heightField = new HeightField( tile.OriginX, tile.OriginZ, tile.WidthX, tile.WidthZ, heights, heightTexture.Width, heightTexture.Height); // Change the height values of the height field. TerrainRoadLayer.ClampTerrainToRoad(heightField, _roadPath, 8, 30, 10, 0.1f); // Rebuild the height texture. TerrainHelper.CreateHeightTexture( GraphicsService.GraphicsDevice, heights, heightTexture.Width, heightTexture.Height, false, ref heightTexture); // Rebuild the normal texture. var normalTexture = tile.NormalTexture; TerrainHelper.CreateNormalTexture( GraphicsService.GraphicsDevice, heights, heightTexture.Width, heightTexture.Height, tile.CellSize, false, ref normalTexture); // Get rigid body that represents this tile. var rigidBody = Simulation.RigidBodies.First(body => body.UserData == tile); // Update the height values of the collision detection height field. ((HeightField)rigidBody.Shape).SetSamples(heights, heightTexture.Width, heightTexture.Height); } }
void RenderTile(TileDrawInfo drawInfo, VertexPos3Tex2[] vertices, int index) { Tile tile = drawInfo.CurrentTile; X = tile.X; Y = tile.Y; height = tile.Height; RectangleF texRec = TerrainHelper.GetTexRec(tile.TerrainId); vertices[index] = new VertexPos3Tex2(X, height, Y, texRec.Left, texRec.Top); vertices[index + 1] = new VertexPos3Tex2(X + tileWidth, height, Y, texRec.Right, texRec.Top); vertices[index + 2] = new VertexPos3Tex2(X + tileWidth, height, Y + tileWidth, texRec.Right, texRec.Bottom); vertices[index + 3] = new VertexPos3Tex2(X, height, Y + tileWidth, texRec.Left, texRec.Bottom); drawInfo.TotalTriangles += 2; }
/// <summary> /// Sets a region around a point as active. /// </summary> /// <param name="center">Center.</param> public override void SetActiveArea(Vector2 center) { _activeX = Helper.NegDivision((int)center.X, Chunk.SIZE); _activeY = Helper.NegDivision((int)center.Y, Chunk.SIZE); Chunk[] newActive = new Chunk[CHUNK_CACHE * CHUNK_CACHE]; for (int y = 0; y < CHUNK_CACHE; ++y) { for (int x = 0; x < CHUNK_CACHE; ++x) { var cX = x + _activeX - CHUNK_CACHE / 2; var cY = y + _activeY - CHUNK_CACHE / 2; var id = TerrainHelper.PackCoordinates(cX, cY); Chunk chunk = null; if (ActiveChunks.Any(c => c != null && c.ID == id)) { chunk = ActiveChunks.First(c => c != null && c.ID == id); } else { chunk = GetChunk(id); if (Multithreaded) { Task.Run(() => { PlaceChunk(chunk); }); } else { PlaceChunk(chunk); } } newActive[x + y * CHUNK_CACHE] = chunk; } } for (int i = 0; i < ActiveChunks.Length; ++i) { if (ActiveChunks[i] != null && !newActive.Contains(ActiveChunks[i])) { UnplaceChunk(ActiveChunks[i]); if (!ActiveChunks[i].ShouldSave) { ChunkCache.Remove(ActiveChunks[i].ID); } } } ActiveChunks = newActive; }
void RenderTile(TileDrawInfo drawInfo) { Tile tile = drawInfo.CurrentTile; const int xWidth = 1, yWidth = 1; float X = tile.X * xWidth; float Y = tile.Y * yWidth; float x1 = X, y1 = Y; //X = ( x1 * xWidth / 2f ) + ( y1 * xWidth / 2f ); //Y = ( y1 * yWidth / 2f ) - ( x1 * yWidth / 2f ); float z = 1; /*if( ( tile.X % 2 ) == 0 ) { * //X /= 2f; * //X += xWidth / 2f; * //y += yWidth / 2f; * //Y /= 2f; * Y += yWidth * 5; * Y -= (int)( yWidth / 2f ); * //return; * //return; * } else { * X /= 2f; * }*/ RectangleF texRec = TerrainHelper.GetTexRec(tile.TerrainId); float Width = xWidth; float Depth = yWidth; float xHalf = Width / 2; GL.TexCoord2(0f, 0f); GL.Vertex3(X - xHalf, z, Y + Depth / 4); // Bottom left GL.TexCoord2(1f, 0f); GL.Vertex3(X + xHalf, z, Y + Depth / 4); // Bottom Right GL.TexCoord2(1f, 1f); GL.Vertex3(X + xHalf, z, Y - 3 * Depth / 4); // Top Right GL.TexCoord2(0f, 1f); GL.Vertex3(X - xHalf, z, Y - 3 * Depth / 4); // Top Left //VertexPosUv( x, height, y, texRec.Left, texRec.Top ); //VertexPosUv( x + tileWidth, height, y, texRec.Right, texRec.Top ); //VertexPosUv( x + tileWidth, height, y + tileHeight, texRec.Right, texRec.Bottom ); //VertexPosUv( x, height, y + tileHeight, texRec.Left, texRec.Bottom ); drawInfo.TotalTriangles += 2; }
public void GetTerrains(BitmapDto model) { var heightMap = new Bitmap(model.Path); TerrainHelper.SetTerrains(BitmapToPixelArray(heightMap)); var terrainHelper = new TerrainHelper(); var terrains = terrainHelper.GetAllTerrains() .Select(t => new TerrainDto() { Type = t.GetType().Name, UpperHeightBound = t.UpperBound }); Clients.All.OnGetTerrainsResponse(terrains); }
//------------------------ events ------------------------- void OnMouseDown() { RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit)) { cam.AdjustPath(Quaternion.LookRotation(hit.point)); int[] selected_quad = TerrainHelper.TriangleIndices(hit.triangleIndex); string debug_text = hit.triangleIndex.ToString() + ": " + selected_quad[0].ToString() + ", " + selected_quad[1].ToString(); Debug.Log(debug_text); } }
public TerrainTextureSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; _graphicsScreen = new DeferredGraphicsScreen(Services); _graphicsScreen.DrawReticle = true; GraphicsService.Screens.Insert(0, _graphicsScreen); GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services)); Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer); var scene = _graphicsScreen.Scene; Services.Register(typeof(IScene), null, scene); // Add gravity and damping to the physics simulation. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a custom game object which controls the camera. var cameraGameObject = new CameraObject(Services, 60); cameraGameObject.ResetPose(new Vector3F(0, 1.8f, 0), 0, 0); GameObjectService.Objects.Add(cameraGameObject); _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode; for (int i = 0; i < 10; i++) { GameObjectService.Objects.Add(new DynamicObject(Services, 1)); } GameObjectService.Objects.Add(new DynamicSkyObject(Services, true, false, true)); // Create a simple flat terrain. var terrain = new Terrain(); _terrainTile = new TerrainTile(GraphicsService) { OriginX = -100, OriginZ = -100, CellSize = 1, }; terrain.Tiles.Add(_terrainTile); // Create a flat dummy height texture. float[] heights = new float[200 * 200]; Texture2D heightTexture = null; TerrainHelper.CreateHeightTexture( GraphicsService.GraphicsDevice, heights, 200, 200, false, ref heightTexture); _terrainTile.HeightTexture = heightTexture; var shadowMapEffect = ContentManager.Load <Effect>("DigitalRune/Terrain/TerrainShadowMap"); var gBufferEffect = ContentManager.Load <Effect>("DigitalRune/Terrain/TerrainGBuffer"); var materialEffect = ContentManager.Load <Effect>("DigitalRune/Terrain/TerrainMaterial"); var material = new Material { { "ShadowMap", new EffectBinding(GraphicsService, shadowMapEffect, null, EffectParameterHint.Material) }, { "GBuffer", new EffectBinding(GraphicsService, gBufferEffect, null, EffectParameterHint.Material) }, { "Material", new EffectBinding(GraphicsService, materialEffect, null, EffectParameterHint.Material) } }; var terrainNode = new TerrainNode(terrain, material) { DetailClipmap = { CellsPerLevel = 1364, NumberOfLevels = 9, EnableMipMap = true, }, }; scene.Children.Add(terrainNode); // Add 3 detail textures layers: gravel, rock, snow. float detailCellSize = terrainNode.DetailClipmap.CellSizes[0]; var materialGravel = new TerrainMaterialLayer(GraphicsService) { DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Gravel-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Gravel-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Gravel-Specular"), TileSize = detailCellSize * 512, BlendRange = 0.1f, }; _terrainTile.Layers.Add(materialGravel); var noiseTexture = NoiseHelper.GetNoiseTexture(GraphicsService, 128, 60); var materialRock = new TerrainMaterialLayer(GraphicsService) { DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Specular"), HeightTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Height"), TileSize = detailCellSize * 1024, DiffuseColor = new Vector3F(1 / 0.702f), BlendTexture = noiseTexture, BlendTextureChannel = 0, BlendRange = 0.1f, TerrainHeightBlendRange = 0.1f, }; _terrainTile.Layers.Add(materialRock); var materialSnow = new TerrainMaterialLayer(GraphicsService) { DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Snow-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Snow-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Snow-Specular"), TileSize = detailCellSize * 512, BlendTexture = noiseTexture, BlendTextureChannel = 1, BlendRange = 0.1f, }; _terrainTile.Layers.Add(materialSnow); // Create a flat plane for collision detection. var rigidBody = new RigidBody(new PlaneShape(), new MassFrame(), null) { MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(rigidBody); CreateGuiControls(); }
private float getRandomWanderForce(Vector3 vertice) { //return Random.Range(-0.05f, 0.05f); return(TerrainHelper.getWaterCurvingHeight(vertice, currentPerlinNoise)); }
private void UpdateHoleTexture() { // Hole information could be loaded from a texture. Here, we procedurally create a pattern of // holes. // The hole buffer is array with 1 where there is no hole and 0 where there is a hole. // (This is similar to an alpha mask: 1 = opaque, 0 = transparent.) int numberOfSamples = _terrainObject.TerrainNode.Terrain.Tiles.First().HeightTexture.Width; float[] holes = new float[numberOfSamples * numberOfSamples]; // Fill hole buffer with 1. for (int i = 0; i < holes.Length; i++) { holes[i] = 1; } if (_holeSize > 0) { // Add some 0 elements to create holes. int counterY = _holeSize; for (int y = 0; y < numberOfSamples - 1; y++) { int counterX = _holeSize; for (int x = 0; x < numberOfSamples - 1; x++) { holes[y * numberOfSamples + x] = 0; counterX--; if (counterX <= 0) { counterX = _holeSize; x += _holeSize * 2; } } counterY--; if (counterY <= 0) { counterY = _holeSize; y += _holeSize * 2; } } } // Copy hole buffer to a texture. TerrainHelper.CreateHoleTexture( GraphicsService.GraphicsDevice, holes, numberOfSamples, numberOfSamples, false, ref _holeTexture); foreach (var tile in _terrainObject.TerrainNode.Terrain.Tiles) { // Assign the hole texture to all terrain tiles. (Normally, each tile would have a // different hole texture or no hole texture at all.) tile.HoleTexture = _holeTexture; // We also have to add the holes to the collision detection height fields. // Get rigid body that represents this tile. var rigidBody = Simulation.RigidBodies.First(body => body.UserData == tile); var heightField = (HeightField)rigidBody.Shape; // Update the height values of the collision detection height field. float[] heights = TerrainHelper.GetTextureLevelSingle(tile.HeightTexture, 0); for (int z = 0; z < numberOfSamples; z++) { for (int x = 0; x < numberOfSamples; x++) { if ((!(holes[z * numberOfSamples + x] > 0.5f))) { // The HeightField class treats NaN as holes. heights[z * numberOfSamples + x] = float.NaN; } } } heightField.SetSamples(heights, numberOfSamples, numberOfSamples); heightField.Invalidate(); } _terrainObject.TerrainNode.Terrain.Invalidate(); }
public static int GetProminantTextureIndex(Vector3 worldPosition) { return(TerrainHelper.GetProminantTextureIndex(Terrain.activeTerrain, worldPosition)); }
public override void GenerateSamples(ref MapPixelData mapPixel) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Create samples arrays mapPixel.tilemapSamples = new TilemapSample[MapsFile.WorldMapTileDim, MapsFile.WorldMapTileDim]; mapPixel.heightmapSamples = new float[HeightmapDimension, HeightmapDimension]; // Divisor ensures continuous 0-1 range of tile samples float div = (float)(HeightmapDimension - 1) / 3f; // Read neighbouring height samples for this map pixel int mx = mapPixel.mapPixelX; int my = mapPixel.mapPixelY; byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); float[,] multiplierValue = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); multiplierValue[x, y] = ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY); } } // Extract height samples for all chunks float averageHeight = 0; float maxHeight = float.MinValue; float baseHeight, noiseHeight; float x1, x2, x3, x4; int dim = HeightmapDimension; //mapPixel.heightmapSamples = new float[dim, dim]; for (int y = 0; y < dim; y++) { for (int x = 0; x < dim; x++) { float rx = (float)x / div; float ry = (float)y / div; int ix = Mathf.FloorToInt(rx); int iy = Mathf.FloorToInt(ry); float sfracx = (float)x / (float)(dim - 1); float sfracy = (float)y / (float)(dim - 1); float fracx = (float)(x - ix * div) / div; float fracy = (float)(y - iy * div) / div; float scaledHeight = 0; // Bicubic sample small height map for base terrain elevation x1 = TerrainHelper.CubicInterpolator(shm[0, 3] * multiplierValue[0, 3], shm[1, 3] * multiplierValue[1, 3], shm[2, 3] * multiplierValue[2, 3], shm[3, 3] * multiplierValue[3, 3], sfracx); x2 = TerrainHelper.CubicInterpolator(shm[0, 2] * multiplierValue[0, 2], shm[1, 2] * multiplierValue[1, 2], shm[2, 2] * multiplierValue[2, 2], shm[3, 2] * multiplierValue[3, 2], sfracx); x3 = TerrainHelper.CubicInterpolator(shm[0, 1] * multiplierValue[0, 1], shm[1, 1] * multiplierValue[1, 1], shm[2, 1] * multiplierValue[2, 1], shm[3, 1] * multiplierValue[3, 1], sfracx); x4 = TerrainHelper.CubicInterpolator(shm[0, 0] * multiplierValue[0, 0], shm[1, 0] * multiplierValue[1, 0], shm[2, 0] * multiplierValue[2, 0], shm[3, 0] * multiplierValue[3, 0], sfracx); baseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy); scaledHeight += baseHeight * baseHeightScale; // Bicubic sample large height map for noise mask over terrain features x1 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 0], lhm[ix + 1, iy + 0], lhm[ix + 2, iy + 0], lhm[ix + 3, iy + 0], fracx); x2 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 1], lhm[ix + 1, iy + 1], lhm[ix + 2, iy + 1], lhm[ix + 3, iy + 1], fracx); x3 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 2], lhm[ix + 1, iy + 2], lhm[ix + 2, iy + 2], lhm[ix + 3, iy + 2], fracx); x4 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 3], lhm[ix + 1, iy + 3], lhm[ix + 2, iy + 3], lhm[ix + 3, iy + 3], fracx); noiseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, fracy); scaledHeight += noiseHeight * noiseMapScale; // Additional noise mask for small terrain features at ground level int noisex = mapPixel.mapPixelX * (HeightmapDimension - 1) + x; int noisey = (MapsFile.MaxMapPixelY - mapPixel.mapPixelY) * (HeightmapDimension - 1) + y; float lowFreq = TerrainHelper.GetNoise(noisex, noisey, 0.3f, 0.5f, 0.5f, 1); float highFreq = TerrainHelper.GetNoise(noisex, noisey, 0.9f, 0.5f, 0.5f, 1); scaledHeight += (lowFreq * highFreq) * extraNoiseScale; // Clamp lower values to ocean elevation if (scaledHeight < scaledOceanElevation) { scaledHeight = scaledOceanElevation; } // Accumulate average height averageHeight += scaledHeight; // Get max height if (scaledHeight > maxHeight) { maxHeight = scaledHeight; } // Set sample float height = Mathf.Clamp01(scaledHeight / MaxTerrainHeight); mapPixel.heightmapSamples[y, x] = height; } } // Average and max heights are passed back for locations mapPixel.averageHeight = (averageHeight /= (float)(dim * dim)) / MaxTerrainHeight; mapPixel.maxHeight = maxHeight / MaxTerrainHeight; }
public override JobHandle ScheduleGenerateSamplesJob(ref MapPixelData mapPixel) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Divisor ensures continuous 0-1 range of tile samples float div = (float)(HeightmapDimension - 1) / 3f; // Read neighbouring height samples for this map pixel int mx = mapPixel.mapPixelX; int my = mapPixel.mapPixelY; // Seed random with terrain key UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(mx, my)); byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); float[,] baseHeightValue = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); baseHeightValue[x, y] = shm[x, y] * ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY); } } float[,] waterMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if (shm[x, y] <= 2) // mappixel is water { waterMap[x, y] = 0.0f; } else { waterMap[x, y] = 1.0f; } } } float[,] climateMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); climateMap[x, y] = GetNoiseMapScaleBasedOnClimate(mapPixelX, mapPixelY); } } float[,] waterDistanceMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); waterDistanceMap[x, y] = (float)Math.Sqrt(ImprovedWorldTerrain.MapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); } } float[,] noiseHeightMultiplierMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { // interpolation multiplier taking near coast map pixels into account // (multiply with 0 at coast line and 1 at interpolationEndDistanceFromWaterForNoiseScaleMultiplier) float multFact = (Mathf.Min(interpolationEndDistanceFromWaterForNoiseScaleMultiplier, waterDistanceMap[x, y]) / interpolationEndDistanceFromWaterForNoiseScaleMultiplier); // blend watermap with climatemap taking into account multFact noiseHeightMultiplierMap[x, y] = waterMap[x, y] * climateMap[x, y] * multFact; } } float extraNoiseScaleBasedOnClimate = GetExtraNoiseScaleBasedOnClimate(mx, my); byte sDim = 4; NativeArray <float> baseHeightValueNativeArray = new NativeArray <float>(shm.Length, Allocator.TempJob); int i = 0; for (int y = 0; y < sDim; y++) { for (int x = 0; x < sDim; x++) { baseHeightValueNativeArray[i++] = baseHeightValue[x, y]; } } i = 0; NativeArray <float> noiseHeightMultiplierNativeArray = new NativeArray <float>(noiseHeightMultiplierMap.Length, Allocator.TempJob); for (int y = 0; y < sDim; y++) { for (int x = 0; x < sDim; x++) { noiseHeightMultiplierNativeArray[i++] = noiseHeightMultiplierMap[x, y]; } } // TODO - shortcut conversion & flattening. NativeArray <byte> lhmNativeArray = new NativeArray <byte>(lhm.Length, Allocator.TempJob); byte lDim = (byte)lhm.GetLength(0); i = 0; for (int y = 0; y < lDim; y++) { for (int x = 0; x < lDim; x++) { lhmNativeArray[i++] = lhm[x, y]; } } // Add the working native arrays to list for later disposal. mapPixel.nativeArrayList.Add(baseHeightValueNativeArray); mapPixel.nativeArrayList.Add(noiseHeightMultiplierNativeArray); mapPixel.nativeArrayList.Add(lhmNativeArray); // Extract height samples for all chunks int hDim = HeightmapDimension; GenerateSamplesJob generateSamplesJob = new GenerateSamplesJob { baseHeightValue = baseHeightValueNativeArray, lhm = lhmNativeArray, noiseHeightMultiplierMap = noiseHeightMultiplierNativeArray, heightmapData = mapPixel.heightmapData, sd = sDim, ld = lDim, hDim = hDim, div = div, mapPixelX = mapPixel.mapPixelX, mapPixelY = mapPixel.mapPixelY, maxTerrainHeight = MaxTerrainHeight, extraNoiseScaleBasedOnClimate = extraNoiseScaleBasedOnClimate, }; JobHandle generateSamplesHandle = generateSamplesJob.Schedule(hDim * hDim, 64); // Batch = 1 breaks it since shm not copied... test again later return(generateSamplesHandle); }
public void Execute(int index) { // Use cols=x and rows=y for height data int x = JobA.Col(index, hDim); int y = JobA.Row(index, hDim); float rx = (float)x / div; float ry = (float)y / div; int ix = Mathf.FloorToInt(rx); int iy = Mathf.FloorToInt(ry); float sfracx = (float)x / (float)(hDim - 1); float sfracy = (float)y / (float)(hDim - 1); float fracx = (float)(x - ix * div) / div; float fracy = (float)(y - iy * div) / div; float scaledHeight = 0; // Bicubic sample small height map for base terrain elevation x1 = TerrainHelper.CubicInterpolator(baseHeightValue[JobA.Idx(0, 3, sd)], baseHeightValue[JobA.Idx(1, 3, sd)], baseHeightValue[JobA.Idx(2, 3, sd)], baseHeightValue[JobA.Idx(3, 3, sd)], sfracx); x2 = TerrainHelper.CubicInterpolator(baseHeightValue[JobA.Idx(0, 2, sd)], baseHeightValue[JobA.Idx(1, 2, sd)], baseHeightValue[JobA.Idx(2, 2, sd)], baseHeightValue[JobA.Idx(3, 2, sd)], sfracx); x3 = TerrainHelper.CubicInterpolator(baseHeightValue[JobA.Idx(0, 1, sd)], baseHeightValue[JobA.Idx(1, 1, sd)], baseHeightValue[JobA.Idx(2, 1, sd)], baseHeightValue[JobA.Idx(3, 1, sd)], sfracx); x4 = TerrainHelper.CubicInterpolator(baseHeightValue[JobA.Idx(0, 0, sd)], baseHeightValue[JobA.Idx(1, 0, sd)], baseHeightValue[JobA.Idx(2, 0, sd)], baseHeightValue[JobA.Idx(3, 0, sd)], sfracx); baseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy); scaledHeight += baseHeight * baseHeightScale; // Bicubic sample large height map for noise mask over terrain features x1 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 0, ld)], lhm[JobA.Idx(ix + 1, iy + 0, ld)], lhm[JobA.Idx(ix + 2, iy + 0, ld)], lhm[JobA.Idx(ix + 3, iy + 0, ld)], fracx); x2 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 1, ld)], lhm[JobA.Idx(ix + 1, iy + 1, ld)], lhm[JobA.Idx(ix + 2, iy + 1, ld)], lhm[JobA.Idx(ix + 3, iy + 1, ld)], fracx); x3 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 2, ld)], lhm[JobA.Idx(ix + 1, iy + 2, ld)], lhm[JobA.Idx(ix + 2, iy + 2, ld)], lhm[JobA.Idx(ix + 3, iy + 2, ld)], fracx); x4 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 3, ld)], lhm[JobA.Idx(ix + 1, iy + 3, ld)], lhm[JobA.Idx(ix + 2, iy + 3, ld)], lhm[JobA.Idx(ix + 3, iy + 3, ld)], fracx); noiseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, fracy); x1 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[JobA.Idx(0, 3, sd)], noiseHeightMultiplierMap[JobA.Idx(1, 3, sd)], noiseHeightMultiplierMap[JobA.Idx(2, 3, sd)], noiseHeightMultiplierMap[JobA.Idx(3, 3, sd)], sfracx); x2 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[JobA.Idx(0, 2, sd)], noiseHeightMultiplierMap[JobA.Idx(1, 2, sd)], noiseHeightMultiplierMap[JobA.Idx(2, 2, sd)], noiseHeightMultiplierMap[JobA.Idx(3, 2, sd)], sfracx); x3 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[JobA.Idx(0, 1, sd)], noiseHeightMultiplierMap[JobA.Idx(1, 1, sd)], noiseHeightMultiplierMap[JobA.Idx(2, 1, sd)], noiseHeightMultiplierMap[JobA.Idx(3, 1, sd)], sfracx); x4 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[JobA.Idx(0, 0, sd)], noiseHeightMultiplierMap[JobA.Idx(1, 0, sd)], noiseHeightMultiplierMap[JobA.Idx(2, 0, sd)], noiseHeightMultiplierMap[JobA.Idx(3, 0, sd)], sfracx); float noiseHeightMultiplier = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy); scaledHeight += noiseHeight * noiseHeightMultiplier; // Additional noise mask for small terrain features at ground level // small terrain features' height scale should depend on climate of map pixel float extraNoiseScale = extraNoiseScaleBasedOnClimate; // prevent seams between different climate map pixels if (x <= 0 || y <= 0 || x >= hDim - 1 || y >= hDim - 1) { extraNoiseScale = defaultExtraNoiseScale; } int noisex = mapPixelX * (hDim - 1) + x; int noisey = (MapsFile.MaxMapPixelY - mapPixelY) * (hDim - 1) + y; float lowFreq = TerrainHelper.GetNoise(noisex, noisey, 0.3f, 0.5f, 0.5f, 1); float highFreq = TerrainHelper.GetNoise(noisex, noisey, 0.9f, 0.5f, 0.5f, 1); scaledHeight += (lowFreq * highFreq) * extraNoiseScale; // Clamp lower values to ocean elevation if (scaledHeight < scaledOceanElevation) { scaledHeight = scaledOceanElevation; } // Set sample float height = Mathf.Clamp01(scaledHeight / maxTerrainHeight); heightmapData[index] = height; }
public override void GenerateSamples(ref MapPixelData mapPixel) { //System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); //long startTime = stopwatch.ElapsedMilliseconds; DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Create samples arrays mapPixel.heightmapSamples = new float[HeightmapDimension, HeightmapDimension]; // Divisor ensures continuous 0-1 range of tile samples float div = (float)(HeightmapDimension - 1) / 3f; // Read neighbouring height samples for this map pixel int mx = mapPixel.mapPixelX; int my = mapPixel.mapPixelY; // Seed random with terrain key UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(mx, my)); byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); float[,] baseHeightValue = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); baseHeightValue[x, y] = shm[x, y] * ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY); } } float[,] waterMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if (shm[x, y] <= 2) // mappixel is water { waterMap[x, y] = 0.0f; } else { waterMap[x, y] = 1.0f; } } } float[,] climateMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); climateMap[x, y] = GetNoiseMapScaleBasedOnClimate(mapPixelX, mapPixelY); } } float[,] waterDistanceMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); waterDistanceMap[x, y] = (float)Math.Sqrt(ImprovedWorldTerrain.MapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); } } float[,] noiseHeightMultiplierMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { // interpolation multiplier taking near coast map pixels into account // (multiply with 0 at coast line and 1 at interpolationEndDistanceFromWaterForNoiseScaleMultiplier) float multFact = (Mathf.Min(interpolationEndDistanceFromWaterForNoiseScaleMultiplier, waterDistanceMap[x, y]) / interpolationEndDistanceFromWaterForNoiseScaleMultiplier); // blend watermap with climatemap taking into account multFact noiseHeightMultiplierMap[x, y] = waterMap[x, y] * climateMap[x, y] * multFact; } } //float[,] noiseHeightMultiplierMap = new float[4, 4]; //for (int y = 0; y < 4; y++) //{ // for (int x = 0; x < 4; x++) // { // int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); // int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); // float climateValue = GetNoiseMapScaleBasedOnClimate(mapPixelX, mapPixelY); // float waterDistance = (float)Math.Sqrt(ImprovedWorldTerrain.MapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); // float waterValue; // if (shm[x, y] <= 2) // mappixel is water // waterValue = 0.0f; // else // waterValue = 1.0f; // // interpolation multiplier taking near coast map pixels into account // // (multiply with 0 at coast line and 1 at interpolationEndDistanceFromWaterForNoiseScaleMultiplier) // float multFact = (Mathf.Min(interpolationEndDistanceFromWaterForNoiseScaleMultiplier, waterDistance) / interpolationEndDistanceFromWaterForNoiseScaleMultiplier); // // blend watermap with climatemap taking into account multFact // noiseHeightMultiplierMap[x, y] = waterValue * climateValue * multFact; // } //} //int numWorkerThreads = 0, completionPortThreads = 0; //int numMinWorkerThreads = 0, numMaxWorkerThreads = 0; //ThreadPool.GetAvailableThreads(out numWorkerThreads, out completionPortThreads); //ThreadPool.GetMinThreads(out numMinWorkerThreads, out completionPortThreads); //ThreadPool.GetMaxThreads(out numMaxWorkerThreads, out completionPortThreads); //Debug.Log(String.Format("available threads: {0}, numMinWorkerThreads: {1}, numMaxWorkerThreads: {2}", numWorkerThreads, numMinWorkerThreads, numMaxWorkerThreads)); float extraNoiseScaleBasedOnClimate = GetExtraNoiseScaleBasedOnClimate(mx, my); // the number of parallel tasks (use logical processor count for now - seems to be a good value) int numParallelTasks = Environment.ProcessorCount; // events used to synchronize thread computations (wait for them to finish) var doneEvents = new ManualResetEvent[numParallelTasks]; // the array of instances of the height computations helper class var heightsComputationTaskArray = new HeightsComputationTask[numParallelTasks]; // array of the data needed by the different tasks var dataForTasks = new HeightsComputationTask.DataForTask[numParallelTasks]; for (int i = 0; i < numParallelTasks; i++) { doneEvents[i] = new ManualResetEvent(false); var heightsComputationTask = new HeightsComputationTask(doneEvents[i]); heightsComputationTaskArray[i] = heightsComputationTask; dataForTasks[i] = new HeightsComputationTask.DataForTask(); dataForTasks[i].numTasks = numParallelTasks; dataForTasks[i].currentTask = i; dataForTasks[i].HeightmapDimension = HeightmapDimension; dataForTasks[i].MaxTerrainHeight = MaxTerrainHeight; dataForTasks[i].div = div; dataForTasks[i].baseHeightValue = baseHeightValue; dataForTasks[i].lhm = lhm; dataForTasks[i].noiseHeightMultiplierMap = noiseHeightMultiplierMap; dataForTasks[i].extraNoiseScaleBasedOnClimate = extraNoiseScaleBasedOnClimate; dataForTasks[i].mapPixel = mapPixel; ThreadPool.QueueUserWorkItem(heightsComputationTask.ThreadProc, dataForTasks[i]); } // wait for all tasks to finish computation WaitHandle.WaitAll(doneEvents); // computed average and max height in a second pass (after threaded tasks computed all heights) float averageHeight = 0; float maxHeight = float.MinValue; int dim = HeightmapDimension; for (int y = 0; y < dim; y++) { for (int x = 0; x < dim; x++) { // get sample float height = mapPixel.heightmapSamples[y, x]; // Accumulate average height averageHeight += height; // Get max height if (height > maxHeight) { maxHeight = height; } } } // Average and max heights are passed back for locations mapPixel.averageHeight = (averageHeight /= (float)(dim * dim)); mapPixel.maxHeight = maxHeight; //long totalTime = stopwatch.ElapsedMilliseconds - startTime; //DaggerfallUnity.LogMessage(string.Format("GenerateSamples took: {0}ms", totalTime), true); }
public void ThreadProc(System.Object stateInfo) { DataForTask dataForTask = stateInfo as DataForTask; // Extract height samples for all chunks float baseHeight, noiseHeight; float x1, x2, x3, x4; int dim = dataForTask.HeightmapDimension; float div = dataForTask.div; float[,] baseHeightValue = dataForTask.baseHeightValue; byte[,] lhm = dataForTask.lhm; float[,] noiseHeightMultiplierMap = dataForTask.noiseHeightMultiplierMap; float extraNoiseScaleBasedOnClimate = dataForTask.extraNoiseScaleBasedOnClimate; // split the work between different tasks running in different threads (thread n computes data elements n, n + numTasks, n + numTasks*2, ...) for (int y = dataForTask.currentTask; y < dim; y += dataForTask.numTasks) { for (int x = 0; x < dim; x++) { float rx = (float)x / div; float ry = (float)y / div; int ix = Mathf.FloorToInt(rx); int iy = Mathf.FloorToInt(ry); float sfracx = (float)x / (float)(dim - 1); float sfracy = (float)y / (float)(dim - 1); float fracx = (float)(x - ix * div) / div; float fracy = (float)(y - iy * div) / div; float scaledHeight = 0; // Bicubic sample small height map for base terrain elevation x1 = TerrainHelper.CubicInterpolator(baseHeightValue[0, 3], baseHeightValue[1, 3], baseHeightValue[2, 3], baseHeightValue[3, 3], sfracx); x2 = TerrainHelper.CubicInterpolator(baseHeightValue[0, 2], baseHeightValue[1, 2], baseHeightValue[2, 2], baseHeightValue[3, 2], sfracx); x3 = TerrainHelper.CubicInterpolator(baseHeightValue[0, 1], baseHeightValue[1, 1], baseHeightValue[2, 1], baseHeightValue[3, 1], sfracx); x4 = TerrainHelper.CubicInterpolator(baseHeightValue[0, 0], baseHeightValue[1, 0], baseHeightValue[2, 0], baseHeightValue[3, 0], sfracx); baseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy); scaledHeight += baseHeight * baseHeightScale; // Bicubic sample large height map for noise mask over terrain features x1 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 0], lhm[ix + 1, iy + 0], lhm[ix + 2, iy + 0], lhm[ix + 3, iy + 0], fracx); x2 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 1], lhm[ix + 1, iy + 1], lhm[ix + 2, iy + 1], lhm[ix + 3, iy + 1], fracx); x3 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 2], lhm[ix + 1, iy + 2], lhm[ix + 2, iy + 2], lhm[ix + 3, iy + 2], fracx); x4 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 3], lhm[ix + 1, iy + 3], lhm[ix + 2, iy + 3], lhm[ix + 3, iy + 3], fracx); noiseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, fracy); x1 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[0, 3], noiseHeightMultiplierMap[1, 3], noiseHeightMultiplierMap[2, 3], noiseHeightMultiplierMap[3, 3], sfracx); x2 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[0, 2], noiseHeightMultiplierMap[1, 2], noiseHeightMultiplierMap[2, 2], noiseHeightMultiplierMap[3, 2], sfracx); x3 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[0, 1], noiseHeightMultiplierMap[1, 1], noiseHeightMultiplierMap[2, 1], noiseHeightMultiplierMap[3, 1], sfracx); x4 = TerrainHelper.CubicInterpolator(noiseHeightMultiplierMap[0, 0], noiseHeightMultiplierMap[1, 0], noiseHeightMultiplierMap[2, 0], noiseHeightMultiplierMap[3, 0], sfracx); float noiseHeightMultiplier = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy); scaledHeight += noiseHeight * noiseHeightMultiplier; // Additional noise mask for small terrain features at ground level // small terrain features' height scale should depend on climate of map pixel float extraNoiseScale = extraNoiseScaleBasedOnClimate; // prevent seams between different climate map pixels if (x <= 0 || y <= 0 || x >= dim - 1 || y >= dim - 1) { extraNoiseScale = defaultExtraNoiseScale; } int noisex = dataForTask.mapPixel.mapPixelX * (dataForTask.HeightmapDimension - 1) + x; int noisey = (MapsFile.MaxMapPixelY - dataForTask.mapPixel.mapPixelY) * (dataForTask.HeightmapDimension - 1) + y; float lowFreq = TerrainHelper.GetNoise(noisex, noisey, 0.3f, 0.5f, 0.5f, 1); float highFreq = TerrainHelper.GetNoise(noisex, noisey, 0.9f, 0.5f, 0.5f, 1); scaledHeight += (lowFreq * highFreq) * extraNoiseScale; // Clamp lower values to ocean elevation if (scaledHeight < scaledOceanElevation) { scaledHeight = scaledOceanElevation; } // Set sample float height = Mathf.Clamp01(scaledHeight / dataForTask.MaxTerrainHeight); dataForTask.mapPixel.heightmapSamples[y, x] = height; } } _doneEvent.Set(); }
// Initialize the terrain geometry from height textures. public void InitializeHeightsAndNormals() { var content = _services.GetInstance<ContentManager>(); // Create the height and normal texture for the 4 tiles. // We can do this in parallel. We use following lock for parts which are not thread-safe. var lockObject = new object(); Parallel.For(0, 2, row => //for (int row = 0; row < 2; row++) { Parallel.For(0, 2, column => //for (int column = 0; column < 2; column++) { string tilePostfix = "-" + row + "-" + column; // e.g. "-0-1" var tile = _tiles[row, column]; var terrainTile = tile.TerrainTile; // The height data is loaded from a 16-bit grayscale texture. Texture2D inputHeightTexture; lock (lockObject) inputHeightTexture = content.Load<Texture2D>("Terrain/Terrain001-Height" + tilePostfix); Debug.Assert(inputHeightTexture.Width == inputHeightTexture.Height, "This code assumes that terrain tiles are square."); // The height textures of the tiles overlap by one texel to avoid gaps. That means, the // input height texture for each tile is 1025 x 1025. The last texture column of the top // left tile is the same as the first column of the top right tile. The last row of the // top left tile is the same as the first row of the bottom left tile. // The tile size in world space units is: float tileSize = (inputHeightTexture.Width - 1) * terrainTile.CellSize; terrainTile.OriginX = -tileSize + tileSize * column; terrainTile.OriginZ = -tileSize + tileSize * row; Debug.Assert(Numeric.IsZero(terrainTile.OriginX % terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); Debug.Assert(Numeric.IsZero(terrainTile.OriginZ % terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); // Extract the height values from the texture. float[] heights = TerrainHelper.GetTextureLevelSingle(inputHeightTexture, 0); // Transform height values from [0, 1] to [_minHeight, _maxheight]. TerrainHelper.TransformTexture(heights, (_maxHeight - _minHeight), _minHeight); // Optional: Smooth the processed height map. (Very useful to remove artifacts from 8-bit // height maps.) TerrainHelper.SmoothTexture(heights, inputHeightTexture.Width, inputHeightTexture.Height, _smoothness); // We have to create a height and normal texture for each tile. // Reuse existing textures to avoid unnecessary allocation. (XNA can run out of memory // if we allocate to many textures in a short time.) Texture2D heightTexture = terrainTile.HeightTexture; Texture2D normalTexture = terrainTile.NormalTexture; // Convert the height value array into a suitable height texture. TerrainHelper.CreateHeightTexture( _graphicsService.GraphicsDevice, heights, inputHeightTexture.Width, inputHeightTexture.Height, _useNearestNeighborMipmaps, ref heightTexture); // Create a normal texture from the height values. TerrainHelper.CreateNormalTexture( _graphicsService.GraphicsDevice, heights, inputHeightTexture.Width, inputHeightTexture.Height, terrainTile.CellSize, _useNearestNeighborMipmaps, ref normalTexture); // If terrainTile.HeightTexture/NormalTexture was null, then CreateHeight/CreateNormal // has created a new texture. We have to dispose this textures in Dispose(). terrainTile.HeightTexture = heightTexture; terrainTile.NormalTexture = normalTexture; // Set the height field data for collision detection. var heightField = (HeightField)tile.RigidBody.Shape; heightField.OriginX = terrainTile.OriginX; heightField.OriginZ = terrainTile.OriginZ; heightField.WidthX = tileSize; heightField.WidthZ = tileSize; lock (lockObject) { heightField.SetSamples(heights, heightTexture.Width, heightTexture.Height); heightField.Invalidate(); } }); }); // Inform the terrain that the old texture content is invalid. TerrainNode.Terrain.Invalidate(); }