/// <summary> /// Paint method /// </summary> /// <param name="paintAll"></param> internal override void Paint(bool paintAll) { if (component.Terrain == null) { return; } if (component.TerrainData.detailPrototypes.Length < 2) { return; } int brushSize, halfBrushSize; Point position, startPosition; Texture2D alphaTexture = null; if (paintAll) { // Select whole terrain startPosition.x = startPosition.y = 0; position.x = position.y = 0; halfBrushSize = Mathf.RoundToInt((float)(brushSize = component.TerrainData.detailResolution) / 2.0f); } else { // Select selected part of terrain if (component.Alpha == null || component.Alpha.alphaBrushes == null || component.Alpha.alphaBrushes.Count < 1) { return; } alphaTexture = (Texture2D)component.Alpha; try { alphaTexture.GetPixel(0, 0); } catch { throw new AccessViolationException(TP.ErrorMessages.TEXTURE_NOT_ACCESSABLE); } brushSize = tp_Utilities.WorldToDetail(component.BrushSize * component.BrushSizeMultiplier, component.TerrainData); halfBrushSize = brushSize / 2; Point center = new Point( tp_Utilities.WorldToDetail(component.brushPosition.x - component.Transform.position.x, component.TerrainData), tp_Utilities.WorldToDetail(component.brushPosition.z - component.Transform.position.z, component.TerrainData.size.z, component.TerrainData)); position = center - Point.one * halfBrushSize; startPosition = Point.Max(position, Point.zero); } // Set human readable format to an easier modifiable format float opacity = component.Opacity / 100.0f; // get offset middle brush position to upper left brush position Point offset = startPosition - position; // Some memory stuff, see tp_SplatmapEditor.cs for reason Point current; float height, angle, alpha; float detailmapResolution = component.TerrainData.detailResolution; int x, y; for (int i = 0; i < component.TerrainData.detailPrototypes.Length; i++) { // For all details if (!component.Foliages.IsSelected(i)) { continue; } int[,] data = component.TerrainData.GetDetailLayer( startPosition.x, startPosition.y, Mathf.Max(0, Mathf.Min(position.x + brushSize, component.TerrainData.detailResolution) - startPosition.x), Mathf.Max(0, Mathf.Min(position.y + brushSize, component.TerrainData.detailResolution) - startPosition.y), i); for (y = 0; y < data.GetLength(1); y++) { for (x = 0; x < data.GetLength(0); x++) { current = new Point(y, x); alpha = paintAll ? 1.0f : opacity *GetAlpha((current + offset), new Point(brushSize, brushSize), alphaTexture); if (component.Foliages.UseRandom) { alpha *= UnityEngine.Random.value; } if (component.Foliages.Erase) { // Erase, no need for calculating all the other stuff data[x, y] = Mathf.RoundToInt(Mathf.Lerp(data[x, y], -component.CurrentFoliageStrength, alpha)); continue; } // Get height and angle GetBaseData(current + startPosition, detailmapResolution, out height, out angle); // Mutliply angle value alpha *= 1.0f - angle; // Mulktiply noise value alpha *= GetNoiseValue(current + startPosition, detailmapResolution); // Aplly "pixel" data[x, y] = Mathf.RoundToInt(Mathf.Lerp(data[x, y], component.CurrentFoliageStrength, alpha));//(int)(Mathf.Clamp((paintAll ? 0 : data[x, y]) + alpha * 5.0f, 0.0f, 3.1f)); } } // Apply component.TerrainData.SetDetailLayer(startPosition.x, startPosition.y, i, data); } }
/// <summary> /// Mail paint method /// </summary> /// <param name="paintAll"></param> internal override void Paint(bool paintAll) { #region Init // Safety if (component.Terrain == null) { return; } if (component.TerrainData.splatPrototypes.Length < 2) { return; } if (component.SplatmapLock) { return; } if (component.SplatTool == SplatTool.HeightBased && component.Heights.Count < 1) { return; } // Data int brushSize, halfBrushSize; Point position, startPosition; Texture2D alphaTexture = null; int splatPrototypesCount = component.TerrainData.splatPrototypes.Length; int[] texIndexes = component.Heights.GetAvailablePaintTextures(); if (paintAll) { // Get whole terrain startPosition.x = startPosition.y = 0; position.x = position.y = 0; halfBrushSize = Mathf.RoundToInt((float)(brushSize = component.TerrainData.alphamapResolution) / 2.0f); } else { // Get selected part of terrain if (component.Alpha == null || component.Alpha.alphaBrushes == null || component.Alpha.alphaBrushes.Count < 1) { return; } // Get alpha texture, and test if it can be used alphaTexture = (Texture2D)component.Alpha; try { alphaTexture.GetPixel(0, 0); } catch { throw new AccessViolationException(TP.ErrorMessages.TEXTURE_NOT_ACCESSABLE); } // Brush size brushSize = tp_Utilities.WorldToSplat(component.BrushSize * component.BrushSizeMultiplier, component.TerrainData); halfBrushSize = brushSize / 2;//Utilities.WorldToSplat(m_size / 2.0f, TerrainData); Point center = new Point( tp_Utilities.WorldToSplat(component.brushPosition.x - component.Transform.position.x, component.TerrainData), tp_Utilities.WorldToSplat(component.brushPosition.z - component.Transform.position.z, component.TerrainData.size.z, component.TerrainData)); position = center - Point.one * halfBrushSize; startPosition = Point.Max(position, Point.zero); } // Get current data Point offset = startPosition - position; float[,,] alphaMaps = component.TerrainData.GetAlphamaps( startPosition.x, startPosition.y, Mathf.Max(0, Mathf.Min(position.x + brushSize, component.TerrainData.alphamapResolution) - startPosition.x), Mathf.Max(0, Mathf.Min(position.y + brushSize, component.TerrainData.alphamapResolution) - startPosition.y)); // Safety if (component.Noise.Texture != null) { if (component.Noise.Texture.width != component.TerrainData.alphamapWidth || component.Noise.Texture.height != component.TerrainData.alphamapHeight || !component.Noise.Enabled) { component.Noise.Texture = null; } } /* * I am not completely sure if this makes any difference * Or if the compiler does this automaticly.. * * But this is for optimasation; * we don't want to create a float 1000000 times, so thats why we do it here * */ float[] prevAlpha = new float[splatPrototypesCount]; float opacity = component.Opacity / 100.0f; Point current; float height, angle, alpha, strength; float alphamapResolution = (float)component.TerrainData.alphamapResolution; int y, x; #endregion for (y = 0; y < alphaMaps.GetLength(0); y++) { for (x = 0; x < alphaMaps.GetLength(1); x++) { current = new Point(x, y); GetBaseData(current + startPosition, alphamapResolution, out height, out angle); // Alpha alpha = paintAll ? 1.0f : opacity *GetAlpha(current + offset, Point.one *brushSize, alphaTexture); strength = paintAll ? 1.0f : 1.0f; if (alpha <= 0.0f) { continue; } strength *= GetNoiseValue(current + startPosition, alphamapResolution); if (component.SplatTool == SplatTool.AngleBased && (alphaMaps[y, x, component.Textures.SelectedCliffBrush] > angle * strength || strength * angle <= 0.0f)) { continue; } if (component.SplatTool == SplatTool.OneTexture) { if (alphaMaps[y, x, component.Textures.SelectedTexture] >= (1.0f - angle) * strength && alphaMaps[y, x, component.Textures.SelectedCliffBrush] >= angle * strength) { continue; } } // for (int i = 0; i < splatPrototypesCount; i++) { prevAlpha[i] = alphaMaps[y, x, i]; alphaMaps[y, x, i] = 0.0f; } // switch (component.SplatTool) { case SplatTool.OneTexture: alphaMaps[y, x, component.Textures.SelectedTexture] = 1.0f - (alphaMaps[y, x, component.Textures.SelectedCliffBrush] = angle); alpha *= strength; break; case SplatTool.HeightBased: alphaMaps[y, x, component.Textures.SelectedCliffBrush] = angle; angle = 1.0f - angle; for (int a = 0; a < texIndexes.Length; a++) { int i = texIndexes[a]; if (height < component.Heights[0].y || a == texIndexes.Length - 1) { alphaMaps[y, x, i] += angle; break; } else { int b = a + 1; int j = texIndexes[b]; if (height >= component.Heights[a].y && height < component.Heights[b].x) { float damp = Mathf.InverseLerp(component.Heights[a].y, component.Heights[b].x, height); damp *= Mathf.Lerp(strength, 1.0f, damp); //damp += strength * 2.0f * (Mathf.Abs(damp - 0.5f)); alphaMaps[y, x, i] += angle * (1.0f - damp); alphaMaps[y, x, j] += angle * damp; break; } else if (height >= component.Heights[a].x && height < component.Heights[a].y) { alphaMaps[y, x, i] += angle; break; } } } break; case SplatTool.AngleBased: alpha = Mathf.Clamp(alpha * angle, 0.0f, strength); alphaMaps[y, x, component.Textures.SelectedCliffBrush] = 1.0f; break; case SplatTool.Sharpen: float total = 0.0f; for (int i = 0; i < splatPrototypesCount; i++) { alphaMaps[y, x, i] = prevAlpha[i]; if (component.Textures.IsMultiSelect && !component.Textures.IsSelected(i)) { total += alphaMaps[y, x, i]; continue; } prevAlpha[i] = Mathf.Pow(prevAlpha[i], component.SharpenStrength); alphaMaps[y, x, i] = Mathf.Lerp(alphaMaps[y, x, i], prevAlpha[i], alpha); total += alphaMaps[y, x, i]; } for (int i = 0; i < splatPrototypesCount; i++) { alphaMaps[y, x, i] /= total; } continue; case SplatTool.Blur: int halfRadius = component.SmoothStrength; int radius = halfRadius + halfRadius; float totalb = 0.0f; for (int i = 0; i < splatPrototypesCount; i++) { // if not selected i then continue alphaMaps[y, x, i] = prevAlpha[i]; if (component.Textures.IsMultiSelect && !component.Textures.IsSelected(i)) { totalb += alphaMaps[y, x, i]; continue; } float val = 0.0f; float sum = 0.0f; for (int j = -halfRadius; j <= halfRadius; j++) // y { int yj = y + j; if (yj < 0 || yj >= alphaMaps.GetLength(0)) { continue; } for (int k = -halfRadius; k <= halfRadius; k++) // x { int xk = x + k; if (xk < 0 || xk >= alphaMaps.GetLength(1)) { continue; } float dsq = j * j * k * k; float whg = Mathf.Exp(-dsq / (2.0f * radius * radius)) / (Mathf.PI * 2.0f * radius * radius); val += alphaMaps[yj, xk, i] * whg; sum += whg; } } alphaMaps[y, x, i] = Mathf.Lerp(alphaMaps[y, x, i], val / sum, alpha); totalb += alphaMaps[y, x, i]; } for (int i = 0; i < splatPrototypesCount; i++) { alphaMaps[y, x, i] /= totalb; } continue; } // Set alpha map for (int i = 0; i < splatPrototypesCount; i++) { alphaMaps[y, x, i] *= alpha; alphaMaps[y, x, i] += prevAlpha[i] * (1.0f - alpha); } } } component.TerrainData.SetAlphamaps(startPosition.x, startPosition.y, alphaMaps); }