private void RemoveTrees(Terrain terrain, IOnPaint editContext, bool clearSelectedOnly) { PaintTreesDetailsContext ctx = PaintTreesDetailsContext.Create(terrain, editContext.uv); for (int i = 0; i < ctx.terrains.Length; ++i) { Terrain ctxTerrain = ctx.terrains[i]; if (ctxTerrain != null) { Vector2 ctxUV = ctx.uvs[i]; float radius = 0.5f * brushSize / ctxTerrain.terrainData.size.x; int treePrototype = kInvalidTree; if (clearSelectedOnly && selectedTree != kInvalidTree) { treePrototype = PaintTreesUtils.FindTreePrototype(ctxTerrain, m_TargetTerrain, selectedTree); } if (!clearSelectedOnly || treePrototype != kInvalidTree) { TerrainPaintUtilityEditor.UpdateTerrainDataUndo(ctxTerrain.terrainData, "Terrain - Remove Trees"); ctxTerrain.RemoveTrees(ctxUV, radius, treePrototype); } } } }
public static PaintTreesDetailsContext Create(Terrain terrain, Vector2 uv) { s_Nbrs[0] = terrain.leftNeighbor; s_Nbrs[1] = terrain.leftNeighbor ? terrain.leftNeighbor.topNeighbor : (terrain.topNeighbor ? terrain.topNeighbor.leftNeighbor : null); s_Nbrs[2] = terrain.topNeighbor; s_Nbrs[3] = terrain.rightNeighbor ? terrain.rightNeighbor.topNeighbor : (terrain.topNeighbor ? terrain.topNeighbor.rightNeighbor : null); s_Nbrs[4] = terrain.rightNeighbor; s_Nbrs[5] = terrain.rightNeighbor ? terrain.rightNeighbor.bottomNeighbor : (terrain.bottomNeighbor ? terrain.bottomNeighbor.rightNeighbor : null); s_Nbrs[6] = terrain.bottomNeighbor; s_Nbrs[7] = terrain.leftNeighbor ? terrain.leftNeighbor.bottomNeighbor : (terrain.bottomNeighbor ? terrain.bottomNeighbor.leftNeighbor : null); s_Uvs[0] = new Vector2(uv.x + 1.0f, uv.y); s_Uvs[1] = new Vector2(uv.x + 1.0f, uv.y - 1.0f); s_Uvs[2] = new Vector2(uv.x, uv.y - 1.0f); s_Uvs[3] = new Vector2(uv.x - 1.0f, uv.y - 1.0f); s_Uvs[4] = new Vector2(uv.x - 1.0f, uv.y); s_Uvs[5] = new Vector2(uv.x - 1.0f, uv.y + 1.0f); s_Uvs[6] = new Vector2(uv.x, uv.y + 1.0f); s_Uvs[7] = new Vector2(uv.x + 1.0f, uv.y + 1.0f); PaintTreesDetailsContext ctx = new PaintTreesDetailsContext(); ctx.terrains[0] = terrain; ctx.uvs[0] = uv; bool left = uv.x < 0.5f; bool right = !left; bool bottom = uv.y < 0.5f; bool top = !bottom; int t = 0; if (right && top) { t = 2; } else if (right && bottom) { t = 4; } else if (left && bottom) { t = 6; } for (int i = 1; i < 4; ++i, t = (t + 1) % 8) { ctx.terrains[i] = s_Nbrs[t]; ctx.uvs[i] = s_Uvs[t]; } return(ctx); }
private void PlaceTrees(Terrain terrain, IOnPaint editContext) { if (m_TargetTerrain == null || selectedTree == kInvalidTree || selectedTree >= m_TargetTerrain.terrainData.treePrototypes.Length) { return; } PaintTreesDetailsContext ctx = PaintTreesDetailsContext.Create(terrain, editContext.uv); int placedTreeCount = 0; int treePrototype = PaintTreesUtils.FindTreePrototype(terrain, m_TargetTerrain, selectedTree); if (treePrototype == kInvalidTree) { treePrototype = PaintTreesUtils.CopyTreePrototype(terrain, m_TargetTerrain, selectedTree); } if (PaintTreesUtils.ValidateTreePrototype(terrain, treePrototype)) { // When painting single tree // And just clicking we always place it, so you can do overlapping trees Vector3 position = new Vector3(editContext.uv.x, 0, editContext.uv.y); bool checkTreeDistance = Event.current.type == EventType.MouseDrag || brushSize > 1; if (!checkTreeDistance || TerrainInspectorUtil.CheckTreeDistance(terrain.terrainData, position, treePrototype, spacing)) { TerrainPaintUtilityEditor.UpdateTerrainDataUndo(terrain.terrainData, "Terrain - Place Trees"); PaintTreesUtils.PlaceTree(terrain, treePrototype, position, GetTreeColor(), GetTreeHeight(), lockWidthToHeight ? GetTreeHeight() : GetTreeWidth(), GetTreeRotation()); ++placedTreeCount; } } for (int i = 0; i < ctx.terrains.Length; ++i) { Terrain ctxTerrain = ctx.terrains[i]; if (ctxTerrain != null) { Vector2 ctxUV = ctx.uvs[i]; treePrototype = PaintTreesUtils.FindTreePrototype(ctxTerrain, m_TargetTerrain, selectedTree); if (treePrototype == kInvalidTree) { treePrototype = PaintTreesUtils.CopyTreePrototype(ctxTerrain, m_TargetTerrain, selectedTree); } if (PaintTreesUtils.ValidateTreePrototype(ctxTerrain, treePrototype)) { Vector3 size = TerrainInspectorUtil.GetPrototypeExtent(ctxTerrain.terrainData, treePrototype); size.y = 0; float treeCountOneAxis = brushSize / (size.magnitude * spacing * .5f); int treeCount = (int)((treeCountOneAxis * treeCountOneAxis) * .5f); treeCount = Mathf.Clamp(treeCount, 0, 100); // Plant a bunch of trees for (int j = ctxTerrain == terrain ? 1 : 0; j < treeCount && placedTreeCount < treeCount; ++j) { Vector2 randomOffset = 0.5f * Random.insideUnitCircle; randomOffset.x *= brushSize / ctxTerrain.terrainData.size.x; randomOffset.y *= brushSize / ctxTerrain.terrainData.size.z; Vector3 position = new Vector3(ctxUV.x + randomOffset.x, 0, ctxUV.y + randomOffset.y); if (position.x >= 0 && position.x <= 1 && position.z >= 0 && position.z <= 1 && TerrainInspectorUtil.CheckTreeDistance(ctxTerrain.terrainData, position, treePrototype, spacing * .5f)) { TerrainPaintUtilityEditor.UpdateTerrainDataUndo(ctxTerrain.terrainData, "Terrain - Place Trees"); PaintTreesUtils.PlaceTree(ctxTerrain, treePrototype, position, GetTreeColor(), GetTreeHeight(), lockWidthToHeight ? GetTreeHeight() : GetTreeWidth(), GetTreeRotation()); ++placedTreeCount; } } } } } }
public override bool OnPaint(Terrain terrain, IOnPaint editContext) { if (m_TargetTerrain == null || selectedDetail == kInvalidDetail || selectedDetail >= m_TargetTerrain.terrainData.detailPrototypes.Length) { return(false); } Texture2D brush = editContext.brushTexture as Texture2D; if (brush == null) { Debug.LogError("Brush texture is not a Texture2D."); return(false); } if (m_BrushRep == null) { m_BrushRep = new BrushRep(); } PaintTreesDetailsContext ctx = PaintTreesDetailsContext.Create(terrain, editContext.uv); for (int t = 0; t < ctx.terrains.Length; ++t) { Terrain ctxTerrain = ctx.terrains[t]; if (ctxTerrain != null) { int detailPrototype = PaintDetailsUtils.FindDetailPrototype(ctxTerrain, m_TargetTerrain, selectedDetail); if (detailPrototype == kInvalidDetail) { detailPrototype = PaintDetailsUtils.CopyDetailPrototype(ctxTerrain, m_TargetTerrain, selectedDetail); } TerrainData terrainData = ctxTerrain.terrainData; TerrainPaintUtilityEditor.UpdateTerrainDataUndo(terrainData, "Terrain - Detail Edit"); int size = (int)Mathf.Max(1.0f, editContext.brushSize * ((float)terrainData.detailResolution / terrainData.size.x)); m_BrushRep.CreateFromBrush(brush, size); Vector2 ctxUV = ctx.uvs[t]; int xCenter = Mathf.FloorToInt(ctxUV.x * terrainData.detailWidth); int yCenter = Mathf.FloorToInt(ctxUV.y * terrainData.detailHeight); int intRadius = Mathf.RoundToInt(size) / 2; int intFraction = Mathf.RoundToInt(size) % 2; int xmin = xCenter - intRadius; int ymin = yCenter - intRadius; int xmax = xCenter + intRadius + intFraction; int ymax = yCenter + intRadius + intFraction; if (xmin >= terrainData.detailWidth || ymin >= terrainData.detailHeight || xmax <= 0 || ymax <= 0) { continue; } xmin = Mathf.Clamp(xmin, 0, terrainData.detailWidth - 1); ymin = Mathf.Clamp(ymin, 0, terrainData.detailHeight - 1); xmax = Mathf.Clamp(xmax, 0, terrainData.detailWidth); ymax = Mathf.Clamp(ymax, 0, terrainData.detailHeight); int width = xmax - xmin; int height = ymax - ymin; float targetStrength = detailStrength; if (Event.current.shift || Event.current.control) { targetStrength = -targetStrength; } int[] layers = { detailPrototype }; if (targetStrength < 0.0F && !Event.current.control) { layers = terrainData.GetSupportedLayers(xmin, ymin, width, height); } for (int i = 0; i < layers.Length; i++) { int[,] alphamap = terrainData.GetDetailLayer(xmin, ymin, width, height, layers[i]); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int xBrushOffset = (xmin + x) - (xCenter - intRadius + intFraction); int yBrushOffset = (ymin + y) - (yCenter - intRadius + intFraction); float opa = detailOpacity * m_BrushRep.GetStrengthInt(xBrushOffset, yBrushOffset); float targetValue = Mathf.Lerp(alphamap[y, x], targetStrength, opa); alphamap[y, x] = Mathf.RoundToInt(targetValue - .5f + Random.value); } } terrainData.SetDetailLayer(xmin, ymin, layers[i], alphamap); } } } return(false); }