private void ShowDetailPrototypeMessages(DetailPrototype detailPrototype, Terrain terrain) { if (!DetailPrototype.IsModeSupportedByRenderPipeline(detailPrototype.renderMode, detailPrototype.useInstancing, out var msg) || !detailPrototype.Validate(out msg)) { EditorGUILayout.HelpBox(msg, MessageType.Error); } else if ((detailPrototype.renderMode != DetailRenderMode.VertexLit || !detailPrototype.useInstancing) && detailPrototype.usePrototypeMesh && detailPrototype.prototype != null && detailPrototype.prototype.TryGetComponent <MeshFilter>(out var meshFilter) && meshFilter.sharedMesh != null) { var maxVertCount = meshFilter.sharedMesh.vertexCount * PaintDetailsUtils.GetMaxDetailInstances(terrain.terrainData) * terrain.detailObjectDensity; if (maxVertCount >= 65536) { EditorGUILayout.HelpBox(s_Styles.detailVertexWarning.text, MessageType.Warning); } } }
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 = m_DetailsStrength; 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 * terrainData.maxDetailScatterPerRes, opa); alphamap[y, x] = Mathf.Min(Mathf.RoundToInt(targetValue - .5f + Random.value), terrainData.maxDetailScatterPerRes); } } terrainData.SetDetailLayer(xmin, ymin, layers[i], alphamap); } } } return(false); }