public void RiverErode() { int fn = 10; // furthest neighbor count (how much this affects other pixels) float scale = 0.001f; // global river erosion factor (heuristic) bool[,] hit = new bool[xRes, zRes]; float[,] prevH = thisTerrain.terrainData.GetHeights(0, 0, xRes, zRes); for (int l = waterSpots.Count, i = 0; i < l; i++) { WaterSeed ws = waterSpots[i]; if (ws.erosionStrength <= 0) { continue; // } int basinHitCount = 0; for (int ll = ws.basinVertices.Count, j = 0; j < ll; j++) { int I = 0, J = 0; terrainUVToPixel(ws.basinVertices[j].uv, ref I, ref J); if (hit[I, J]) { continue; } hit[I, J] = true; basinHitCount++; // Compute neighbour# and strength based on steepness and relative length run int N = (int)(ws.basinVertices[j].normal.y * fn * (basinHitCount / 10)) + 1; // affect more neighbors if flatter float strength = ws.erosionStrength * (1 - ws.basinVertices[j].normal.y); // erode less if flatter // Center prevH[J, I] -= scale * strength; for (int u = -N; u <= N; u++) { if (I + u < 0 || I + u > xRes) { continue; // if outside terrain heightmap pixels } for (int v = -N; v <= N; v++) { if (J + v < 0 || J + v > zRes) { continue; // if outside terrain heightmap pixels } prevH[J + v, I + u] -= scale * strength - scale * strength * Mathf.Clamp01((u * u + v * v) / (N * N)); // note flipped i,j here } } } ws.erosionStrength -= 0.1f; } thisTerrain.terrainData.SetHeights(0, 0, prevH); }
/****************************** * DEBUG GIZMO DISPLAY ******************************/ void OnDrawGizmos() { if (riverGizmos) { Gizmos.color = Color.blue; for (int i = 0; i < waterSpots.Count; i++) { WaterSeed ws = waterSpots[i]; for (int j = 0; j < ws.basinVertices.Count; j++) { BasinVertex bv = ws.basinVertices[j]; Gizmos.DrawLine(bv.position, bv.position + 10 * bv.normal); } } } }
// ██╗ ██╗██████╗ ██████╗ █████╗ ████████╗███████╗ // ██║ ██║██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝ // ██║ ██║██████╔╝██║ ██║███████║ ██║ █████╗ // ██║ ██║██╔═══╝ ██║ ██║██╔══██║ ██║ ██╔══╝ // ╚██████╔╝██║ ██████╔╝██║ ██║ ██║ ███████╗ // ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ // Update is called once per frame void Update() { // Pre-update state-based vars frame++; // KEYBOARD INTERACTION if (Input.GetKeyDown(KeyCode.G)) { riverGizmos = !riverGizmos; } // Toggle geological time scale on right mouse click if (Input.GetKeyDown(KeyCode.T)) { toggleGeoTime(); if (geoTimeScale) { storeCurrentPosition(); enableCursor(false); } else { resetPrevPosition(); enableCursor(true); } toggleMainCamera(); } // Change to Terraform mode if (Input.GetKeyDown(KeyCode.P)) { cursorMode = TERRAFORM; } // Change to Mountain mode if (Input.GetKeyDown(KeyCode.M)) { cursorMode = MOUNTAIN; } // Change to Water mode if (Input.GetKeyDown(KeyCode.F)) { cursorMode = WATER; } // Force Erode if (Input.GetKey(KeyCode.E)) { switch (ErosionType) { case (ET.steepness): SteepnessErode(); break; case (ET.wind): WindErode(); break; case (ET.gradientDescent): GradientErode(); break; } } // Recompute textures on ErodeUP if (Input.GetKeyUp(KeyCode.E)) { applyTexture(); } // MOUSE INTERACTIONS // Perform human actions if applicable (disabled for geotime mode) if (humanActions && !geoTimeScale) { // Calculate terrain cursor position RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); bool wasHit = Physics.Raycast(ray, out hit); Vector3 targetPos = new Vector3(hit.point.x, hit.point.y + 5f, hit.point.z); cursorProjector.transform.position = targetPos; // MOUSE BUTTON DOWN if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) { switch (cursorMode) { // set mountain seeds case MOUNTAIN: MountainSeed ms = new MountainSeed(this, hit.point, pullRadius); if (wasHit) { mountainSpots.Add(ms); } if (debugLogs) { Debug.Log(ms.ToString()); } break; case WATER: WaterSeed ws = new WaterSeed(this, hit.point, pullRadius); if (wasHit) { waterSpots.Add(ws); } if (debugLogs) { Debug.Log(ws.ToString()); } break; } } // MOUSE BUTTON HELD DOWN // If any mouse button is pressed, perform actions based on cursorMode if (Input.GetMouseButton(0) || Input.GetMouseButton(1)) { switch (cursorMode) { // push/pull terrain case TERRAFORM: if (wasHit) { humanPullTerrain(hit.point, Input.GetMouseButton(0)); } break; } } // Apply textures on human terraforming mouse up if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1)) { switch (cursorMode) { // push/pull terrain case TERRAFORM: if (wasHit) { //applyTexture(); } break; } } // If scrollwheel, update pullRadius and cursorProjector float wheel = Input.GetAxis("Mouse ScrollWheel"); if (wheel != 0) { pullRadius *= wheel > 0 ? wheelIncrement : (1 / wheelIncrement); cursorProjector.orthographicSize = pullRadius; } } // If on geoTimeScale, what to do if (geoTimeScale) { year += 1000; // Update GUI elements labelContent.text = "Year " + year.ToString() + "..."; // Call the main function with terrain modifications for this mode geoTimeTerrainMod(); } if (Input.GetKeyDown(KeyCode.R)) { resetTerrain(); } }
// ██╗ ██╗██████╗ ██████╗ █████╗ ████████╗███████╗ // ██║ ██║██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝ // ██║ ██║██████╔╝██║ ██║███████║ ██║ █████╗ // ██║ ██║██╔═══╝ ██║ ██║██╔══██║ ██║ ██╔══╝ // ╚██████╔╝██║ ██████╔╝██║ ██║ ██║ ███████╗ // ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ // Update is called once per frame void Update() { // Pre-update state-based vars frame++; // KEYBOARD INTERACTION if (Input.GetKeyDown (KeyCode.G)) { riverGizmos = !riverGizmos; } // Toggle geological time scale on right mouse click if (Input.GetKeyDown(KeyCode.T)) { toggleGeoTime(); if (geoTimeScale) { storeCurrentPosition(); enableCursor(false); } else { resetPrevPosition(); enableCursor(true); } toggleMainCamera(); } // Change to Terraform mode if (Input.GetKeyDown (KeyCode.P)) { cursorMode = TERRAFORM; } // Change to Mountain mode if (Input.GetKeyDown (KeyCode.M)) { cursorMode = MOUNTAIN; } // Change to Water mode if (Input.GetKeyDown (KeyCode.F)) { cursorMode = WATER; } // Force Erode if (Input.GetKey(KeyCode.E)){ switch(ErosionType) { case(ET.steepness): SteepnessErode(); break; case(ET.wind): WindErode(); break; case(ET.gradientDescent): GradientErode(); break; } } // Recompute textures on ErodeUP if (Input.GetKeyUp(KeyCode.E)) { applyTexture (); } // MOUSE INTERACTIONS // Perform human actions if applicable (disabled for geotime mode) if (humanActions && !geoTimeScale) { // Calculate terrain cursor position RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); bool wasHit = Physics.Raycast(ray, out hit); Vector3 targetPos = new Vector3(hit.point.x, hit.point.y + 5f, hit.point.z); cursorProjector.transform.position = targetPos; // MOUSE BUTTON DOWN if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) { switch(cursorMode) { // set mountain seeds case MOUNTAIN: MountainSeed ms = new MountainSeed(this, hit.point, pullRadius); if (wasHit) mountainSpots.Add(ms); if (debugLogs) Debug.Log(ms.ToString()); break; case WATER: WaterSeed ws = new WaterSeed(this, hit.point, pullRadius); if (wasHit) waterSpots.Add(ws); if (debugLogs) Debug.Log(ws.ToString()); break; } } // MOUSE BUTTON HELD DOWN // If any mouse button is pressed, perform actions based on cursorMode if (Input.GetMouseButton(0) || Input.GetMouseButton (1)) { switch(cursorMode) { // push/pull terrain case TERRAFORM: if(wasHit) { humanPullTerrain(hit.point, Input.GetMouseButton(0)); } break; } } // Apply textures on human terraforming mouse up if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp (1)) { switch(cursorMode) { // push/pull terrain case TERRAFORM: if(wasHit) { //applyTexture(); } break; } } // If scrollwheel, update pullRadius and cursorProjector float wheel = Input.GetAxis("Mouse ScrollWheel"); if (wheel != 0) { pullRadius *= wheel > 0 ? wheelIncrement : ( 1/ wheelIncrement); cursorProjector.orthographicSize = pullRadius; } } // If on geoTimeScale, what to do if (geoTimeScale) { year += 1000; // Update GUI elements labelContent.text = "Year " + year.ToString() + "..."; // Call the main function with terrain modifications for this mode geoTimeTerrainMod(); } if (Input.GetKeyDown (KeyCode.R)) { resetTerrain(); } }