This commit is contained in:
CortexCore
2025-03-14 21:04:19 +08:00
parent ff8670c453
commit 757ffe79ee
1282 changed files with 104378 additions and 3 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 99df235d1a63c114b8c74607080f9ad4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
#if GRIFFIN
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using Unity.Collections;
using Unity.Jobs;
namespace Pinwheel.Griffin.PaintTool
{
public static class GPaintToolUtilities
{
public static void AddCustomSpawnFilter(List<Type> filters)
{
for (int i = 0; i < GSpawnFilter.AllFilters.Count; ++i)
{
Type t = GSpawnFilter.AllFilters[i];
if (!IsBuiltinFilter(t))
filters.Add(t);
}
}
private static bool IsBuiltinFilter(Type t)
{
return t == typeof(GAlignToSurfaceFilter) ||
t == typeof(GHeightConstraintFilter) ||
t == typeof(GRotationRandomizeFilter) ||
t == typeof(GScaleClampFilter) ||
t == typeof(GScaleRandomizeFilter) ||
t == typeof(GSlopeConstraintFilter);
}
public static Matrix4x4 GetUnitRectToWorldMatrix(Vector3 position, float radius, float rotation)
{
Matrix4x4 m = Matrix4x4.TRS(position, Quaternion.Euler(0, rotation, 0), Vector3.one * radius);
Matrix4x4 offset = Matrix4x4.Translate(new Vector3(-0.5f, 0, -0.5f));
return m * offset;
}
public static List<GOverlapTestResult> OverlapTest(int groupId, Vector3 position, float radius, float rotation)
{
Vector3[] corners = GCommon.GetBrushQuadCorners(position, radius, rotation);
return GCommon.OverlapTest(groupId, corners);
}
public static Rect GetDirtyRect(GStylizedTerrain t, Vector3[] worldCorners)
{
Vector2[] uvCorners = new Vector2[worldCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = t.WorldPointToUV(worldCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
return dirtyRect;
}
public static Vector2[] WorldToUvCorners(GStylizedTerrain t, Vector3[] worldCorners)
{
Vector2[] uvCorners = new Vector2[worldCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = t.WorldPointToUV(worldCorners[i]);
}
return uvCorners;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9cb2040270de7b44b9e31cfc45208142
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if GRIFFIN
namespace Pinwheel.Griffin.PaintTool
{
public enum GPainterActionType
{
Normal, Negative, Alternative
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 977f5fe957b28b441ae4c38659d7bc73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if GRIFFIN
namespace Pinwheel.Griffin.PaintTool
{
public enum GPainterMouseEventType
{
Down, Drag, Up
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b72658925d377924a9088a0ca3394a48
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3be83a406f028364199cf9a7387ae4fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,549 @@
#if GRIFFIN
using UnityEngine;
using System.Collections.Generic;
using Type = System.Type;
using Rand = System.Random;
#if UNITY_EDITOR
using Pinwheel.Griffin.BackupTool;
#endif
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
[ExecuteInEditMode]
public class GFoliagePainter : MonoBehaviour
{
private static readonly List<string> BUILTIN_PAINTER_NAME = new List<string>(new string[]
{
"GTreePainter",
"GTreeScaler",
"GGrassPainter",
"GGrassScaler"
});
private static List<Type> customPainterTypes;
public static List<Type> CustomPainterTypes
{
get
{
if (customPainterTypes == null)
customPainterTypes = new List<Type>();
return customPainterTypes;
}
private set
{
customPainterTypes = value;
}
}
public static string FoliagePainterInterfaceName
{
get
{
return typeof(IGFoliagePainter).Name;
}
}
static GFoliagePainter()
{
RefreshCustomPainterTypes();
}
public static void RefreshCustomPainterTypes()
{
List<Type> loadedTypes = GCommon.GetAllLoadedTypes();
CustomPainterTypes = loadedTypes.FindAll(
t => t.GetInterface(FoliagePainterInterfaceName) != null &&
!BUILTIN_PAINTER_NAME.Contains(t.Name));
}
[SerializeField]
private int groupId;
public int GroupId
{
get
{
return groupId;
}
set
{
groupId = value;
}
}
[SerializeField]
private GFoliagePaintingMode mode;
public GFoliagePaintingMode Mode
{
get
{
return mode;
}
set
{
mode = value;
}
}
[SerializeField]
private int customPainterIndex;
public int CustomPainterIndex
{
get
{
return customPainterIndex;
}
set
{
customPainterIndex = value;
}
}
[SerializeField]
private string customPainterArgs;
public string CustomPainterArgs
{
get
{
return customPainterArgs;
}
set
{
customPainterArgs = value;
}
}
[SerializeField]
private bool enableTerrainMask;
public bool EnableTerrainMask
{
get
{
return enableTerrainMask;
}
set
{
enableTerrainMask = value;
}
}
public IGFoliagePainter ActivePainter
{
get
{
if (Mode == GFoliagePaintingMode.PaintTree)
{
return new GTreePainter();
}
else if (Mode == GFoliagePaintingMode.ScaleTree)
{
return new GTreeScaler();
}
else if (Mode == GFoliagePaintingMode.PaintGrass)
{
return new GGrassPainter();
}
else if (Mode == GFoliagePaintingMode.ScaleGrass)
{
return new GGrassScaler();
}
else if (mode == GFoliagePaintingMode.Custom)
{
if (CustomPainterIndex >= 0 && CustomPainterIndex < CustomPainterTypes.Count)
return System.Activator.CreateInstance(CustomPainterTypes[CustomPainterIndex]) as IGFoliagePainter;
}
return null;
}
}
[SerializeField]
private float brushRadius;
public float BrushRadius
{
get
{
return brushRadius;
}
set
{
brushRadius = Mathf.Max(0.01f, value);
}
}
[SerializeField]
private float brushRadiusJitter;
public float BrushRadiusJitter
{
get
{
return brushRadiusJitter;
}
set
{
brushRadiusJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushRotation;
public float BrushRotation
{
get
{
return brushRotation;
}
set
{
brushRotation = value;
}
}
[SerializeField]
private float brushRotationJitter;
public float BrushRotationJitter
{
get
{
return brushRotationJitter;
}
set
{
brushRotationJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private int brushDensity;
public int BrushDensity
{
get
{
return brushDensity;
}
set
{
brushDensity = Mathf.Clamp(value, 1, 100);
}
}
[SerializeField]
private float brushDensityJitter;
public float BrushDensityJitter
{
get
{
return brushDensityJitter;
}
set
{
brushDensityJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushScatter;
public float BrushScatter
{
get
{
return brushScatter;
}
set
{
brushScatter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushScatterJitter;
public float BrushScatterJitter
{
get
{
return brushScatterJitter;
}
set
{
brushScatterJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private List<Texture2D> brushMasks;
public List<Texture2D> BrushMasks
{
get
{
if (brushMasks == null)
brushMasks = new List<Texture2D>();
return brushMasks;
}
set
{
brushMasks = value;
}
}
[SerializeField]
private int selectedBrushMaskIndex;
public int SelectedBrushMaskIndex
{
get
{
return selectedBrushMaskIndex;
}
set
{
if (BrushMasks.Count > 0)
selectedBrushMaskIndex = Mathf.Clamp(value, 0, BrushMasks.Count);
else
selectedBrushMaskIndex = -1;
}
}
[SerializeField]
private List<int> selectedTreeIndices;
public List<int> SelectedTreeIndices
{
get
{
if (selectedTreeIndices == null)
{
selectedTreeIndices = new List<int>();
}
return selectedTreeIndices;
}
set
{
selectedTreeIndices = value;
}
}
[SerializeField]
private List<int> selectedGrassIndices;
public List<int> SelectedGrassIndices
{
get
{
if (selectedGrassIndices == null)
{
selectedGrassIndices = new List<int>();
}
return selectedGrassIndices;
}
set
{
selectedGrassIndices = value;
}
}
[SerializeField]
private float eraseRatio;
public float EraseRatio
{
get
{
return eraseRatio;
}
set
{
eraseRatio = Mathf.Clamp01(value);
}
}
[SerializeField]
private float scaleStrength;
public float ScaleStrength
{
get
{
return scaleStrength;
}
set
{
scaleStrength = Mathf.Max(0, value);
}
}
private void OnEnable()
{
ReloadBrushMasks();
}
private void Reset()
{
GroupId = 0;
Mode = GFoliagePaintingMode.PaintTree;
BrushRadius = 50;
BrushRadiusJitter = 0;
BrushDensity = 1;
BrushDensityJitter = 0;
BrushRotation = 0;
BrushRotationJitter = 0;
EraseRatio = 1;
ScaleStrength = 1;
}
public void ReloadBrushMasks()
{
BrushMasks = new List<Texture2D>(Resources.LoadAll<Texture2D>(GCommon.BRUSH_MASK_RESOURCES_PATH));
}
public void Paint(GFoliagePainterArgs args)
{
IGFoliagePainter p = ActivePainter;
if (p == null)
return;
args.Radius = BrushRadius;
args.Rotation = BrushRotation;
args.Density = BrushDensity;
args.EraseRatio = EraseRatio;
args.ScaleStrength = ScaleStrength;
args.TreeIndices = SelectedTreeIndices;
args.GrassIndices = SelectedGrassIndices;
args.CustomArgs = CustomPainterArgs;
if (SelectedBrushMaskIndex >= 0 && SelectedBrushMaskIndex < BrushMasks.Count)
{
args.Mask = BrushMasks[SelectedBrushMaskIndex];
}
args.Filters = GetComponents<GSpawnFilter>();
args.EnableTerrainMask = EnableTerrainMask;
ProcessBrushDynamic(ref args);
Vector3[] corners = GCommon.GetBrushQuadCorners(args.HitPoint, args.Radius, args.Rotation);
args.WorldPointCorners = corners;
List<GStylizedTerrain> overlappedTerrain = GUtilities.ExtractTerrainsFromOverlapTest(GPaintToolUtilities.OverlapTest(GroupId, args.HitPoint, args.Radius, args.Rotation));
#if UNITY_EDITOR
if ((args.MouseEventType == GPainterMouseEventType.Down ||
args.MouseEventType == GPainterMouseEventType.Drag) &&
args.ShouldCommitNow == false)
{
Editor_CreateInitialHistoryEntry(args, overlappedTerrain);
}
#endif
foreach (GStylizedTerrain t in overlappedTerrain)
{
p.Paint(t, args);
}
#if UNITY_EDITOR
EditedTerrains.UnionWith(overlappedTerrain);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
Editor_CreateHistory(args);
currentInitialBackupName = null;
InitialRecordedTerrains.Clear();
EditedTerrains.Clear();
}
#endif
}
#if UNITY_EDITOR
private HashSet<GStylizedTerrain> initialRecordedTerrains;
private HashSet<GStylizedTerrain> InitialRecordedTerrains
{
get
{
if (initialRecordedTerrains == null)
{
initialRecordedTerrains = new HashSet<GStylizedTerrain>();
}
return initialRecordedTerrains;
}
}
private HashSet<GStylizedTerrain> editedTerrains;
private HashSet<GStylizedTerrain> EditedTerrains
{
get
{
if (editedTerrains == null)
{
editedTerrains = new HashSet<GStylizedTerrain>();
}
return editedTerrains;
}
}
private string currentInitialBackupName;
private void Editor_CreateInitialHistoryEntry(GFoliagePainterArgs args, List<GStylizedTerrain> overlappedTerrains)
{
if (!GEditorSettings.Instance.paintTools.enableHistory)
return;
if (overlappedTerrains.Count == 0)
return;
List<GTerrainResourceFlag> flags = new List<GTerrainResourceFlag>();
flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));
if (InitialRecordedTerrains.Count == 0)
{
currentInitialBackupName = GBackup.TryCreateInitialBackup(ActivePainter.HistoryPrefix, overlappedTerrains[0], flags, false);
if (!string.IsNullOrEmpty(currentInitialBackupName))
{
InitialRecordedTerrains.Add(overlappedTerrains[0]);
}
}
else
{
if (!string.IsNullOrEmpty(currentInitialBackupName))
{
for (int i = 0; i < overlappedTerrains.Count; ++i)
{
if (InitialRecordedTerrains.Contains(overlappedTerrains[i]))
continue;
GBackup.BackupTerrain(overlappedTerrains[i], currentInitialBackupName, flags);
InitialRecordedTerrains.Add(overlappedTerrains[i]);
}
}
}
}
private void Editor_CreateHistory(GFoliagePainterArgs args)
{
if (!GEditorSettings.Instance.paintTools.enableHistory)
return;
if (EditedTerrains.Count == 0)
return;
List<GTerrainResourceFlag> flags = new List<GTerrainResourceFlag>();
flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));
List<GStylizedTerrain> terrainList = new List<GStylizedTerrain>(EditedTerrains);
string backupName = GBackup.TryCreateBackup(ActivePainter.HistoryPrefix, terrainList[0], flags, false);
if (!string.IsNullOrEmpty(backupName))
{
for (int i = 1; i < terrainList.Count; ++i)
{
GBackup.BackupTerrain(terrainList[i], backupName, flags);
}
}
}
#endif
private Rand GetRandomGenerator()
{
return new Rand(Time.frameCount);
}
private void ProcessBrushDynamic(ref GFoliagePainterArgs args)
{
Rand rand = GetRandomGenerator();
args.Radius -= BrushRadius * BrushRadiusJitter * (float)rand.NextDouble();
args.Rotation += Mathf.Sign((float)rand.NextDouble() - 0.5f) * BrushRotation * BrushRotationJitter * (float)rand.NextDouble();
args.Density -= Mathf.RoundToInt(BrushDensity * BrushDensityJitter * (float)rand.NextDouble());
Vector3 scatterDir = new Vector3((float)(rand.NextDouble() * 2 - 1), 0, (float)(rand.NextDouble() * 2 - 1)).normalized;
float scatterLengthMultiplier = BrushScatter - (float)rand.NextDouble() * BrushScatterJitter;
float scatterLength = args.Radius * scatterLengthMultiplier;
args.HitPoint += scatterDir * scatterLength;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e2008cbec175ad4ebdeee8f0afc9a7e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public struct GFoliagePainterArgs
{
public Vector3 HitPoint { get; set; }
public Vector3[] WorldPointCorners { get; internal set; }
public Texture2D Mask { get; set; }
public bool EnableTerrainMask { get; set; }
public float Radius { get; internal set; }
public float Rotation { get; internal set; }
public int Density { get; internal set; }
public float EraseRatio { get; internal set; }
public float ScaleStrength { get; internal set; }
public List<int> TreeIndices { get; set; }
public List<int> GrassIndices { get; set; }
public string CustomArgs { get; internal set; }
public GPainterMouseEventType MouseEventType { get; set; }
public GPainterActionType ActionType { get; set; }
public GSpawnFilter[] Filters { get; internal set; }
public bool ShouldCommitNow { get; set; }
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0e54795f5f5fcb4eb25ef58da3ba1b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if GRIFFIN
namespace Pinwheel.Griffin.PaintTool
{
public enum GFoliagePaintingMode
{
PaintTree, ScaleTree, PaintGrass, ScaleGrass, Custom
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea1a8b6e9c28a3c4e8aa0a0ed33395ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,352 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
public class GGrassPainter : IGFoliagePainter
{
public string Instruction
{
get
{
string s = string.Format(
"Paint grass onto the terrain.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase instances of the same type.\n" +
" - Hold Shift & Left Mouse to erase all instances in range.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Paint Grass";
}
}
public List<Type> SuitableFilterTypes
{
get
{
List<Type> types = new List<Type>(new Type[]
{
typeof(GHeightConstraintFilter),
typeof(GSlopeConstraintFilter),
typeof(GRotationRandomizeFilter),
typeof(GScaleRandomizeFilter),
typeof(GScaleClampFilter)
});
GPaintToolUtilities.AddCustomSpawnFilter(types);
return types;
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GFoliagePainterArgs args)
{
return GCommon.GrassInstancesResourceFlags;
}
public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GFoliagePainterArgs args)
{
if (args.GrassIndices.Count == 0)
return;
if (terrain.TerrainData == null)
return;
if (terrain.TerrainData.Foliage.Grasses == null)
return;
if (args.MouseEventType == GPainterMouseEventType.Up || args.ShouldCommitNow)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
GRuntimeSettings.Instance.isEditingFoliage = false;
return;
}
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
return;
GRuntimeSettings.Instance.isEditingFoliage = true;
if (args.ActionType == GPainterActionType.Normal)
{
HandleSpawnGrass(terrain, args);
}
else
{
HandleEraseGrass(terrain, args);
}
GUtilities.MarkCurrentSceneDirty();
}
private void HandleSpawnGrass(GStylizedTerrain terrain, GFoliagePainterArgs args)
{
int grassIndex = -1;
Vector3 randomPos = Vector3.zero;
Vector3 rayOrigin = Vector3.zero;
Vector3 rayDirection = Vector3.down;
float sqrtTwo = Mathf.Sqrt(2);
Ray ray = new Ray();
RaycastHit samplePoint;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Vector2 samplePointTexcoord = Vector2.zero;
Color maskColor = Color.white;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
int prototypeCount = terrain.TerrainData.Foliage.Grasses.Prototypes.Count;
int sampleCount = args.Density;
List<GGrassInstance> newInstances = new List<GGrassInstance>();
for (int i = 0; i < sampleCount; ++i)
{
grassIndex = args.GrassIndices[Random.Range(0, args.GrassIndices.Count)];
if (grassIndex < 0 || grassIndex >= prototypeCount)
continue;
GGrassPrototype proto = terrain.TerrainData.Foliage.Grasses.Prototypes[grassIndex];
randomPos = args.HitPoint + Random.insideUnitSphere * args.Radius * sqrtTwo;
rayOrigin.Set(
randomPos.x,
10000,
randomPos.z);
ray.origin = rayOrigin;
ray.direction = rayDirection;
bool isRaycastHit = false;
if (terrain.TerrainData.Foliage.GrassSnapMode == GSnapMode.Terrain)
{
isRaycastHit = terrain.Raycast(ray, out samplePoint, float.MaxValue);
}
else
{
LayerMask mask = LayerMask.GetMask(GStylizedTerrain.RAYCAST_LAYER);
mask |= terrain.TerrainData.Foliage.GrassSnapLayerMask;
int raycastLayer = mask.value;
isRaycastHit = Physics.Raycast(ray, out samplePoint, float.MaxValue, raycastLayer);
}
if (isRaycastHit)
{
GUtilities.CalculateBarycentricCoord(
new Vector2(samplePoint.point.x, samplePoint.point.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(samplePoint.point.x, samplePoint.point.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
continue;
}
//sample mask
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale)
continue;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
samplePointTexcoord = samplePoint.textureCoord;
maskColor = terrainMask.GetPixelBilinear(samplePointTexcoord.x, samplePointTexcoord.y);
if (Random.value < maskColor.r)
continue;
}
//apply filter
GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
filterArgs.Terrain = terrain;
filterArgs.Position = samplePoint.point;
filterArgs.SurfaceNormal = samplePoint.normal;
filterArgs.SurfaceTexcoord = samplePoint.textureCoord;
List<Type> suitableFilter = SuitableFilterTypes;
if (args.Filters != null)
{
for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
{
if (args.Filters[fIndex] != null &&
args.Filters[fIndex].Ignore != true)
{
if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
args.Filters[fIndex].Apply(ref filterArgs);
}
if (filterArgs.ShouldExclude)
break;
}
}
Quaternion rot = filterArgs.Rotation;
if (proto.AlignToSurface)
{
Quaternion currentRotationY = Quaternion.Euler(0, rot.eulerAngles.y, 0);
Quaternion rotationXZ = Quaternion.FromToRotation(Vector3.up, samplePoint.normal);
rot = rotationXZ * currentRotationY;
}
else
{
Quaternion currentRotationY = Quaternion.Euler(0, rot.eulerAngles.y, 0);
rot = currentRotationY;
}
filterArgs.Rotation = rot;
//spawn
if (filterArgs.ShouldExclude)
continue;
GGrassInstance grass = GGrassInstance.Create(grassIndex);
grass.Position = terrain.WorldPointToNormalized(filterArgs.Position);
grass.Rotation = filterArgs.Rotation;
grass.Scale = filterArgs.Scale;
newInstances.Add(grass);
}
}
terrain.TerrainData.Foliage.AddGrassInstances(newInstances);
if (clonedMask != null)
Object.DestroyImmediate(clonedMask);
}
private void HandleEraseGrass(GStylizedTerrain terrain, GFoliagePainterArgs args)
{
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int grassIndex = -1;
Vector3 terrainSize = new Vector3(
terrain.TerrainData.Geometry.Width,
terrain.TerrainData.Geometry.Height,
terrain.TerrainData.Geometry.Length);
Vector3 localPos = Vector3.zero;
Vector3 worldPos = Vector3.zero;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Color maskColor = Color.white;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
GGrassPatch[] patches = terrain.TerrainData.Foliage.GrassPatches;
for (int i = 0; i < patches.Length; ++i)
{
if (!patches[i].GetUvRange().Overlaps(dirtyRect))
continue;
if (args.ActionType == GPainterActionType.Alternative)
patches[i].RequireFullUpdate = true;
patches[i].RemoveInstances(grass =>
{
grassIndex = args.GrassIndices[Random.Range(0, args.GrassIndices.Count)];
localPos.Set(
grass.position.x * terrainSize.x,
grass.position.y * terrainSize.y,
grass.position.z * terrainSize.z);
worldPos = terrain.transform.TransformPoint(localPos);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
return false;
}
//sample mask
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale * args.EraseRatio)
return false;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
maskColor = terrainMask.GetPixelBilinear(grass.position.x, grass.position.z);
if (Random.value < maskColor.r)
return false;
}
if (args.ActionType == GPainterActionType.Negative &&
grass.PrototypeIndex == grassIndex)
{
return true;
}
else if (args.ActionType == GPainterActionType.Alternative)
{
return true;
}
return false;
});
}
if (clonedMask != null)
Object.DestroyImmediate(clonedMask);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6eb2bfcbecc9f74997beb3f47eb93f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,196 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
public class GGrassScaler : IGFoliagePainter
{
public string Instruction
{
get
{
string s = string.Format(
"Scale grass instances.\n" +
" - Hold Left Mouse to scale up.\n" +
" - Hold Ctrl & Left Mouse to scale down.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Scale Grass";
}
}
public List<Type> SuitableFilterTypes
{
get
{
List<Type> types = new List<Type>(new Type[]
{
typeof(GScaleClampFilter)
});
GPaintToolUtilities.AddCustomSpawnFilter(types);
return types;
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GFoliagePainterArgs args)
{
return GCommon.GrassInstancesResourceFlags;
}
public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GFoliagePainterArgs args)
{
if (args.GrassIndices.Count == 0)
return;
if (terrain.TerrainData == null)
return;
if (terrain.TerrainData.Foliage.Grasses == null)
return;
if (args.MouseEventType == GPainterMouseEventType.Up || args.ShouldCommitNow)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
GRuntimeSettings.Instance.isEditingFoliage = false;
return;
}
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
return;
GRuntimeSettings.Instance.isEditingFoliage = true;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
int multiplier = args.ActionType == GPainterActionType.Normal ? 1 : -1;
int grassIndex = -1;
Vector3 terrainSize = new Vector3(
terrain.TerrainData.Geometry.Width,
terrain.TerrainData.Geometry.Height,
terrain.TerrainData.Geometry.Length);
Vector3 localPos = Vector3.zero;
Vector3 worldPos = Vector3.zero;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Color maskColor = Color.white;
Vector3 scale = Vector3.zero;
GGrassPatch[] patches = terrain.TerrainData.Foliage.GrassPatches;
for (int p = 0; p < patches.Length; ++p)
{
if (!patches[p].GetUvRange().Overlaps(dirtyRect))
continue;
List<GGrassInstance> instances = patches[p].Instances;
int instanceCount = instances.Count;
for (int i = 0; i < instanceCount; ++i)
{
grassIndex = args.GrassIndices[Random.Range(0, args.GrassIndices.Count)];
GGrassInstance grass = instances[i];
if (grass.PrototypeIndex != grassIndex)
continue;
localPos.Set(
grass.position.x * terrainSize.x,
grass.position.y * terrainSize.y,
grass.position.z * terrainSize.z);
worldPos = terrain.transform.TransformPoint(localPos);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
continue;
}
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale)
continue;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
maskColor = terrainMask.GetPixelBilinear(grass.position.x, grass.position.z);
if (Random.value < maskColor.r)
continue;
}
scale.Set(
Mathf.Max(0, grass.scale.x + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
Mathf.Max(0, grass.scale.y + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
Mathf.Max(0, grass.scale.z + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME));
GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
filterArgs.Terrain = terrain;
filterArgs.Position = worldPos;
filterArgs.Rotation = grass.Rotation;
filterArgs.Scale = scale;
List<Type> suitableFilter = SuitableFilterTypes;
if (args.Filters != null)
{
for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
{
if (args.Filters[fIndex] != null &&
args.Filters[fIndex].Ignore != true)
{
if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
args.Filters[fIndex].Apply(ref filterArgs);
}
if (filterArgs.ShouldExclude)
break;
}
}
grass.scale = filterArgs.Scale;
instances[i] = grass;
}
patches[p].Changed();
}
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
GUtilities.MarkCurrentSceneDirty();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3af3c0404f111ae4e81e320eb236046e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,325 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
public class GTreePainter : IGFoliagePainter
{
public string Instruction
{
get
{
string s = string.Format(
"Paint tree onto the terrain.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase instances of the same type.\n" +
" - Hold Shift & Left Mouse to erase all instances in range.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Paint Tree";
}
}
public List<Type> SuitableFilterTypes
{
get
{
List<Type> types = new List<Type>(new Type[]
{
typeof(GHeightConstraintFilter),
typeof(GSlopeConstraintFilter),
typeof(GRotationRandomizeFilter),
typeof(GScaleRandomizeFilter),
typeof(GScaleClampFilter)
});
GPaintToolUtilities.AddCustomSpawnFilter(types);
return types;
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GFoliagePainterArgs args)
{
return GCommon.TreeInstancesResourceFlags;
}
public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GFoliagePainterArgs args)
{
if (args.TreeIndices.Count == 0)
return;
if (terrain.TerrainData == null)
return;
if (terrain.TerrainData.Foliage.Trees == null)
return;
if (args.MouseEventType == GPainterMouseEventType.Up || args.ShouldCommitNow)
{
GCommon.SetDirty(terrain.TerrainData.Foliage);
terrain.UpdateTreesPosition();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
GRuntimeSettings.Instance.isEditingFoliage = false;
return;
}
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
return;
GRuntimeSettings.Instance.isEditingFoliage = true;
if (args.ActionType == GPainterActionType.Normal)
{
HandleSpawnTree(terrain, args);
}
else
{
HandleEraseTree(terrain, args);
}
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
GUtilities.MarkCurrentSceneDirty();
}
private void HandleSpawnTree(GStylizedTerrain terrain, GFoliagePainterArgs args)
{
int treeIndex = -1;
Vector3 randomPos = Vector3.zero;
Vector3 rayOrigin = Vector3.zero;
Vector3 rayDirection = Vector3.down;
float sqrtTwo = Mathf.Sqrt(2);
Ray ray = new Ray();
RaycastHit samplePoint;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Vector2 samplePointTexcoord = Vector2.zero;
Color maskColor = Color.white;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
int prototypeCount = terrain.TerrainData.Foliage.Trees.Prototypes.Count;
List<GTreeInstance> newInstances = new List<GTreeInstance>();
for (int i = 0; i < args.Density; ++i)
{
treeIndex = args.TreeIndices[Random.Range(0, args.TreeIndices.Count)];
if (treeIndex < 0 || treeIndex >= prototypeCount)
continue;
randomPos = args.HitPoint + Random.insideUnitSphere * args.Radius * sqrtTwo;
rayOrigin.Set(
randomPos.x,
10000,
randomPos.z);
ray.origin = rayOrigin;
ray.direction = rayDirection;
bool isRaycastHit = false;
if (terrain.TerrainData.Foliage.TreeSnapMode == GSnapMode.Terrain)
{
isRaycastHit = terrain.Raycast(ray, out samplePoint, float.MaxValue);
}
else
{
LayerMask mask = LayerMask.GetMask(GStylizedTerrain.RAYCAST_LAYER);
mask |= terrain.TerrainData.Foliage.TreeSnapLayerMask;
int raycastLayer = mask.value;
isRaycastHit = Physics.Raycast(ray, out samplePoint, float.MaxValue, raycastLayer);
}
if (isRaycastHit)
{
GUtilities.CalculateBarycentricCoord(
new Vector2(samplePoint.point.x, samplePoint.point.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(samplePoint.point.x, samplePoint.point.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
continue;
}
//sample mask
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale)
continue;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
samplePointTexcoord = samplePoint.textureCoord;
maskColor = terrainMask.GetPixelBilinear(samplePointTexcoord.x, samplePointTexcoord.y);
if (Random.value < maskColor.r)
continue;
}
//apply filter
GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
filterArgs.Terrain = terrain;
filterArgs.Position = samplePoint.point;
filterArgs.SurfaceNormal = samplePoint.normal;
filterArgs.SurfaceTexcoord = samplePoint.textureCoord;
List<Type> suitableFilter = SuitableFilterTypes;
if (args.Filters != null)
{
for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
{
if (args.Filters[fIndex] != null &&
args.Filters[fIndex].Ignore != true)
{
if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
args.Filters[fIndex].Apply(ref filterArgs);
}
if (filterArgs.ShouldExclude)
break;
}
}
//spawn
if (filterArgs.ShouldExclude)
continue;
GTreeInstance tree = GTreeInstance.Create(treeIndex);
tree.Position = terrain.WorldPointToNormalized(filterArgs.Position);
tree.Rotation = filterArgs.Rotation;
tree.Scale = filterArgs.Scale;
newInstances.Add(tree);
}
}
terrain.TerrainData.Foliage.AddTreeInstances(newInstances);
newInstances.Clear();
if (clonedMask != null)
Object.DestroyImmediate(clonedMask);
}
private void HandleEraseTree(GStylizedTerrain terrain, GFoliagePainterArgs args)
{
int treeIndex = -1;
Vector3 terrainSize = new Vector3(
terrain.TerrainData.Geometry.Width,
terrain.TerrainData.Geometry.Height,
terrain.TerrainData.Geometry.Length);
Vector3 localPos = Vector3.zero;
Vector3 worldPos = Vector3.zero;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Color maskColor = Color.white;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
terrain.TerrainData.Foliage.RemoveTreeInstances(tree =>
{
treeIndex = args.TreeIndices[Random.Range(0, args.TreeIndices.Count)];
localPos.Set(
tree.position.x * terrainSize.x,
tree.position.y * terrainSize.y,
tree.position.z * terrainSize.z);
worldPos = terrain.transform.TransformPoint(localPos);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
return false;
}
//sample mask
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale * args.EraseRatio)
return false;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
maskColor = terrainMask.GetPixelBilinear(tree.position.x, tree.position.z);
if (Random.value < maskColor.r)
return false;
}
if (args.ActionType == GPainterActionType.Negative &&
tree.PrototypeIndex == treeIndex)
{
return true;
}
else if (args.ActionType == GPainterActionType.Alternative)
{
return true;
}
return false;
});
if (clonedMask != null)
Object.DestroyImmediate(clonedMask);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7ecf6fdd323a96479394d1e960eba61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,190 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
public class GTreeScaler : IGFoliagePainter
{
public string Instruction
{
get
{
string s = string.Format(
"Scale tree instances.\n" +
" - Hold Left Mouse to scale up.\n" +
" - Hold Ctrl & Left Mouse to scale down.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Scale Tree";
}
}
public List<Type> SuitableFilterTypes
{
get
{
List<Type> types = new List<Type>(new Type[]
{
typeof(GScaleClampFilter)
});
GPaintToolUtilities.AddCustomSpawnFilter(types);
return types;
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GFoliagePainterArgs args)
{
return GCommon.TreeInstancesResourceFlags;
}
public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GFoliagePainterArgs args)
{
if (args.TreeIndices.Count == 0)
return;
if (terrain.TerrainData == null)
return;
if (terrain.TerrainData.Foliage.Trees == null)
return;
if (args.MouseEventType == GPainterMouseEventType.Up || args.ShouldCommitNow)
{
terrain.UpdateTreesPosition();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
GCommon.SetDirty(terrain.TerrainData.Foliage);
GRuntimeSettings.Instance.isEditingFoliage = false;
return;
}
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
return;
GRuntimeSettings.Instance.isEditingFoliage = true;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
int multiplier = args.ActionType == GPainterActionType.Normal ? 1 : -1;
int treeIndex = -1;
Vector3 terrainSize = new Vector3(
terrain.TerrainData.Geometry.Width,
terrain.TerrainData.Geometry.Height,
terrain.TerrainData.Geometry.Length);
Vector3 localPos = Vector3.zero;
Vector3 worldPos = Vector3.zero;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Color maskColor = Color.white;
Vector3 scale = Vector3.zero;
List<GTreeInstance> instances = terrain.TerrainData.Foliage.TreeInstances;
int instanceCount = instances.Count;
for (int i = 0; i < instanceCount; ++i)
{
treeIndex = args.TreeIndices[Random.Range(0, args.TreeIndices.Count)];
GTreeInstance tree = instances[i];
if (tree.PrototypeIndex != treeIndex)
continue;
localPos.Set(
tree.position.x * terrainSize.x,
tree.position.y * terrainSize.y,
tree.position.z * terrainSize.z);
worldPos = terrain.transform.TransformPoint(localPos);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
continue;
}
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale)
continue;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
maskColor = terrainMask.GetPixelBilinear(tree.position.x, tree.position.z);
if (Random.value < maskColor.r)
continue;
}
scale.Set(
Mathf.Max(0, tree.scale.x + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
Mathf.Max(0, tree.scale.y + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
Mathf.Max(0, tree.scale.z + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME));
GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
filterArgs.Terrain = terrain;
filterArgs.Position = worldPos;
filterArgs.Rotation = tree.Rotation;
filterArgs.Scale = scale;
List<Type> suitableFilter = SuitableFilterTypes;
if (args.Filters != null)
{
for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
{
if (args.Filters[fIndex] != null &&
args.Filters[fIndex].Ignore != true)
{
if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
args.Filters[fIndex].Apply(ref filterArgs);
}
if (filterArgs.ShouldExclude)
break;
}
}
tree.scale = filterArgs.Scale;
instances[i] = tree;
}
terrain.TerrainData.Foliage.TreeAllChanged();
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
GUtilities.MarkCurrentSceneDirty();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db6e69be87ee1bf40a74abc0b5b49946
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
#if GRIFFIN
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
public interface IGFoliagePainter
{
string HistoryPrefix { get; }
string Instruction { get; }
List<System.Type> SuitableFilterTypes { get; }
void Paint(GStylizedTerrain terrain, GFoliagePainterArgs args);
List<GTerrainResourceFlag> GetResourceFlagForHistory(GFoliagePainterArgs args);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71739752b5c2e58449325280ae8898e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b7a5da4597a2f1f4f84776a737a31d75
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,201 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GAlbedoPainter : IGTexturePainter, IGTexturePainterWithLivePreview, IConditionalPainter
{
private static readonly int COLOR = Shader.PropertyToID("_Color");
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MAIN_TEX_L = Shader.PropertyToID("_MainTex_Left");
private static readonly int MAIN_TEX_TL = Shader.PropertyToID("_MainTex_TopLeft");
private static readonly int MAIN_TEX_T = Shader.PropertyToID("_MainTex_Top");
private static readonly int MAIN_TEX_TR = Shader.PropertyToID("_MainTex_TopRight");
private static readonly int MAIN_TEX_R = Shader.PropertyToID("_MainTex_Right");
private static readonly int MAIN_TEX_RB = Shader.PropertyToID("_MainTex_BottomRight");
private static readonly int MAIN_TEX_B = Shader.PropertyToID("_MainTex_Bottom");
private static readonly int MAIN_TEX_BL = Shader.PropertyToID("_MainTex_BottomLeft");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.albedoPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Modify terrain Albedo Map color.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase.\n" +
" - Hold Shift & Left Mouse to smooth.\n" +
"Use a material that utilizes Albedo Map to see the result.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Albedo Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.AlbedoResourceFlags;
}
private void SetupTextureGrid(GStylizedTerrain t, Material mat)
{
mat.SetTexture(MAIN_TEX_L,
t.LeftNeighbor && t.LeftNeighbor.TerrainData ?
t.LeftNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.LeftNeighbor && t.LeftNeighbor.TopNeighbor && t.LeftNeighbor.TopNeighbor.TerrainData ?
t.LeftNeighbor.TopNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.TopNeighbor && t.TopNeighbor.LeftNeighbor && t.TopNeighbor.LeftNeighbor.TerrainData ?
t.TopNeighbor.LeftNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_T,
t.TopNeighbor && t.TopNeighbor.TerrainData ?
t.TopNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.RightNeighbor && t.RightNeighbor.TopNeighbor && t.RightNeighbor.TopNeighbor.TerrainData ?
t.RightNeighbor.TopNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.TopNeighbor && t.TopNeighbor.RightNeighbor && t.TopNeighbor.RightNeighbor.TerrainData ?
t.TopNeighbor.RightNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_R,
t.RightNeighbor && t.RightNeighbor.TerrainData ?
t.RightNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.RightNeighbor && t.RightNeighbor.BottomNeighbor && t.RightNeighbor.BottomNeighbor.TerrainData ?
t.RightNeighbor.BottomNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.BottomNeighbor && t.BottomNeighbor.RightNeighbor && t.BottomNeighbor.RightNeighbor.TerrainData ?
t.BottomNeighbor.RightNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_B,
t.BottomNeighbor && t.BottomNeighbor.TerrainData ?
t.BottomNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.LeftNeighbor && t.LeftNeighbor.BottomNeighbor && t.LeftNeighbor.BottomNeighbor.TerrainData ?
t.LeftNeighbor.BottomNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.BottomNeighbor && t.BottomNeighbor.LeftNeighbor && t.BottomNeighbor.LeftNeighbor.TerrainData ?
t.BottomNeighbor.LeftNeighbor.TerrainData.Shading.AlbedoMapOrDefault :
Texture2D.blackTexture);
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int albedoResolution = terrain.TerrainData.Shading.AlbedoMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, albedoResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Shading.AlbedoMap.ReadPixels(
new Rect(0, 0, albedoResolution, albedoResolution), 0, 0);
terrain.TerrainData.Shading.AlbedoMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
}
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Shading.AlbedoMapOrDefault;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetColor(COLOR, args.Color);
mat.SetTexture(MAIN_TEX, bg);
SetupTextureGrid(terrain, mat);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, args.Opacity);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
args.ConditionalPaintingConfigs.SetupMaterial(terrain, mat);
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 :
args.ActionType == GPainterActionType.Alternative ? 2 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int albedoResolution = terrain.TerrainData.Shading.AlbedoMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, albedoResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawAlbedoLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afd3422bd3e4ab24ab75231a55db4a23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,364 @@
#if GRIFFIN
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
public class GConditionalPaintingConfigs
{
private static readonly string BLEND_HEIGHT_KW = "BLEND_HEIGHT";
private static readonly int HEIGHT_MAP = Shader.PropertyToID("_HeightMap");
private static readonly int MIN_HEIGHT = Shader.PropertyToID("_MinHeight");
private static readonly int MAX_HEIGHT = Shader.PropertyToID("_MaxHeight");
private static readonly int HEIGHT_TRANSITION = Shader.PropertyToID("_HeightTransition");
private static readonly string BLEND_SLOPE_KW = "BLEND_SLOPE";
private static readonly int NORMAL_MAP = Shader.PropertyToID("_NormalMap");
private static readonly int MIN_SLOPE = Shader.PropertyToID("_MinSlope");
private static readonly int MAX_SLOPE = Shader.PropertyToID("_MaxSlope");
private static readonly int SLOPE_TRANSITION = Shader.PropertyToID("_SlopeTransition");
private static readonly string BLEND_NOISE_KW = "BLEND_NOISE";
private static readonly int NOISE_ORIGIN = Shader.PropertyToID("_NoiseOrigin");
private static readonly int NOISE_FREQUENCY = Shader.PropertyToID("_NoiseFrequency");
private static readonly int NOISE_OCTAVES = Shader.PropertyToID("_NoiseOctaves");
private static readonly int NOISE_LACUNARITY = Shader.PropertyToID("_NoiseLacunarity");
private static readonly int NOISE_PERSISTENCE = Shader.PropertyToID("_NoisePersistence");
private static readonly int NOISE_REMAP = Shader.PropertyToID("_NoiseRemap");
private static readonly int TERRAIN_DIMENSION = Shader.PropertyToID("_TerrainDimension");
[SerializeField]
private bool blendHeight;
public bool BlendHeight
{
get
{
return blendHeight;
}
set
{
blendHeight = value;
}
}
[SerializeField]
private float minHeight;
public float MinHeight
{
get
{
return minHeight;
}
set
{
minHeight = Mathf.Max(0, value);
}
}
[SerializeField]
private float maxHeight;
public float MaxHeight
{
get
{
return maxHeight;
}
set
{
maxHeight = Mathf.Max(0, value);
}
}
[SerializeField]
private AnimationCurve heightTransition;
public AnimationCurve HeightTransition
{
get
{
if (heightTransition == null)
{
heightTransition = AnimationCurve.EaseInOut(0, 1, 1, 1);
}
return heightTransition;
}
set
{
heightTransition = value;
}
}
[SerializeField]
private bool blendSlope;
public bool BlendSlope
{
get
{
return blendSlope;
}
set
{
blendSlope = value;
}
}
[SerializeField]
private GNormalMapMode normalMapMode;
public GNormalMapMode NormalMapMode
{
get
{
return normalMapMode;
}
set
{
normalMapMode = value;
}
}
[SerializeField]
private float minSlope;
public float MinSlope
{
get
{
return minSlope;
}
set
{
minSlope = Mathf.Clamp(value, 0, 90);
}
}
[SerializeField]
private float maxSlope;
public float MaxSlope
{
get
{
return maxSlope;
}
set
{
maxSlope = Mathf.Clamp(value, 0, 90);
}
}
[SerializeField]
private AnimationCurve slopeTransition;
public AnimationCurve SlopeTransition
{
get
{
if (slopeTransition == null)
{
slopeTransition = AnimationCurve.EaseInOut(0, 1, 1, 1);
}
return slopeTransition;
}
set
{
slopeTransition = value;
}
}
[SerializeField]
private bool blendNoise;
public bool BlendNoise
{
get
{
return blendNoise;
}
set
{
blendNoise = value;
}
}
[SerializeField]
private Vector2 noiseOrigin;
public Vector2 NoiseOrigin
{
get
{
return noiseOrigin;
}
set
{
noiseOrigin = value;
}
}
[SerializeField]
private float noiseFrequency;
public float NoiseFrequency
{
get
{
return noiseFrequency;
}
set
{
noiseFrequency = value;
}
}
[SerializeField]
private int noiseOctaves;
public int NoiseOctaves
{
get
{
return noiseOctaves;
}
set
{
noiseOctaves = Mathf.Clamp(value, 1, 4);
}
}
[SerializeField]
private float noiseLacunarity;
public float NoiseLacunarity
{
get
{
return noiseLacunarity;
}
set
{
noiseLacunarity = Mathf.Max(1, value);
}
}
[SerializeField]
private float noisePersistence;
public float NoisePersistence
{
get
{
return noisePersistence;
}
set
{
noisePersistence = Mathf.Clamp(value, 0.01f, 1f);
}
}
[SerializeField]
private AnimationCurve noiseRemap;
public AnimationCurve NoiseRemap
{
get
{
if (noiseRemap == null)
{
noiseRemap = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return noiseRemap;
}
set
{
noiseRemap = value;
}
}
internal Texture2D heightTransitionTexture;
internal Texture2D slopeTransitionTexture;
internal Texture2D noiseRemapTexture;
public void UpdateCurveTextures()
{
CleanUp();
heightTransitionTexture = GCommon.CreateTextureFromCurve(HeightTransition, 256, 1);
slopeTransitionTexture = GCommon.CreateTextureFromCurve(SlopeTransition, 256, 1);
noiseRemapTexture = GCommon.CreateTextureFromCurve(NoiseRemap, 256, 1);
}
public void CleanUp()
{
if (heightTransitionTexture != null)
GUtilities.DestroyObject(heightTransitionTexture);
if (slopeTransitionTexture != null)
GUtilities.DestroyObject(slopeTransitionTexture);
if (noiseRemapTexture != null)
GUtilities.DestroyObject(noiseRemapTexture);
}
public GConditionalPaintingConfigs()
{
BlendHeight = false;
MinHeight = 0;
MaxHeight = 1000;
HeightTransition = AnimationCurve.EaseInOut(0, 1, 1, 1);
BlendSlope = false;
MinSlope = 0;
MaxSlope = 90;
SlopeTransition = AnimationCurve.EaseInOut(0, 1, 1, 1);
BlendNoise = false;
NoiseOrigin = Vector2.zero;
NoiseFrequency = 100f;
NoiseOctaves = 1;
NoiseLacunarity = 2;
NoisePersistence = 0.5f;
NoiseRemap = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
public void SetupMaterial(GStylizedTerrain t, Material painterMat)
{
int resolution = t.TerrainData.Geometry.HeightMapResolution;
Vector3 terrainSize = t.TerrainData.Geometry.Size;
if (BlendHeight)
{
RenderTexture heightMap = t.GetHeightMap(resolution);
painterMat.EnableKeyword(BLEND_HEIGHT_KW);
painterMat.SetTexture(HEIGHT_MAP, heightMap);
painterMat.SetFloat(MIN_HEIGHT, GUtilities.InverseLerpUnclamped(0, terrainSize.y, MinHeight));
painterMat.SetFloat(MAX_HEIGHT, GUtilities.InverseLerpUnclamped(0, terrainSize.y, MaxHeight));
painterMat.SetTexture(HEIGHT_TRANSITION, heightTransitionTexture);
}
else
{
painterMat.DisableKeyword(BLEND_HEIGHT_KW);
}
if (BlendSlope)
{
RenderTexture normalMap =
NormalMapMode == GNormalMapMode.Sharp ? t.GetSharpNormalMap(resolution) :
NormalMapMode == GNormalMapMode.Interpolated ? t.GetInterpolatedNormalMap(resolution) :
NormalMapMode == GNormalMapMode.PerPixel ? t.GetPerPixelNormalMap(resolution) : null;
painterMat.EnableKeyword(BLEND_SLOPE_KW);
painterMat.SetTexture(NORMAL_MAP, normalMap);
painterMat.SetFloat(MIN_SLOPE, MinSlope * Mathf.Deg2Rad);
painterMat.SetFloat(MAX_SLOPE, MaxSlope * Mathf.Deg2Rad);
painterMat.SetTexture(SLOPE_TRANSITION, slopeTransitionTexture);
}
else
{
painterMat.DisableKeyword(BLEND_SLOPE_KW);
}
if (BlendNoise)
{
painterMat.EnableKeyword(BLEND_NOISE_KW);
painterMat.SetVector(NOISE_ORIGIN, NoiseOrigin);
painterMat.SetFloat(NOISE_FREQUENCY, NoiseFrequency);
painterMat.SetFloat(NOISE_OCTAVES, NoiseOctaves);
painterMat.SetFloat(NOISE_LACUNARITY, NoiseLacunarity);
painterMat.SetFloat(NOISE_PERSISTENCE, NoisePersistence);
painterMat.SetTexture(NOISE_REMAP, noiseRemapTexture);
painterMat.SetVector(TERRAIN_DIMENSION, new Vector4(t.transform.position.x, t.transform.position.z, terrainSize.x, terrainSize.z));
}
else
{
painterMat.DisableKeyword(BLEND_NOISE_KW);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f47d8a1f50481b44a82642626147eca5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,211 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GElevationPainter : IGTexturePainter, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MAIN_TEX_L = Shader.PropertyToID("_MainTex_Left");
private static readonly int MAIN_TEX_TL = Shader.PropertyToID("_MainTex_TopLeft");
private static readonly int MAIN_TEX_T = Shader.PropertyToID("_MainTex_Top");
private static readonly int MAIN_TEX_TR = Shader.PropertyToID("_MainTex_TopRight");
private static readonly int MAIN_TEX_R = Shader.PropertyToID("_MainTex_Right");
private static readonly int MAIN_TEX_RB = Shader.PropertyToID("_MainTex_BottomRight");
private static readonly int MAIN_TEX_B = Shader.PropertyToID("_MainTex_Bottom");
private static readonly int MAIN_TEX_BL = Shader.PropertyToID("_MainTex_BottomLeft");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.elevationPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Gradually modifying terrain geometry.\n" +
" - Hold Left Mouse to raise.\n" +
" - Hold {0} & Left Mouse to lower.\n" +
" - Hold {1} & Left Mouse to smooth.",
"Ctrl",
"Shift");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Elevation Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
private void SetupTextureGrid(GStylizedTerrain t, Material mat)
{
mat.SetTexture(MAIN_TEX_L,
t.LeftNeighbor && t.LeftNeighbor.TerrainData ?
t.LeftNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.LeftNeighbor && t.LeftNeighbor.TopNeighbor && t.LeftNeighbor.TopNeighbor.TerrainData ?
t.LeftNeighbor.TopNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.TopNeighbor && t.TopNeighbor.LeftNeighbor && t.TopNeighbor.LeftNeighbor.TerrainData ?
t.TopNeighbor.LeftNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_T,
t.TopNeighbor && t.TopNeighbor.TerrainData ?
t.TopNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.RightNeighbor && t.RightNeighbor.TopNeighbor && t.RightNeighbor.TopNeighbor.TerrainData ?
t.RightNeighbor.TopNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.TopNeighbor && t.TopNeighbor.RightNeighbor && t.TopNeighbor.RightNeighbor.TerrainData ?
t.TopNeighbor.RightNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_R,
t.RightNeighbor && t.RightNeighbor.TerrainData ?
t.RightNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.RightNeighbor && t.RightNeighbor.BottomNeighbor && t.RightNeighbor.BottomNeighbor.TerrainData ?
t.RightNeighbor.BottomNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.BottomNeighbor && t.BottomNeighbor.RightNeighbor && t.BottomNeighbor.RightNeighbor.TerrainData ?
t.BottomNeighbor.RightNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_B,
t.BottomNeighbor && t.BottomNeighbor.TerrainData ?
t.BottomNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.LeftNeighbor && t.LeftNeighbor.BottomNeighbor && t.LeftNeighbor.BottomNeighbor.TerrainData ?
t.LeftNeighbor.BottomNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.BottomNeighbor && t.BottomNeighbor.LeftNeighbor && t.BottomNeighbor.LeftNeighbor.TerrainData ?
t.BottomNeighbor.LeftNeighbor.TerrainData.Geometry.HeightMap :
Texture2D.blackTexture);
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
SetupTextureGrid(terrain, mat);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 :
args.ActionType == GPainterActionType.Alternative ? 2 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
terrain.ForceLOD(-1);
GRuntimeSettings.Instance.isEditingGeometry = false;
terrain.UpdateTreesPosition();
terrain.UpdateGrassPatches();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
terrain.TerrainData.Foliage.ClearGrassDirtyRegions();
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawGeometryLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f4e5ad81ad9ae94d810f177eef58846
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,153 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GHeightSamplingPainter : IGTexturePainter, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TARGET_GRAY = Shader.PropertyToID("_TargetGray");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.heightSamplingPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Set geometry to a pre-sampled height.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Shift & Left Mouse to sample height.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Height Sampling";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
if (args.ActionType == GPainterActionType.Normal)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
else
{
return GCommon.EmptyResourceFlags;
}
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ActionType != GPainterActionType.Normal)
{
terrain.ForceLOD(-1);
return;
}
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
Vector3 localSamplePoint = terrain.transform.InverseTransformPoint(args.SamplePoint);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
mat.SetFloat(TARGET_GRAY, Mathf.InverseLerp(0, terrain.TerrainData.Geometry.Height, localSamplePoint.y));
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
int pass = 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ActionType != GPainterActionType.Normal)
{
terrain.ForceLOD(-1);
return;
}
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
GRuntimeSettings.Instance.isEditingGeometry = false;
terrain.UpdateTreesPosition();
terrain.UpdateGrassPatches();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
terrain.TerrainData.Foliage.ClearGrassDirtyRegions();
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawGeometryLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 125721534eb906d4e9f1b2b41318e35c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,239 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Pinwheel.Griffin.PaintTool
{
public class GMaskPainter : IGTexturePainter, IGTexturePainterWithCustomParams, IGTexturePainterWithLivePreview, IConditionalPainter
{
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.maskPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Modify terrain mask.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold {0} & Left Mouse to erase.\n" +
" - Hold {1} & Left Mouse to smooth.",
"Ctrl",
"Shift");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Mask Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.MaskMapResourceFlags;
}
private void SetupTextureGrid(GStylizedTerrain t, Material mat)
{
mat.SetTexture("_MainTex_Left",
t.LeftNeighbor && t.LeftNeighbor.TerrainData ?
t.LeftNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_TopLeft",
t.LeftNeighbor && t.LeftNeighbor.TopNeighbor && t.LeftNeighbor.TopNeighbor.TerrainData ?
t.LeftNeighbor.TopNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_TopLeft",
t.TopNeighbor && t.TopNeighbor.LeftNeighbor && t.TopNeighbor.LeftNeighbor.TerrainData ?
t.TopNeighbor.LeftNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_Top",
t.TopNeighbor && t.TopNeighbor.TerrainData ?
t.TopNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_TopRight",
t.RightNeighbor && t.RightNeighbor.TopNeighbor && t.RightNeighbor.TopNeighbor.TerrainData ?
t.RightNeighbor.TopNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_TopRight",
t.TopNeighbor && t.TopNeighbor.RightNeighbor && t.TopNeighbor.RightNeighbor.TerrainData ?
t.TopNeighbor.RightNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_Right",
t.RightNeighbor && t.RightNeighbor.TerrainData ?
t.RightNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_BottomRight",
t.RightNeighbor && t.RightNeighbor.BottomNeighbor && t.RightNeighbor.BottomNeighbor.TerrainData ?
t.RightNeighbor.BottomNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_BottomRight",
t.BottomNeighbor && t.BottomNeighbor.RightNeighbor && t.BottomNeighbor.RightNeighbor.TerrainData ?
t.BottomNeighbor.RightNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_Bottom",
t.BottomNeighbor && t.BottomNeighbor.TerrainData ?
t.BottomNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_BottomLeft",
t.LeftNeighbor && t.LeftNeighbor.BottomNeighbor && t.LeftNeighbor.BottomNeighbor.TerrainData ?
t.LeftNeighbor.BottomNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture("_MainTex_BottomLeft",
t.BottomNeighbor && t.BottomNeighbor.LeftNeighbor && t.BottomNeighbor.LeftNeighbor.TerrainData ?
t.BottomNeighbor.LeftNeighbor.TerrainData.Mask.MaskMapOrDefault :
Texture2D.blackTexture);
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int maskResolution = terrain.TerrainData.Mask.MaskMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, maskResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Mask.MaskMap.ReadPixels(
new Rect(0, 0, maskResolution, maskResolution), 0, 0);
terrain.TerrainData.Mask.MaskMap.Apply();
RenderTexture.active = null;
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
}
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Mask.MaskMapOrDefault;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetTexture("_MainTex", bg);
SetupTextureGrid(terrain, mat);
mat.SetTexture("_Mask", args.BrushMask);
mat.SetFloat("_Opacity", args.Opacity);
GMaskPainterParams param = GTexturePainterCustomParams.Instance.Mask;
Vector4 channel;
if (param.Channel == GTextureChannel.R)
{
channel = new Vector4(1, 0, 0, 0);
}
else if (param.Channel == GTextureChannel.G)
{
channel = new Vector4(0, 1, 0, 0);
}
else if (param.Channel == GTextureChannel.B)
{
channel = new Vector4(0, 0, 1, 0);
}
else
{
channel = new Vector4(0, 0, 0, 1);
}
mat.SetVector("_Channel", channel);
args.ConditionalPaintingConfigs.SetupMaterial(terrain, mat);
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 :
args.ActionType == GPainterActionType.Alternative ? 2 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
if (GTexturePainterCustomParams.Instance.Mask.Visualize == false)
return;
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int maskResolution = terrain.TerrainData.Mask.MaskMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, maskResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawMask4ChannelsLivePreview(
terrain, cam,
rt,
GCommon.UnitRect);
#endif
}
public void Editor_DrawCustomParamsGUI()
{
#if UNITY_EDITOR
string label = "Mask Painting";
string id = "mask-painter";
GCommonGUI.Foldout(label, true, id, () =>
{
GMaskPainterParams param = GTexturePainterCustomParams.Instance.Mask;
string[] labels = new string[]
{
"R (Terrain Mask)",
"G (Smooth Normal Mask)",
"B (Water Source)",
"A"
};
int[] values = new int[]
{
1,
2,
4,
8
};
param.Channel = (GTextureChannel)EditorGUILayout.IntPopup("Channel", (int)param.Channel, labels, values);
param.Visualize = EditorGUILayout.Toggle("Visualize", param.Visualize);
GTexturePainterCustomParams.Instance.Mask = param;
EditorUtility.SetDirty(GTexturePainterCustomParams.Instance);
});
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0897186fb16d4d4fbaa728b2560c0e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if GRIFFIN
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
public struct GMaskPainterParams
{
[SerializeField]
private GTextureChannel channel;
public GTextureChannel Channel
{
get
{
return channel;
}
set
{
channel = value;
}
}
[SerializeField]
private bool visualize;
public bool Visualize
{
get
{
return visualize;
}
set
{
visualize = value;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6f20d8d839c14948b9135934d1a10e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,203 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GMetallicPainter : IGTexturePainter, IGTexturePainterWithLivePreview, IConditionalPainter
{
private static readonly int COLOR = Shader.PropertyToID("_Color");
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MAIN_TEX_L = Shader.PropertyToID("_MainTex_Left");
private static readonly int MAIN_TEX_TL = Shader.PropertyToID("_MainTex_TopLeft");
private static readonly int MAIN_TEX_T = Shader.PropertyToID("_MainTex_Top");
private static readonly int MAIN_TEX_TR = Shader.PropertyToID("_MainTex_TopRight");
private static readonly int MAIN_TEX_R = Shader.PropertyToID("_MainTex_Right");
private static readonly int MAIN_TEX_RB = Shader.PropertyToID("_MainTex_BottomRight");
private static readonly int MAIN_TEX_B = Shader.PropertyToID("_MainTex_Bottom");
private static readonly int MAIN_TEX_BL = Shader.PropertyToID("_MainTex_BottomLeft");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.metallicPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Modify terrain Metallic Map Red channel.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase.\n" +
" - Hold Shift & Left Mouse to smooth.\n" +
"This painter uses the Red channel of brush color.\n" +
"Use a material that utilizes Metallic Map to see the result.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Metallic Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.MetallicResourceFlags;
}
private void SetupTextureGrid(GStylizedTerrain t, Material mat)
{
mat.SetTexture(MAIN_TEX_L,
t.LeftNeighbor && t.LeftNeighbor.TerrainData ?
t.LeftNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.LeftNeighbor && t.LeftNeighbor.TopNeighbor && t.LeftNeighbor.TopNeighbor.TerrainData ?
t.LeftNeighbor.TopNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.TopNeighbor && t.TopNeighbor.LeftNeighbor && t.TopNeighbor.LeftNeighbor.TerrainData ?
t.TopNeighbor.LeftNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_T,
t.TopNeighbor && t.TopNeighbor.TerrainData ?
t.TopNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.RightNeighbor && t.RightNeighbor.TopNeighbor && t.RightNeighbor.TopNeighbor.TerrainData ?
t.RightNeighbor.TopNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.TopNeighbor && t.TopNeighbor.RightNeighbor && t.TopNeighbor.RightNeighbor.TerrainData ?
t.TopNeighbor.RightNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_R,
t.RightNeighbor && t.RightNeighbor.TerrainData ?
t.RightNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.RightNeighbor && t.RightNeighbor.BottomNeighbor && t.RightNeighbor.BottomNeighbor.TerrainData ?
t.RightNeighbor.BottomNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.BottomNeighbor && t.BottomNeighbor.RightNeighbor && t.BottomNeighbor.RightNeighbor.TerrainData ?
t.BottomNeighbor.RightNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_B,
t.BottomNeighbor && t.BottomNeighbor.TerrainData ?
t.BottomNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.LeftNeighbor && t.LeftNeighbor.BottomNeighbor && t.LeftNeighbor.BottomNeighbor.TerrainData ?
t.LeftNeighbor.BottomNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.BottomNeighbor && t.BottomNeighbor.LeftNeighbor && t.BottomNeighbor.LeftNeighbor.TerrainData ?
t.BottomNeighbor.LeftNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int metallicResolution = terrain.TerrainData.Shading.MetallicMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, metallicResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Shading.MetallicMap.ReadPixels(
new Rect(0, 0, metallicResolution, metallicResolution), 0, 0);
terrain.TerrainData.Shading.MetallicMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
}
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Shading.MetallicMapOrDefault;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetColor(COLOR, args.Color);
mat.SetTexture(MAIN_TEX, bg);
SetupTextureGrid(terrain, mat);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, args.Opacity);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
args.ConditionalPaintingConfigs.SetupMaterial(terrain, mat);
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 :
args.ActionType == GPainterActionType.Alternative ? 2 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int metallicResolution = terrain.TerrainData.Shading.MetallicMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, metallicResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawMetallicSmoothnessLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b464d2a81b55724384cbdb7b7a3ae72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,215 @@
#if GRIFFIN
using UnityEngine;
using System.Collections.Generic;
using Pinwheel.Griffin.TextureTool;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Pinwheel.Griffin.PaintTool
{
public class GNoisePainter : IGTexturePainter, IGTexturePainterWithCustomParams, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int NOISE_MAP = Shader.PropertyToID("_NoiseMap");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static readonly string WORLD_SPACE_KW = "USE_WORLD_SPACE";
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.noisePainterShader);
}
return painterMaterial;
}
}
public string HistoryPrefix
{
get
{
return "Noise Painting";
}
}
public string Instruction
{
get
{
string s = string.Format(
"Raise or lower terrain surface using noise.\n" +
" - Hold Left Mouse to raise.\n" +
" - Hold Ctrl & Left Mouse to lower.");
return s;
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture noiseMap = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution, 1);
RenderNoiseTexture(noiseMap, terrain);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
mat.SetTexture(NOISE_MAP, noiseMap);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
GCommon.SetMaterialKeywordActive(mat, WORLD_SPACE_KW, GTexturePainterCustomParams.Instance.Noise.UseWorldSpace);
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
terrain.ForceLOD(-1);
GRuntimeSettings.Instance.isEditingGeometry = false;
terrain.UpdateTreesPosition();
terrain.UpdateGrassPatches();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
terrain.TerrainData.Foliage.ClearGrassDirtyRegions();
}
}
public void Editor_DrawCustomParamsGUI()
{
#if UNITY_EDITOR
string label = "Noise Painting";
string id = "noise-painter";
GCommonGUI.Foldout(label, true, id, () =>
{
GNoisePainterParams param = GTexturePainterCustomParams.Instance.Noise;
EditorGUILayout.GetControlRect(GUILayout.Height(1));
Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(200));
RenderTexture preview = GTerrainTexturePainter.Internal_GetRenderTexture(null, 200, 0);
RenderNoiseTexture(preview, null);
EditorGUI.DrawPreviewTexture(r, preview, null, ScaleMode.ScaleToFit);
EditorGUILayout.GetControlRect(GUILayout.Height(1));
param.Type = (GNoiseType)EditorGUILayout.EnumPopup("Type", param.Type);
param.Origin = GCommonGUI.InlineVector2Field("Origin", param.Origin);
param.Frequency = EditorGUILayout.FloatField("Frequency", param.Frequency);
param.Lacunarity = EditorGUILayout.FloatField("Lacunarity", param.Lacunarity);
param.Persistence = EditorGUILayout.FloatField("Persistence", param.Persistence);
param.Octaves = EditorGUILayout.IntField("Octaves", param.Octaves);
param.Seed = EditorGUILayout.FloatField("Seed", param.Seed);
param.UseWorldSpace = EditorGUILayout.Toggle("World Space", param.UseWorldSpace);
GTexturePainterCustomParams.Instance.Noise = param;
EditorUtility.SetDirty(GTexturePainterCustomParams.Instance);
});
#endif
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawGeometryLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
private void RenderNoiseTexture(RenderTexture noiseMap, GStylizedTerrain terrain)
{
GNoisePainterParams param = GTexturePainterCustomParams.Instance.Noise;
GNoiseMapGenerator gen = new GNoiseMapGenerator();
GNoiseMapGeneratorParams genParam = new GNoiseMapGeneratorParams();
genParam.Type = param.Type;
if (param.UseWorldSpace && terrain != null)
{
Vector3 terrainSize = new Vector3(
terrain.TerrainData.Geometry.Width,
terrain.TerrainData.Geometry.Height,
terrain.TerrainData.Geometry.Length);
Vector3 terrainPos = terrain.transform.position;
if (genParam.Type == GNoiseType.Voronoi)
{
genParam.Origin = param.Origin + new Vector2(terrainPos.x / terrainSize.x, terrainPos.z / terrainSize.z);
}
else
{
genParam.Origin = param.Origin + new Vector2(terrainPos.x * param.Frequency / terrainSize.x, terrainPos.z * param.Frequency / terrainSize.z);
}
}
else
{
genParam.Origin = param.Origin;
}
genParam.Frequency = param.Frequency;
genParam.Lacunarity = param.Lacunarity;
genParam.Persistence = param.Persistence;
genParam.Octaves = param.Octaves;
genParam.Seed = param.Seed;
gen.Generate(noiseMap, genParam);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69a4c5b88485c8f49b738b48dd0b766d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
#if GRIFFIN
using Pinwheel.Griffin.TextureTool;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
public struct GNoisePainterParams
{
[SerializeField]
private GNoiseType type;
public GNoiseType Type
{
get
{
return type;
}
set
{
type = value;
}
}
[SerializeField]
private Vector2 origin;
public Vector2 Origin
{
get
{
return origin;
}
set
{
origin = value;
}
}
[SerializeField]
private float frequency;
public float Frequency
{
get
{
return frequency;
}
set
{
frequency = value;
}
}
[SerializeField]
private float laccunarity;
public float Lacunarity
{
get
{
return laccunarity;
}
set
{
laccunarity = Mathf.Max(1, value);
}
}
[SerializeField]
private float persistent;
public float Persistence
{
get
{
return persistent;
}
set
{
persistent = Mathf.Clamp(value, 0.01f, 1);
}
}
[SerializeField]
private int octaves;
public int Octaves
{
get
{
return octaves;
}
set
{
octaves = Mathf.Clamp(value, 1, 4);
}
}
[SerializeField]
private float seed;
public float Seed
{
get
{
return seed;
}
set
{
seed = Mathf.Max(1, value);
}
}
[SerializeField]
private bool useWorldSpace;
public bool UseWorldSpace
{
get
{
return useWorldSpace;
}
set
{
useWorldSpace = value;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f86bbec0bf3c1eb40956da0829357fa8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,154 @@
#if GRIFFIN
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Pinwheel.Griffin.PaintTool
{
public class GRemapPainter : IGTexturePainter, IGTexturePainterWithCustomParams, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int REMAP_TEX = Shader.PropertyToID("_RemapTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.remapPainterShader);
}
return painterMaterial;
}
}
public string HistoryPrefix
{
get
{
return "Remap Painting";
}
}
public string Instruction
{
get
{
string s = string.Format(
"Remap evelation value by a curve.\n" +
" - Use Left Mouse to paint.");
return s;
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
Texture2D remapTex = GCommon.CreateTextureFromCurve(GTexturePainterCustomParams.Instance.Remap.Curve, 512, 1);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
mat.SetTexture(REMAP_TEX, remapTex);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
int pass = 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
GUtilities.DestroyObject(remapTex);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
terrain.ForceLOD(-1);
GRuntimeSettings.Instance.isEditingGeometry = false;
terrain.UpdateTreesPosition();
terrain.UpdateGrassPatches();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
terrain.TerrainData.Foliage.ClearGrassDirtyRegions();
}
}
public void Editor_DrawCustomParamsGUI()
{
#if UNITY_EDITOR
string label = "Remap Painting";
string id = "remap-painter";
GCommonGUI.Foldout(label, true, id, () =>
{
GRemapPainterParams param = GTexturePainterCustomParams.Instance.Remap;
param.Curve = EditorGUILayout.CurveField("Curve", param.Curve, Color.red, GCommon.UnitRect);
GTexturePainterCustomParams.Instance.Remap = param;
EditorUtility.SetDirty(GTexturePainterCustomParams.Instance);
});
#endif
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawGeometryLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bd3b2c6a74be8e4da975dd2c362f071
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
public struct GRemapPainterParams
{
[SerializeField]
private AnimationCurve curve;
public AnimationCurve Curve
{
get
{
if (curve == null)
{
curve = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return curve;
}
set
{
curve = value;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2516f4523e35ed8438b55b948c812cc0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,202 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GSmoothnessPainter : IGTexturePainter, IGTexturePainterWithLivePreview, IConditionalPainter
{
private static readonly int COLOR = Shader.PropertyToID("_Color");
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MAIN_TEX_L = Shader.PropertyToID("_MainTex_Left");
private static readonly int MAIN_TEX_TL = Shader.PropertyToID("_MainTex_TopLeft");
private static readonly int MAIN_TEX_T = Shader.PropertyToID("_MainTex_Top");
private static readonly int MAIN_TEX_TR = Shader.PropertyToID("_MainTex_TopRight");
private static readonly int MAIN_TEX_R = Shader.PropertyToID("_MainTex_Right");
private static readonly int MAIN_TEX_RB = Shader.PropertyToID("_MainTex_BottomRight");
private static readonly int MAIN_TEX_B = Shader.PropertyToID("_MainTex_Bottom");
private static readonly int MAIN_TEX_BL = Shader.PropertyToID("_MainTex_BottomLeft");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.smoothnessPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Modify terrain Metallic Map Alpha channel.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase.\n" +
" - Hold Shift & Left Mouse to smooth.\n" +
"This painter uses the Alpha channel of brush color.\n" +
"Use a material that utilizes Metallic Map to see the result.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Smoothness Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.MetallicResourceFlags;
}
private void SetupTextureGrid(GStylizedTerrain t, Material mat)
{
mat.SetTexture(MAIN_TEX_L,
t.LeftNeighbor && t.LeftNeighbor.TerrainData ?
t.LeftNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.LeftNeighbor && t.LeftNeighbor.TopNeighbor && t.LeftNeighbor.TopNeighbor.TerrainData ?
t.LeftNeighbor.TopNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TL,
t.TopNeighbor && t.TopNeighbor.LeftNeighbor && t.TopNeighbor.LeftNeighbor.TerrainData ?
t.TopNeighbor.LeftNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_T,
t.TopNeighbor && t.TopNeighbor.TerrainData ?
t.TopNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.RightNeighbor && t.RightNeighbor.TopNeighbor && t.RightNeighbor.TopNeighbor.TerrainData ?
t.RightNeighbor.TopNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_TR,
t.TopNeighbor && t.TopNeighbor.RightNeighbor && t.TopNeighbor.RightNeighbor.TerrainData ?
t.TopNeighbor.RightNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_R,
t.RightNeighbor && t.RightNeighbor.TerrainData ?
t.RightNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.RightNeighbor && t.RightNeighbor.BottomNeighbor && t.RightNeighbor.BottomNeighbor.TerrainData ?
t.RightNeighbor.BottomNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_RB,
t.BottomNeighbor && t.BottomNeighbor.RightNeighbor && t.BottomNeighbor.RightNeighbor.TerrainData ?
t.BottomNeighbor.RightNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_B,
t.BottomNeighbor && t.BottomNeighbor.TerrainData ?
t.BottomNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.LeftNeighbor && t.LeftNeighbor.BottomNeighbor && t.LeftNeighbor.BottomNeighbor.TerrainData ?
t.LeftNeighbor.BottomNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
mat.SetTexture(MAIN_TEX_BL,
t.BottomNeighbor && t.BottomNeighbor.LeftNeighbor && t.BottomNeighbor.LeftNeighbor.TerrainData ?
t.BottomNeighbor.LeftNeighbor.TerrainData.Shading.MetallicMapOrDefault :
Texture2D.blackTexture);
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int metallicResolution = terrain.TerrainData.Shading.MetallicMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, metallicResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Shading.MetallicMap.ReadPixels(
new Rect(0, 0, metallicResolution, metallicResolution), 0, 0);
terrain.TerrainData.Shading.MetallicMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
}
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Shading.MetallicMapOrDefault;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetColor(COLOR, args.Color);
mat.SetTexture(MAIN_TEX, bg);
SetupTextureGrid(terrain, mat);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, args.Opacity);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
args.ConditionalPaintingConfigs.SetupMaterial(terrain, mat);
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 :
args.ActionType == GPainterActionType.Alternative ? 2 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int metallicResolution = terrain.TerrainData.Shading.MetallicMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, metallicResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawMetallicSmoothnessLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 89d294aa45a6a494eb5e5a66c9763ad3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,170 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GSplatPainter : IGTexturePainter, IGTexturePainterWithLivePreview, IConditionalPainter
{
private static readonly int CHANNEL_INDEX = Shader.PropertyToID("_ChannelIndex");
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splatPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Paint blend weight on terrain Splat Control maps.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase selected layer.\n" +
" - Hold Shift & Left Mouse to erase all layer.\n" +
"Use a material that utilizes splat maps to see the result.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Splat Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.SplatResourceFlags;
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (terrain.TerrainData.Shading.Splats == null)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
int splatIndex = args.SplatIndex;
if (splatIndex < 0 || splatIndex >= terrain.TerrainData.Shading.SplatControlMapCount * 4)
{
return;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int controlMapResolution = terrain.TerrainData.Shading.SplatControlResolution;
int controlMapCount = terrain.TerrainData.Shading.SplatControlMapCount;
for (int i = 0; i < controlMapCount; ++i)
{
Texture2D currentSplatControl = terrain.TerrainData.Shading.GetSplatControl(i);
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, controlMapResolution, i);
PaintOnRT(terrain, args, rt, uvCorners, i);
RenderTexture.active = rt;
currentSplatControl.ReadPixels(new Rect(0, 0, controlMapResolution, controlMapResolution), 0, 0);
currentSplatControl.Apply();
RenderTexture.active = null;
}
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
}
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners, int currentSplatIndex)
{
Texture2D currentSplatControl = terrain.TerrainData.Shading.GetSplatControl(currentSplatIndex);
GCommon.CopyToRT(currentSplatControl, rt);
Material mat = PainterMaterial;
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, args.Opacity);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
args.ConditionalPaintingConfigs.SetupMaterial(terrain, mat);
mat.SetTexture(MAIN_TEX, currentSplatControl);
int selectedSplatIndex = args.SplatIndex;
if (selectedSplatIndex / 4 == currentSplatIndex)
{
mat.SetInt(CHANNEL_INDEX, selectedSplatIndex % 4);
}
else
{
mat.SetInt(CHANNEL_INDEX, -1);
}
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 :
args.ActionType == GPainterActionType.Alternative ? 2 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.ForceUpdateGeometry)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
int splatIndex = args.SplatIndex;
if (splatIndex < 0 || splatIndex >= terrain.TerrainData.Shading.SplatControlMapCount * 4)
{
return;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int controlMapResolution = terrain.TerrainData.Shading.SplatControlResolution;
int controlMapCount = terrain.TerrainData.Shading.SplatControlMapCount;
for (int i = 0; i < controlMapCount; ++i)
{
Texture2D currentSplatControl = terrain.TerrainData.Shading.GetSplatControl(i);
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, controlMapResolution, i);
PaintOnRT(terrain, args, rt, uvCorners, i);
}
Texture[] controls = new Texture[controlMapCount];
for (int i = 0; i < controlMapCount; ++i)
{
controls[i] = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, controlMapResolution, i);
}
GLivePreviewDrawer.DrawSplatLivePreview(terrain, cam, controls, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69c05278b4a8f114f9f45e09608b80de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,140 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GSubDivPainter : IGTexturePainter, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.subdivPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Add more sub division to a particular area of the terrain.\n" +
" - Hold Left Mouse to add.\n" +
" - Hold {0} & Left Mouse to subtract.",
"Ctrl");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Sub Div Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
terrain.ForceLOD(-1);
GRuntimeSettings.Instance.isEditingGeometry = false;
terrain.UpdateTreesPosition();
terrain.UpdateGrassPatches();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
terrain.TerrainData.Foliage.ClearGrassDirtyRegions();
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
Matrix4x4 worldToMaskMatrix = Matrix4x4.TRS(
args.WorldPointCorners[0],
Quaternion.Euler(0, args.Rotation, 0),
args.Radius * 2 * Vector3.one).inverse;
GLivePreviewDrawer.DrawSubdivLivePreview(terrain, cam, rt, dirtyRect, args.BrushMask, worldToMaskMatrix);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c07c7738f8146094398c400eabfebde1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,152 @@
#if GRIFFIN
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Pinwheel.Griffin.PaintTool
{
public class GTerracePainter : IGTexturePainter, IGTexturePainterWithCustomParams, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int STEP_COUNT = Shader.PropertyToID("_StepCount");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.terracePainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Paint terrace (step) effect on terrain geometry.\n" +
" - Use Left Mouse to paint.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Terrace Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
mat.SetInt(STEP_COUNT, GTexturePainterCustomParams.Instance.Terrace.StepCount);
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
int pass = 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
terrain.ForceLOD(-1);
GRuntimeSettings.Instance.isEditingGeometry = false;
terrain.UpdateTreesPosition();
terrain.UpdateGrassPatches();
terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
terrain.TerrainData.Foliage.ClearGrassDirtyRegions();
}
}
public void Editor_DrawCustomParamsGUI()
{
#if UNITY_EDITOR
string label = "Terrace Painting";
string id = "terrace-painter-params";
GCommonGUI.Foldout(label, true, id, () =>
{
GTerracePainterParams param = GTexturePainterCustomParams.Instance.Terrace;
param.StepCount = EditorGUILayout.IntField("Step Count", param.StepCount);
GTexturePainterCustomParams.Instance.Terrace = param;
EditorUtility.SetDirty(GTexturePainterCustomParams.Instance);
});
#endif
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
GLivePreviewDrawer.DrawGeometryLivePreview(terrain, cam, rt, dirtyRect);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93c2e86a56af13940b6fbe48383183bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
public struct GTerracePainterParams
{
[SerializeField]
private int stepCount;
public int StepCount
{
get
{
return stepCount;
}
set
{
stepCount = Mathf.Max(1, value);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c6e363d854cd224e9f5f22000e124b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,735 @@
#if GRIFFIN
using UnityEngine;
using System.Collections.Generic;
using Type = System.Type;
using Rand = System.Random;
#if UNITY_EDITOR
using Pinwheel.Griffin.BackupTool;
#endif
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
[ExecuteInEditMode]
public class GTerrainTexturePainter : MonoBehaviour
{
public const float GEOMETRY_OPACITY_EXPONENT = 3;
private static readonly List<string> BUILTIN_PAINTER_NAME = new List<string>(new string[]
{
"GElevationPainter",
"GHeightSamplingPainter",
"GTerracePainter",
"GRemapPainter",
"GNoisePainter",
"GSubDivPainter",
"GVisibilityPainter",
"GAlbedoPainter",
"GMetallicPainter",
"GSmoothnessPainter",
"GSplatPainter",
"GMaskPainter"
});
private static List<Type> customPainterTypes;
private static List<Type> CustomPainterTypes
{
get
{
if (customPainterTypes == null)
customPainterTypes = new List<Type>();
return customPainterTypes;
}
set
{
customPainterTypes = value;
}
}
public static string TexturePainterInterfaceName
{
get
{
return typeof(IGTexturePainter).Name;
}
}
static GTerrainTexturePainter()
{
RefreshCustomPainterTypes();
}
public static void RefreshCustomPainterTypes()
{
List<Type> loadedTypes = GCommon.GetAllLoadedTypes();
CustomPainterTypes = loadedTypes.FindAll(
t => t.GetInterface(TexturePainterInterfaceName) != null &&
!BUILTIN_PAINTER_NAME.Contains(t.Name));
}
public static List<Type> GetCustomPainterTypes()
{
return CustomPainterTypes;
}
[SerializeField]
private int groupId;
public int GroupId
{
get
{
return groupId;
}
set
{
groupId = value;
}
}
[SerializeField]
private GTexturePaintingMode mode;
public GTexturePaintingMode Mode
{
get
{
return mode;
}
set
{
mode = value;
}
}
[SerializeField]
private int customPainterIndex;
public int CustomPainterIndex
{
get
{
return customPainterIndex;
}
set
{
customPainterIndex = value;
}
}
[SerializeField]
private string customPainterArgs;
public string CustomPainterArgs
{
get
{
return customPainterArgs;
}
set
{
customPainterArgs = value;
}
}
[SerializeField]
private bool enableTerrainMask = true;
public bool EnableTerrainMask
{
get
{
return enableTerrainMask;
}
set
{
enableTerrainMask = value;
}
}
[SerializeField]
private bool forceUpdateGeometry;
public bool ForceUpdateGeometry
{
get
{
return forceUpdateGeometry;
}
set
{
forceUpdateGeometry = value;
}
}
public IGTexturePainter ActivePainter
{
get
{
if (Mode == GTexturePaintingMode.Elevation)
{
return new GElevationPainter();
}
else if (Mode == GTexturePaintingMode.HeightSampling)
{
return new GHeightSamplingPainter();
}
else if (Mode == GTexturePaintingMode.Terrace)
{
return new GTerracePainter();
}
else if (Mode == GTexturePaintingMode.Remap)
{
return new GRemapPainter();
}
else if (Mode == GTexturePaintingMode.Noise)
{
return new GNoisePainter();
}
else if (Mode == GTexturePaintingMode.SubDivision)
{
return new GSubDivPainter();
}
else if (Mode == GTexturePaintingMode.Visibility)
{
return new GVisibilityPainter();
}
else if (Mode == GTexturePaintingMode.Albedo)
{
return new GAlbedoPainter();
}
else if (Mode == GTexturePaintingMode.Metallic)
{
return new GMetallicPainter();
}
else if (Mode == GTexturePaintingMode.Smoothness)
{
return new GSmoothnessPainter();
}
else if (Mode == GTexturePaintingMode.Splat)
{
return new GSplatPainter();
}
else if (Mode == GTexturePaintingMode.Mask)
{
return new GMaskPainter();
}
else if (mode == GTexturePaintingMode.Custom)
{
if (CustomPainterIndex >= 0 && CustomPainterIndex < CustomPainterTypes.Count)
return System.Activator.CreateInstance(CustomPainterTypes[CustomPainterIndex]) as IGTexturePainter;
}
return null;
}
}
[SerializeField]
private float brushRadius;
public float BrushRadius
{
get
{
return brushRadius;
}
set
{
brushRadius = Mathf.Max(0.01f, value);
}
}
[SerializeField]
private float brushRadiusJitter;
public float BrushRadiusJitter
{
get
{
return brushRadiusJitter;
}
set
{
brushRadiusJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushRotation;
public float BrushRotation
{
get
{
return brushRotation;
}
set
{
brushRotation = value;
}
}
[SerializeField]
private float brushRotationJitter;
public float BrushRotationJitter
{
get
{
return brushRotationJitter;
}
set
{
brushRotationJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushOpacity;
public float BrushOpacity
{
get
{
return brushOpacity;
}
set
{
brushOpacity = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushOpacityJitter;
public float BrushOpacityJitter
{
get
{
return brushOpacityJitter;
}
set
{
brushOpacityJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushTargetStrength = 1;
public float BrushTargetStrength
{
get
{
return brushTargetStrength;
}
set
{
brushTargetStrength = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushScatter;
public float BrushScatter
{
get
{
return brushScatter;
}
set
{
brushScatter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushScatterJitter;
public float BrushScatterJitter
{
get
{
return brushScatterJitter;
}
set
{
brushScatterJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private Color brushColor;
public Color BrushColor
{
get
{
return brushColor;
}
set
{
brushColor = value;
}
}
[SerializeField]
private List<Texture2D> brushMasks;
public List<Texture2D> BrushMasks
{
get
{
if (brushMasks == null)
brushMasks = new List<Texture2D>();
return brushMasks;
}
set
{
brushMasks = value;
}
}
[SerializeField]
private int selectedBrushMaskIndex;
public int SelectedBrushMaskIndex
{
get
{
return selectedBrushMaskIndex;
}
set
{
if (BrushMasks.Count > 0)
selectedBrushMaskIndex = Mathf.Clamp(value, 0, BrushMasks.Count);
else
selectedBrushMaskIndex = -1;
}
}
public int SelectedSplatIndex
{
get
{
if (SelectedSplatIndices.Count == 0)
{
SelectedSplatIndices.Add(0);
}
return SelectedSplatIndices[0];
}
set
{
SelectedSplatIndices.Clear();
SelectedSplatIndices.Add(value);
}
}
[SerializeField]
private List<int> selectedSplatIndices;
public List<int> SelectedSplatIndices
{
get
{
if (selectedSplatIndices == null)
{
selectedSplatIndices = new List<int>();
}
return selectedSplatIndices;
}
set
{
selectedSplatIndices = value;
}
}
[SerializeField]
private Vector3 samplePoint;
public Vector3 SamplePoint
{
get
{
return samplePoint;
}
set
{
samplePoint = value;
}
}
[SerializeField]
private GConditionalPaintingConfigs conditionalPaintingConfigs;
public GConditionalPaintingConfigs ConditionalPaintingConfigs
{
get
{
return conditionalPaintingConfigs;
}
set
{
conditionalPaintingConfigs = value;
}
}
internal static Dictionary<string, RenderTexture> internal_RenderTextures;
private void OnEnable()
{
ReloadBrushMasks();
if (conditionalPaintingConfigs != null)
{
conditionalPaintingConfigs.UpdateCurveTextures();
}
}
private void OnDisable()
{
CleanUp();
}
private void Reset()
{
GroupId = 0;
Mode = GTexturePaintingMode.Elevation;
BrushRadius = 50;
BrushRadiusJitter = 0;
BrushOpacity = 0.5f;
BrushOpacityJitter = 0;
BrushTargetStrength = 1;
BrushRotation = 0;
BrushRotationJitter = 0;
BrushColor = Color.white;
if (conditionalPaintingConfigs != null)
{
conditionalPaintingConfigs.CleanUp();
}
conditionalPaintingConfigs = new GConditionalPaintingConfigs();
conditionalPaintingConfigs.UpdateCurveTextures();
}
public void ReloadBrushMasks()
{
BrushMasks = new List<Texture2D>(Resources.LoadAll<Texture2D>(GCommon.BRUSH_MASK_RESOURCES_PATH));
}
public void Paint(GTexturePainterArgs args)
{
IGTexturePainter p = ActivePainter;
if (p == null)
return;
FillArgs(ref args);
List<GStylizedTerrain> overlappedTerrain = GUtilities.ExtractTerrainsFromOverlapTest(GPaintToolUtilities.OverlapTest(GroupId, args.HitPoint, args.Radius, args.Rotation));
#if UNITY_EDITOR
if (args.MouseEventType == GPainterMouseEventType.Down ||
args.MouseEventType == GPainterMouseEventType.Drag)
{
Editor_CreateInitialHistoryEntry(args, overlappedTerrain);
}
#endif
foreach (GStylizedTerrain t in overlappedTerrain)
{
p.BeginPainting(t, args);
}
foreach (GStylizedTerrain t in overlappedTerrain)
{
p.EndPainting(t, args);
}
#if UNITY_EDITOR
EditedTerrains.UnionWith(overlappedTerrain);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
Editor_CreateHistory(args);
currentInitialBackupName = null;
InitialRecordedTerrains.Clear();
EditedTerrains.Clear();
}
#endif
}
#if UNITY_EDITOR
private HashSet<GStylizedTerrain> initialRecordedTerrains;
private HashSet<GStylizedTerrain> InitialRecordedTerrains
{
get
{
if (initialRecordedTerrains == null)
{
initialRecordedTerrains = new HashSet<GStylizedTerrain>();
}
return initialRecordedTerrains;
}
}
private HashSet<GStylizedTerrain> editedTerrains;
private HashSet<GStylizedTerrain> EditedTerrains
{
get
{
if (editedTerrains == null)
{
editedTerrains = new HashSet<GStylizedTerrain>();
}
return editedTerrains;
}
}
private string currentInitialBackupName;
private void Editor_CreateInitialHistoryEntry(GTexturePainterArgs args, List<GStylizedTerrain> overlappedTerrains)
{
if (!GEditorSettings.Instance.paintTools.enableHistory)
return;
if (overlappedTerrains.Count == 0)
return;
List<GTerrainResourceFlag> flags = new List<GTerrainResourceFlag>();
flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));
if (InitialRecordedTerrains.Count == 0)
{
currentInitialBackupName = GBackup.TryCreateInitialBackup(ActivePainter.HistoryPrefix, overlappedTerrains[0], flags, false);
if (!string.IsNullOrEmpty(currentInitialBackupName))
{
InitialRecordedTerrains.Add(overlappedTerrains[0]);
}
}
else
{
if (!string.IsNullOrEmpty(currentInitialBackupName))
{
for (int i = 0; i < overlappedTerrains.Count; ++i)
{
if (InitialRecordedTerrains.Contains(overlappedTerrains[i]))
continue;
GBackup.BackupTerrain(overlappedTerrains[i], currentInitialBackupName, flags);
InitialRecordedTerrains.Add(overlappedTerrains[i]);
}
}
}
}
private void Editor_CreateHistory(GTexturePainterArgs args)
{
if (!GEditorSettings.Instance.paintTools.enableHistory)
return;
if (EditedTerrains.Count == 0)
return;
List<GTerrainResourceFlag> flags = new List<GTerrainResourceFlag>();
flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));
List<GStylizedTerrain> terrainList = new List<GStylizedTerrain>(EditedTerrains);
string backupName = GBackup.TryCreateBackup(ActivePainter.HistoryPrefix, terrainList[0], flags, false);
if (!string.IsNullOrEmpty(backupName))
{
for (int i = 1; i < terrainList.Count; ++i)
{
GBackup.BackupTerrain(terrainList[i], backupName, flags);
}
}
}
#endif
internal static RenderTexture Internal_GetRenderTexture(GStylizedTerrain t, int resolution, int id = 0)
{
if (internal_RenderTextures == null)
{
internal_RenderTextures = new Dictionary<string, RenderTexture>();
}
string key = string.Format("{0}_{1}", t != null ? t.GetInstanceID() : 0, id);
if (!internal_RenderTextures.ContainsKey(key) ||
internal_RenderTextures[key] == null)
{
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
internal_RenderTextures[key] = rt;
}
else if (internal_RenderTextures[key].width != resolution ||
internal_RenderTextures[key].height != resolution ||
internal_RenderTextures[key].format != GGeometry.HeightMapRTFormat)
{
internal_RenderTextures[key].Release();
Object.DestroyImmediate(internal_RenderTextures[key]);
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
internal_RenderTextures[key] = rt;
}
internal_RenderTextures[key].wrapMode = TextureWrapMode.Clamp;
return internal_RenderTextures[key];
}
public static void Internal_ReleaseRenderTextures()
{
if (internal_RenderTextures != null)
{
foreach (string k in internal_RenderTextures.Keys)
{
RenderTexture rt = internal_RenderTextures[k];
if (rt == null)
continue;
rt.Release();
Object.DestroyImmediate(rt);
}
}
}
private Rand GetRandomGenerator()
{
return new Rand(System.DateTime.Now.Millisecond);
}
private void ProcessBrushDynamic(ref GTexturePainterArgs args)
{
Rand rand = GetRandomGenerator();
args.Radius -= BrushRadius * BrushRadiusJitter * (float)rand.NextDouble();
args.Rotation += Mathf.Sign((float)rand.NextDouble() - 0.5f) * BrushRotation * BrushRotationJitter * (float)rand.NextDouble();
args.Opacity -= BrushOpacity * BrushOpacityJitter * (float)rand.NextDouble();
Vector3 scatterDir = new Vector3((float)(rand.NextDouble() * 2 - 1), 0, (float)(rand.NextDouble() * 2 - 1)).normalized;
float scatterLengthMultiplier = BrushScatter - (float)rand.NextDouble() * BrushScatterJitter;
float scatterLength = args.Radius * scatterLengthMultiplier;
args.HitPoint += scatterDir * scatterLength;
}
public void FillArgs(ref GTexturePainterArgs args, bool useBrushDynamic = true)
{
args.Radius = BrushRadius;
args.Rotation = BrushRotation;
args.Opacity = BrushOpacity * BrushTargetStrength;
args.Color = BrushColor;
if (SelectedSplatIndices.Count > 0)
{
args.SplatIndex = SelectedSplatIndices[Random.Range(0, SelectedSplatIndices.Count)];
}
else
{
args.SplatIndex = -1;
}
args.SamplePoint = SamplePoint;
args.CustomArgs = CustomPainterArgs;
args.ForceUpdateGeometry = ForceUpdateGeometry;
args.EnableTerrainMask = EnableTerrainMask;
if (SelectedBrushMaskIndex >= 0 && SelectedBrushMaskIndex < BrushMasks.Count)
{
args.BrushMask = BrushMasks[SelectedBrushMaskIndex];
}
if (args.ActionType == GPainterActionType.Alternative &&
args.MouseEventType == GPainterMouseEventType.Down)
{
SamplePoint = args.HitPoint;
args.SamplePoint = args.HitPoint;
}
if (useBrushDynamic)
{
ProcessBrushDynamic(ref args);
}
Vector3[] corners = GCommon.GetBrushQuadCorners(args.HitPoint, args.Radius, args.Rotation);
args.WorldPointCorners = corners;
args.ConditionalPaintingConfigs = this.ConditionalPaintingConfigs;
}
public void CleanUp()
{
Internal_ReleaseRenderTextures();
if (conditionalPaintingConfigs != null)
{
conditionalPaintingConfigs.CleanUp();
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec1be11e0fe923b42bb2d698e52e0198
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
#if GRIFFIN
using UnityEngine;
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
public struct GTexturePainterArgs
{
public Vector3 HitPoint { get; set; }
public Vector3[] WorldPointCorners { get; set; }
public Texture BrushMask { get; set; }
public float Radius { get; set; }
public float Rotation { get; set; }
public float Opacity { get; set; }
public Color Color { get; set; }
public int SplatIndex { get; set; }
public Vector3 SamplePoint { get; set; }
public string CustomArgs { get; set; }
public GPainterMouseEventType MouseEventType { get; set; }
public GPainterActionType ActionType { get; set; }
public bool ForceUpdateGeometry { get; set; }
public bool EnableTerrainMask { get; set; }
public GConditionalPaintingConfigs ConditionalPaintingConfigs { get; set; }
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b70b9405f5d0824cab909b072f86618
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
//[CreateAssetMenu(menuName = "Griffin/Texture Painter Custom Params")]
public class GTexturePainterCustomParams : ScriptableObject
{
private static GTexturePainterCustomParams instance;
public static GTexturePainterCustomParams Instance
{
get
{
if (instance == null)
{
instance = Resources.Load<GTexturePainterCustomParams>("TexturePainterCustomParams");
if (instance == null)
{
instance = ScriptableObject.CreateInstance<GTexturePainterCustomParams>();
}
}
return instance;
}
}
[SerializeField]
private GTerracePainterParams terrace;
public GTerracePainterParams Terrace
{
get
{
return terrace;
}
set
{
terrace = value;
}
}
[SerializeField]
private GRemapPainterParams remap;
public GRemapPainterParams Remap
{
get
{
return remap;
}
set
{
remap = value;
}
}
[SerializeField]
private GNoisePainterParams noise;
public GNoisePainterParams Noise
{
get
{
return noise;
}
set
{
noise = value;
}
}
[SerializeField]
private GMaskPainterParams mask;
public GMaskPainterParams Mask
{
get
{
return mask;
}
set
{
mask = value;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1c6f07db5b50b1429e5c9bf5274042f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if GRIFFIN
namespace Pinwheel.Griffin.PaintTool
{
public enum GTexturePaintingMode
{
Elevation, HeightSampling, Terrace, Remap, Noise, SubDivision, Visibility, Albedo, Metallic, Smoothness, Splat, Mask, Custom
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4559f3cb800a56a4783b3b7bdb34d0c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,133 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public class GVisibilityPainter : IGTexturePainter, IGTexturePainterWithLivePreview
{
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int MASK = Shader.PropertyToID("_Mask");
private static readonly int OPACITY = Shader.PropertyToID("_Opacity");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static Material painterMaterial;
public static Material PainterMaterial
{
get
{
if (painterMaterial == null)
{
painterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.visibilityPainterShader);
}
return painterMaterial;
}
}
public string Instruction
{
get
{
string s = string.Format(
"Mark a particular area of terrain surface as visible or not.\n" +
" - Hold Left Mouse to paint.\n" +
" - Hold Ctrl & Left Mouse to erase.");
return s;
}
}
public string HistoryPrefix
{
get
{
return "Visibility Painting";
}
}
public List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args)
{
return GCommon.HeightMapAndFoliageResourceFlags;
}
public void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
if (args.MouseEventType == GPainterMouseEventType.Down)
{
terrain.ForceLOD(0);
GRuntimeSettings.Instance.isEditingGeometry = true;
}
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
RenderTexture.active = rt;
terrain.TerrainData.Geometry.HeightMap.ReadPixels(
new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
terrain.TerrainData.Geometry.HeightMap.Apply();
RenderTexture.active = null;
terrain.TerrainData.Geometry.SetRegionDirty(dirtyRect);
}
private void PaintOnRT(GStylizedTerrain terrain, GTexturePainterArgs args, RenderTexture rt, Vector2[] uvCorners)
{
Texture2D bg = terrain.TerrainData.Geometry.HeightMap;
GCommon.CopyToRT(bg, rt);
Material mat = PainterMaterial;
mat.SetTexture(MAIN_TEX, bg);
mat.SetTexture(MASK, args.BrushMask);
mat.SetFloat(OPACITY, Mathf.Pow(args.Opacity, GTerrainTexturePainter.GEOMETRY_OPACITY_EXPONENT));
if (args.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, terrain.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
int pass =
args.ActionType == GPainterActionType.Normal ? 0 :
args.ActionType == GPainterActionType.Negative ? 1 : 0;
GCommon.DrawQuad(rt, uvCorners, mat, pass);
}
public void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args)
{
terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
if (args.MouseEventType == GPainterMouseEventType.Up)
{
terrain.ForceLOD(-1);
GRuntimeSettings.Instance.isEditingGeometry = false;
}
}
public void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam)
{
#if UNITY_EDITOR
Vector2[] uvCorners = GPaintToolUtilities.WorldToUvCorners(terrain, args.WorldPointCorners);
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
int heightMapResolution = terrain.TerrainData.Geometry.HeightMapResolution;
RenderTexture rt = GTerrainTexturePainter.Internal_GetRenderTexture(terrain, heightMapResolution);
PaintOnRT(terrain, args, rt, uvCorners);
Matrix4x4 worldToMaskMatrix = Matrix4x4.TRS(
args.WorldPointCorners[0],
Quaternion.Euler(0, args.Rotation, 0),
args.Radius * 2 * Vector3.one).inverse;
GLivePreviewDrawer.DrawVisibilityLivePreview(terrain, cam, rt, dirtyRect, args.BrushMask, worldToMaskMatrix);
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ec1fff30a847dd4cb516e456a3c77a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
#if GRIFFIN
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
public interface IConditionalPainter
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85dc21eb047aaaa4db54d9a3f9ed779c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
#if GRIFFIN
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
public interface IGTexturePainter
{
string HistoryPrefix { get; }
string Instruction { get; }
void BeginPainting(GStylizedTerrain terrain, GTexturePainterArgs args);
void EndPainting(GStylizedTerrain terrain, GTexturePainterArgs args);
List<GTerrainResourceFlag> GetResourceFlagForHistory(GTexturePainterArgs args);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8be616b830d600646b1a9cbb3492efb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if GRIFFIN
namespace Pinwheel.Griffin.PaintTool
{
public interface IGTexturePainterWithCustomParams
{
void Editor_DrawCustomParamsGUI();
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01c5d52d49be4904a96c7c3887f728c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public interface IGTexturePainterWithLivePreview
{
void Editor_DrawLivePreview(GStylizedTerrain terrain, GTexturePainterArgs args, Camera cam);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4cdf524f9e719624cbabfab6c840c2d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a34d25df79fe1214590e2849f1088432
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,435 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Rand = System.Random;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
[System.Serializable]
[ExecuteInEditMode]
public class GObjectPainter : MonoBehaviour
{
private static readonly List<string> BUILTIN_PAINTER_NAME = new List<string>(new string[]
{
"GObjectSpawner",
"GObjectScaler"
});
private static List<Type> customPainterTypes;
public static List<Type> CustomPainterTypes
{
get
{
if (customPainterTypes == null)
customPainterTypes = new List<Type>();
return customPainterTypes;
}
private set
{
customPainterTypes = value;
}
}
public static string ObjectPainterInterfaceName
{
get
{
return typeof(IGObjectPainter).Name;
}
}
static GObjectPainter()
{
RefreshCustomPainterTypes();
}
public static void RefreshCustomPainterTypes()
{
List<Type> loadedTypes = GCommon.GetAllLoadedTypes();
CustomPainterTypes = loadedTypes.FindAll(
t => t.GetInterface(ObjectPainterInterfaceName) != null &&
!BUILTIN_PAINTER_NAME.Contains(t.Name));
}
[SerializeField]
private int groupId;
public int GroupId
{
get
{
return groupId;
}
set
{
groupId = value;
}
}
[SerializeField]
private bool enableTerrainMask;
public bool EnableTerrainMask
{
get
{
return enableTerrainMask;
}
set
{
enableTerrainMask = value;
}
}
[SerializeField]
private GObjectPaintingMode mode;
public GObjectPaintingMode Mode
{
get
{
return mode;
}
set
{
mode = value;
}
}
[SerializeField]
private int customPainterIndex;
public int CustomPainterIndex
{
get
{
return customPainterIndex;
}
set
{
customPainterIndex = value;
}
}
[SerializeField]
private string customPainterArgs;
public string CustomPainterArgs
{
get
{
return customPainterArgs;
}
set
{
customPainterArgs = value;
}
}
public IGObjectPainter ActivePainter
{
get
{
if (Mode == GObjectPaintingMode.Spawn)
{
return new GObjectSpawner();
}
else if (Mode == GObjectPaintingMode.Scale)
{
return new GObjectScaler();
}
else if (mode == GObjectPaintingMode.Custom)
{
if (CustomPainterIndex >= 0 && CustomPainterIndex < CustomPainterTypes.Count)
return System.Activator.CreateInstance(CustomPainterTypes[CustomPainterIndex]) as IGObjectPainter;
}
return null;
}
}
[SerializeField]
private float brushRadius;
public float BrushRadius
{
get
{
return brushRadius;
}
set
{
brushRadius = Mathf.Max(0.01f, value);
}
}
[SerializeField]
private float brushRadiusJitter;
public float BrushRadiusJitter
{
get
{
return brushRadiusJitter;
}
set
{
brushRadiusJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushRotation;
public float BrushRotation
{
get
{
return brushRotation;
}
set
{
brushRotation = value;
}
}
[SerializeField]
private float brushRotationJitter;
public float BrushRotationJitter
{
get
{
return brushRotationJitter;
}
set
{
brushRotationJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private int brushDensity;
public int BrushDensity
{
get
{
return brushDensity;
}
set
{
brushDensity = Mathf.Clamp(value, 1, 100);
}
}
[SerializeField]
private float brushDensityJitter;
public float BrushDensityJitter
{
get
{
return brushDensityJitter;
}
set
{
brushDensityJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushScatter;
public float BrushScatter
{
get
{
return brushScatter;
}
set
{
brushScatter = Mathf.Clamp01(value);
}
}
[SerializeField]
private float brushScatterJitter;
public float BrushScatterJitter
{
get
{
return brushScatterJitter;
}
set
{
brushScatterJitter = Mathf.Clamp01(value);
}
}
[SerializeField]
private List<Texture2D> brushMasks;
public List<Texture2D> BrushMasks
{
get
{
if (brushMasks == null)
brushMasks = new List<Texture2D>();
return brushMasks;
}
set
{
brushMasks = value;
}
}
[SerializeField]
private int selectedBrushMaskIndex;
public int SelectedBrushMaskIndex
{
get
{
return selectedBrushMaskIndex;
}
set
{
if (BrushMasks.Count > 0)
selectedBrushMaskIndex = Mathf.Clamp(value, 0, BrushMasks.Count);
else
selectedBrushMaskIndex = -1;
}
}
[SerializeField]
private List<GameObject> prototypes;
public List<GameObject> Prototypes
{
get
{
if (prototypes == null)
{
prototypes = new List<GameObject>();
}
return prototypes;
}
set
{
prototypes = value;
}
}
[SerializeField]
private List<int> selectedPrototypeIndices;
public List<int> SelectedPrototypeIndices
{
get
{
if (selectedPrototypeIndices == null)
{
selectedPrototypeIndices = new List<int>();
}
return selectedPrototypeIndices;
}
set
{
selectedPrototypeIndices = value;
}
}
[SerializeField]
private float eraseRatio;
public float EraseRatio
{
get
{
return eraseRatio;
}
set
{
eraseRatio = Mathf.Clamp01(value);
}
}
[SerializeField]
private float scaleStrength;
public float ScaleStrength
{
get
{
return scaleStrength;
}
set
{
scaleStrength = Mathf.Max(0, value);
}
}
private void OnEnable()
{
ReloadBrushMasks();
}
private void Reset()
{
GroupId = 0;
Mode = GObjectPaintingMode.Spawn;
BrushRadius = 50;
BrushRadiusJitter = 0;
BrushDensity = 1;
BrushDensityJitter = 0;
BrushRotation = 0;
BrushRotationJitter = 0;
EraseRatio = 1;
ScaleStrength = 1;
}
public void ReloadBrushMasks()
{
BrushMasks = new List<Texture2D>(Resources.LoadAll<Texture2D>(GCommon.BRUSH_MASK_RESOURCES_PATH));
}
public void Paint(GObjectPainterArgs args)
{
IGObjectPainter p = ActivePainter;
if (p == null)
return;
args.Radius = BrushRadius;
args.Rotation = BrushRotation;
args.Density = BrushDensity;
args.EraseRatio = EraseRatio;
args.ScaleStrength = ScaleStrength;
args.CustomArgs = CustomPainterArgs;
args.Filters = GetComponents<GSpawnFilter>();
if (SelectedBrushMaskIndex >= 0 && SelectedBrushMaskIndex < BrushMasks.Count)
{
args.Mask = BrushMasks[SelectedBrushMaskIndex];
}
if (SelectedPrototypeIndices.Count == 0)
{
return;
}
args.Prototypes = Prototypes;
args.PrototypeIndices = SelectedPrototypeIndices;
args.EnableTerrainMask = EnableTerrainMask;
ProcessBrushDynamic(ref args);
Vector3[] corners = GCommon.GetBrushQuadCorners(args.HitPoint, args.Radius, args.Rotation);
args.WorldPointCorners = corners;
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GPaintToolUtilities.OverlapTest(groupId, args.HitPoint, args.Radius, args.Rotation));
foreach(GStylizedTerrain t in terrains)
{
p.Paint(t, args);
}
}
private Rand GetRandomGenerator()
{
return new Rand(Time.frameCount);
}
private void ProcessBrushDynamic(ref GObjectPainterArgs args)
{
Rand rand = GetRandomGenerator();
args.Radius -= BrushRadius * BrushRadiusJitter * (float)rand.NextDouble();
args.Rotation += Mathf.Sign((float)rand.NextDouble() - 0.5f) * BrushRotation * BrushRotationJitter * (float)rand.NextDouble();
args.Density -= Mathf.RoundToInt(BrushDensity * BrushDensityJitter * (float)rand.NextDouble());
Vector3 scatterDir = new Vector3((float)(rand.NextDouble() * 2 - 1), 0, (float)(rand.NextDouble() * 2 - 1)).normalized;
float scatterLengthMultiplier = BrushScatter - (float)rand.NextDouble() * BrushScatterJitter;
float scatterLength = args.Radius * scatterLengthMultiplier;
args.HitPoint += scatterDir * scatterLength;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d026a0fb6cce7d4fa3a70b37045c798
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
public struct GObjectPainterArgs
{
public Vector3 HitPoint { get; set; }
public Vector3[] WorldPointCorners { get; internal set; }
public Texture2D Mask { get; set; }
public bool EnableTerrainMask { get; set; }
public float Radius { get; internal set; }
public float Rotation { get; internal set; }
public int Density { get; internal set; }
public float EraseRatio { get; internal set; }
public float ScaleStrength { get; internal set; }
public List<GameObject> Prototypes { get; set; }
public List<int> PrototypeIndices { get; set; }
public GSpawnFilter[] Filters { get; set; }
public string CustomArgs { get; internal set; }
public GPainterMouseEventType MouseEventType { get; set; }
public GPainterActionType ActionType { get; set; }
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6cb838285c72feb47ae47162c479bfff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if GRIFFIN
namespace Pinwheel.Griffin.PaintTool
{
public enum GObjectPaintingMode
{
Spawn, Scale, Custom
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1fd9419f68c9294f9d9a933e351db36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,158 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
public class GObjectScaler : IGObjectPainter
{
public string Instruction
{
get
{
return string.Format(
"Scale game object instances.\n" +
" - Hold Left Mouse to scale up.\n" +
" - Hold Ctrl & Left Mouse to scale down.");
}
}
public List<Type> SuitableFilterTypes
{
get
{
List<Type> types = new List<Type>(new Type[]
{
typeof(GScaleClampFilter)
});
GPaintToolUtilities.AddCustomSpawnFilter(types);
return types;
}
}
public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GObjectPainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
{
return;
}
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
return;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
int multiplier = args.ActionType == GPainterActionType.Normal ? 1 : -1;
int prototypeIndex = -1;
Vector2 terrainUv = Vector2.zero;
Vector3 worldPos = Vector3.zero;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Color maskColor = Color.white;
Vector3 scale = Vector3.zero;
for (int i = 0; i < args.PrototypeIndices.Count; ++i)
{
prototypeIndex = args.PrototypeIndices[i];
if (prototypeIndex < 0 || prototypeIndex >= args.Prototypes.Count)
continue;
GameObject g = args.Prototypes[prototypeIndex];
if (g == null)
continue;
GameObject root = GSpawner.GetRoot(terrain, g);
Transform[] instances = GUtilities.GetChildrenTransforms(root.transform);
int instanceCount = instances.Length;
for (int j = 0; j < instanceCount; ++j)
{
worldPos = instances[j].position;
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
continue;
}
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale)
continue;
}
if (args.EnableTerrainMask)
{
terrainUv = terrain.WorldPointToUV(worldPos);
maskColor = terrainMask.GetPixelBilinear(terrainUv.x, terrainUv.y);
if (Random.value < maskColor.r)
continue;
}
scale.Set(
Mathf.Max(0, instances[j].transform.localScale.x + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
Mathf.Max(0, instances[j].transform.localScale.y + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
Mathf.Max(0, instances[j].transform.localScale.z + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME));
GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
filterArgs.Terrain = terrain;
filterArgs.Position = worldPos;
filterArgs.Rotation = instances[j].transform.rotation;
filterArgs.Scale = scale;
List<Type> suitableFilter = SuitableFilterTypes;
if (args.Filters != null)
{
for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
{
if (args.Filters[fIndex] != null &&
args.Filters[fIndex].Ignore != true)
{
if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
args.Filters[fIndex].Apply(ref filterArgs);
}
if (filterArgs.ShouldExclude)
break;
}
}
instances[j].transform.localScale = filterArgs.Scale;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98b0f017deda5384ba9f2b94d9735693
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,268 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Type = System.Type;
namespace Pinwheel.Griffin.PaintTool
{
public class GObjectSpawner : IGObjectPainter
{
public string Instruction
{
get
{
return string.Format(
"Spawn game object into the scene.\n" +
" - Hold Left Mouse to spawn.\n" +
" - Hold Ctrl & Left Mouse to erase.");
}
}
public List<Type> SuitableFilterTypes
{
get
{
List<Type> types = new List<Type>(new Type[]
{
typeof(GAlignToSurfaceFilter),
typeof(GHeightConstraintFilter),
typeof(GSlopeConstraintFilter),
typeof(GRotationRandomizeFilter),
typeof(GScaleRandomizeFilter),
typeof(GScaleClampFilter)
});
GPaintToolUtilities.AddCustomSpawnFilter(types);
return types;
}
}
public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GObjectPainterArgs args)
{
if (args.MouseEventType == GPainterMouseEventType.Up)
return;
Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
for (int i = 0; i < uvCorners.Length; ++i)
{
uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
}
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
return;
if (args.ActionType == GPainterActionType.Normal)
{
HandleSpawnObject(terrain, args);
}
else if (args.ActionType == GPainterActionType.Negative)
{
HandleEraseObject(terrain, args);
}
}
private void HandleSpawnObject(GStylizedTerrain terrain, GObjectPainterArgs args)
{
int prototypeIndex = -1;
Vector3 randomPos = Vector3.zero;
Vector3 rayOrigin = Vector3.zero;
Vector3 rayDirection = Vector3.down;
float sqrtTwo = Mathf.Sqrt(2);
Ray ray = new Ray();
RaycastHit samplePoint;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Vector2 samplePointTexcoord = Vector2.zero;
Color maskColor = Color.white;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
for (int i = 0; i < args.Density; ++i)
{
prototypeIndex = args.PrototypeIndices[Random.Range(0, args.PrototypeIndices.Count)];
if (prototypeIndex < 0 || prototypeIndex >= args.Prototypes.Count)
continue;
GameObject g = args.Prototypes[prototypeIndex];
if (g == null)
continue;
randomPos = args.HitPoint + Random.insideUnitSphere * args.Radius * sqrtTwo;
rayOrigin.Set(
randomPos.x,
10000,
randomPos.z);
ray.origin = rayOrigin;
ray.direction = rayDirection;
if (terrain.Raycast(ray, out samplePoint, float.MaxValue))
{
GUtilities.CalculateBarycentricCoord(
new Vector2(samplePoint.point.x, samplePoint.point.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(samplePoint.point.x, samplePoint.point.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
continue;
}
//sample mask
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale)
continue;
}
//sample terrain mask
if (args.EnableTerrainMask)
{
samplePointTexcoord = samplePoint.textureCoord;
maskColor = terrainMask.GetPixelBilinear(samplePointTexcoord.x, samplePointTexcoord.y);
if (Random.value < maskColor.r)
continue;
}
//apply filter
GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
filterArgs.Terrain = terrain;
filterArgs.Position = samplePoint.point;
filterArgs.SurfaceNormal = samplePoint.normal;
filterArgs.SurfaceTexcoord = samplePoint.textureCoord;
List<Type> suitableFilter = SuitableFilterTypes;
if (args.Filters != null)
{
for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
{
if (args.Filters[fIndex] != null &&
args.Filters[fIndex].Ignore != true)
{
if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
args.Filters[fIndex].Apply(ref filterArgs);
}
if (filterArgs.ShouldExclude)
break;
}
}
//spawn
if (filterArgs.ShouldExclude)
continue;
//spawn here
GameObject instance = GSpawner.Spawn(terrain, g, samplePoint.point);
instance.transform.position = filterArgs.Position;
instance.transform.rotation = filterArgs.Rotation;
instance.transform.localScale = filterArgs.Scale;
}
}
if (clonedMask != null)
Object.DestroyImmediate(clonedMask);
}
private void HandleEraseObject(GStylizedTerrain terrain, GObjectPainterArgs args)
{
int prototypeIndex = -1;
Vector2 terrainUv = Vector3.zero;
Vector3 worldPos = Vector3.zero;
Vector3 bary0 = Vector3.zero;
Vector3 bary1 = Vector3.zero;
Vector2 maskUv = Vector2.zero;
Color maskColor = Color.white;
Texture2D clonedMask = null;
Texture2D terrainMask = null;
if (args.Mask != null)
{
clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
}
if (args.EnableTerrainMask)
{
terrainMask = terrain.TerrainData.Mask.MaskMapOrDefault;
}
for (int i = 0; i < args.PrototypeIndices.Count; ++i)
{
prototypeIndex = args.PrototypeIndices[i];
if (prototypeIndex < 0 || prototypeIndex >= args.Prototypes.Count)
continue;
GameObject g = args.Prototypes[prototypeIndex];
if (g == null)
continue;
GSpawner.DestroyIf(terrain, g, (instance =>
{
worldPos = instance.transform.position;
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
ref bary0);
GUtilities.CalculateBarycentricCoord(
new Vector2(worldPos.x, worldPos.z),
new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
ref bary1);
if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
{
maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
}
else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
{
maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
}
else
{
return false;
}
//sample mask
if (clonedMask != null)
{
maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
if (Random.value > maskColor.grayscale * args.EraseRatio)
return false;
}
if (args.EnableTerrainMask)
{
terrainUv = terrain.WorldPointToUV(worldPos);
maskColor = terrainMask.GetPixelBilinear(terrainUv.x, terrainUv.y);
if (Random.value < maskColor.r)
return false;
}
return true;
}));
}
if (clonedMask != null)
Object.DestroyImmediate(clonedMask);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8952777ed5ccbef4e8e819b41bdda8f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
#if GRIFFIN
using System.Collections.Generic;
namespace Pinwheel.Griffin.PaintTool
{
public interface IGObjectPainter
{
string Instruction { get; }
List<System.Type> SuitableFilterTypes { get; }
void Paint(GStylizedTerrain terrain, GObjectPainterArgs args);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5d13f18c0e50cc4b906fec0d92b36c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ee8b2e9b1ecc46479fe1a9ad410300f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[GDisplayName("Align To Surface")]
public class GAlignToSurfaceFilter : GSpawnFilter
{
public override void Apply(ref GSpawnFilterArgs args)
{
Quaternion currentRotationY = Quaternion.Euler(0, args.Rotation.eulerAngles.y, 0);
Quaternion rotationXZ = Quaternion.FromToRotation(Vector3.up, args.SurfaceNormal);
args.Rotation = rotationXZ * currentRotationY;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0447f32f2703d1498a766ae0d698309
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
#if GRIFFIN
//using UnityEngine;
//using System.Collections;
//using System.Collections.Generic;
//using Pinwheel.Griffin.PaintTool;
//namespace Pinwheel.Griffin.PaintTool
//{
// [GDisplayName("Distance Constraint")]
// public class GDistanceConstraintFilter : GSpawnFilter
// {
// [SerializeField]
// private bool checkAllPrototype;
// public bool CheckAllPrototype
// {
// get
// {
// return checkAllPrototype;
// }
// set
// {
// checkAllPrototype = value;
// }
// }
// [SerializeField]
// private float minimumDistance;
// public float MinimumDistance
// {
// get
// {
// return minimumDistance;
// }
// set
// {
// minimumDistance = Mathf.Max(0, value);
// }
// }
// public override void Apply(ref GSpawnFilterArgs args)
// {
// float sqrDistance = 0;
// float sqrMinimumDistance = MinimumDistance * MinimumDistance;
// Vector3 localPos = Vector3.zero;
// Vector3 worldPos = Vector3.zero;
// Vector3 terrainSize = new Vector3(
// args.Terrain.TerrainData.Geometry.Width,
// args.Terrain.TerrainData.Geometry.Height,
// args.Terrain.TerrainData.Geometry.Length);
// List<GTreeInstance> instances = args.Terrain.TerrainData.Foliage.TreeInstances;
// for (int i = instances.Count - 1; i >= 0; --i)
// {
// if (!CheckAllPrototype &&
// instances[i].PrototypeIndex != args.PrototypeIndex)
// continue;
// localPos.Set(
// terrainSize.x * instances[i].Position.x,
// terrainSize.y * instances[i].Position.y,
// terrainSize.z * instances[i].Position.z);
// worldPos = args.Terrain.transform.TransformPoint(localPos);
// sqrDistance = (args.Position - worldPos).sqrMagnitude;
// if (sqrDistance < sqrMinimumDistance)
// {
// args.ShouldExclude = true;
// return;
// }
// }
// args.ShouldExclude = false;
// }
// private void Reset()
// {
// CheckAllPrototype = false;
// MinimumDistance = 1;
// }
// }
//}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec6645d2bc213064e94be134d652b9c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,74 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[GDisplayName("Height Constraint")]
public class GHeightConstraintFilter : GSpawnFilter
{
[SerializeField]
private Space space;
public Space Space
{
get
{
return space;
}
set
{
space = value;
}
}
[SerializeField]
private float minHeight;
public float MinHeight
{
get
{
return minHeight;
}
set
{
minHeight = value;
}
}
[SerializeField]
private float maxHeight;
public float MaxHeight
{
get
{
return maxHeight;
}
set
{
maxHeight = value;
}
}
public override void Apply(ref GSpawnFilterArgs args)
{
float h = 0;
if (Space == Space.World)
{
h = args.Position.y;
}
else
{
h = args.Terrain.transform.InverseTransformPoint(args.Position).y;
}
args.ShouldExclude = h < MinHeight || h > MaxHeight;
}
private void Reset()
{
Space = Space.Self;
MinHeight = 0;
MaxHeight = 1000;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6de8c0af7acc3d41be2ecaa8426ccab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[GDisplayName("Randomize Rotation")]
public class GRotationRandomizeFilter : GSpawnFilter
{
[SerializeField]
private float minAngle;
public float MinAngle
{
get
{
return minAngle;
}
set
{
minAngle = value;
}
}
[SerializeField]
private float maxAngle;
public float MaxAngle
{
get
{
return maxAngle;
}
set
{
maxAngle = value;
}
}
public override void Apply(ref GSpawnFilterArgs args)
{
args.Rotation = Quaternion.Euler(0, Random.Range(MinAngle, MaxAngle), 0);
}
private void Reset()
{
MinAngle = 0;
MaxAngle = 360;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c0a5e377418a3741979fd128755604b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[GDisplayName("Clamp Scale")]
public class GScaleClampFilter : GSpawnFilter
{
[SerializeField]
private Vector3 minScale;
public Vector3 MinScale
{
get
{
return minScale;
}
set
{
minScale = value;
}
}
[SerializeField]
private Vector3 maxScale;
public Vector3 MaxScale
{
get
{
return maxScale;
}
set
{
maxScale = value;
}
}
public override void Apply(ref GSpawnFilterArgs args)
{
Vector3 scale = new Vector3(
Mathf.Clamp(args.Scale.x, MinScale.x, MaxScale.x),
Mathf.Clamp(args.Scale.y, MinScale.y, MaxScale.y),
Mathf.Clamp(args.Scale.z, MinScale.z, MaxScale.z));
args.Scale = scale;
}
private void Reset()
{
MinScale = new Vector3(0.5f, 0.5f, 0.5f);
MaxScale = new Vector3(2f, 2f, 2f);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 04289f35afcf6a644ab7f395bbb08b50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.PaintTool
{
[GDisplayName("Randomize Scale")]
public class GScaleRandomizeFilter : GSpawnFilter
{
[SerializeField]
private Vector3 minScale;
public Vector3 MinScale
{
get
{
return minScale;
}
set
{
minScale = value;
}
}
[SerializeField]
private Vector3 maxScale;
public Vector3 MaxScale
{
get
{
return maxScale;
}
set
{
maxScale = value;
}
}
public override void Apply(ref GSpawnFilterArgs args)
{
Vector3 scale = Vector3.Lerp(MinScale, MaxScale, Random.value);
args.Scale = scale;
}
private void Reset()
{
MinScale = new Vector3(0.7f, 0.8f, 0.7f);
MaxScale = new Vector3(1.2f, 1.5f, 1.2f);
}
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More