1
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
#if GRIFFIN
|
||||
using Pinwheel.Griffin.TextureTool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
public class GConditionalStampLayer
|
||||
{
|
||||
[SerializeField]
|
||||
private string name;
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
set
|
||||
{
|
||||
name = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool ignore;
|
||||
public bool Ignore
|
||||
{
|
||||
get
|
||||
{
|
||||
return ignore;
|
||||
}
|
||||
set
|
||||
{
|
||||
ignore = value;
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
~GConditionalStampLayer()
|
||||
{
|
||||
if (heightTransitionTexture != null)
|
||||
GUtilities.DestroyObject(heightTransitionTexture);
|
||||
if (slopeTransitionTexture != null)
|
||||
GUtilities.DestroyObject(slopeTransitionTexture);
|
||||
if (noiseRemapTexture != null)
|
||||
GUtilities.DestroyObject(noiseRemapTexture);
|
||||
}
|
||||
|
||||
public void UpdateCurveTextures()
|
||||
{
|
||||
if (heightTransitionTexture != null)
|
||||
GUtilities.DestroyObject(heightTransitionTexture);
|
||||
if (slopeTransitionTexture != null)
|
||||
GUtilities.DestroyObject(slopeTransitionTexture);
|
||||
if (noiseRemapTexture != null)
|
||||
GUtilities.DestroyObject(noiseRemapTexture);
|
||||
|
||||
heightTransitionTexture = GCommon.CreateTextureFromCurve(HeightTransition, 256, 1);
|
||||
slopeTransitionTexture = GCommon.CreateTextureFromCurve(SlopeTransition, 256, 1);
|
||||
noiseRemapTexture = GCommon.CreateTextureFromCurve(NoiseRemap, 256, 1);
|
||||
}
|
||||
|
||||
public GConditionalStampLayer()
|
||||
{
|
||||
Ignore = false;
|
||||
BlendHeight = true;
|
||||
MinHeight = 0;
|
||||
MaxHeight = 1000;
|
||||
HeightTransition = AnimationCurve.EaseInOut(0, 1, 1, 1);
|
||||
BlendSlope = true;
|
||||
MinSlope = 0;
|
||||
MaxSlope = 90;
|
||||
SlopeTransition = AnimationCurve.EaseInOut(0, 1, 1, 1);
|
||||
BlendNoise = false;
|
||||
NoiseOrigin = Vector2.zero;
|
||||
NoiseFrequency = 1f;
|
||||
NoiseOctaves = 1;
|
||||
NoiseLacunarity = 2;
|
||||
NoisePersistence = 0.5f;
|
||||
NoiseRemap = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3606907f819ef9a43b61141ba1580450
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,196 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
public class GFoliageStampLayer : GConditionalStampLayer
|
||||
{
|
||||
[SerializeField]
|
||||
private Color visualizeColor;
|
||||
public Color VisualizeColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return visualizeColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
visualizeColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool stampTrees;
|
||||
public bool StampTrees
|
||||
{
|
||||
get
|
||||
{
|
||||
return stampTrees;
|
||||
}
|
||||
set
|
||||
{
|
||||
stampTrees = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<int> treeIndices;
|
||||
public List<int> TreeIndices
|
||||
{
|
||||
get
|
||||
{
|
||||
if (treeIndices == null)
|
||||
{
|
||||
treeIndices = new List<int>();
|
||||
}
|
||||
return treeIndices;
|
||||
}
|
||||
set
|
||||
{
|
||||
treeIndices = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool stampGrass;
|
||||
public bool StampGrasses
|
||||
{
|
||||
get
|
||||
{
|
||||
return stampGrass;
|
||||
}
|
||||
set
|
||||
{
|
||||
stampGrass = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<int> grassIndices;
|
||||
public List<int> GrassIndices
|
||||
{
|
||||
get
|
||||
{
|
||||
if (grassIndices == null)
|
||||
{
|
||||
grassIndices = new List<int>();
|
||||
}
|
||||
return grassIndices;
|
||||
}
|
||||
set
|
||||
{
|
||||
grassIndices = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int treeInstanceCount;
|
||||
public int TreeInstanceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return treeInstanceCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
treeInstanceCount = Mathf.Max(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int grassInstanceCount;
|
||||
public int GrassInstanceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return grassInstanceCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
grassInstanceCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float minRotation;
|
||||
public float MinRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return minRotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
minRotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float maxRotation;
|
||||
public float MaxRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return maxRotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
maxRotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[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 GFoliageStampLayer() : base() { }
|
||||
|
||||
public static GFoliageStampLayer Create()
|
||||
{
|
||||
GFoliageStampLayer layer = new GFoliageStampLayer();
|
||||
layer.StampTrees = true;
|
||||
layer.TreeIndices = null;
|
||||
layer.StampGrasses = false;
|
||||
layer.GrassIndices = null;
|
||||
layer.TreeInstanceCount = 1000;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
layer.VisualizeColor = GEditorSettings.Instance.stampTools.visualizeColor;
|
||||
layer.MinRotation = GEditorSettings.Instance.stampTools.minRotation;
|
||||
layer.MaxRotation = GEditorSettings.Instance.stampTools.maxRotation;
|
||||
layer.MinScale = GEditorSettings.Instance.stampTools.minScale;
|
||||
layer.MaxScale = GEditorSettings.Instance.stampTools.maxScale;
|
||||
#endif
|
||||
|
||||
layer.UpdateCurveTextures();
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81ade946a78e26f4e83f0f4975f2281f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,606 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
[ExecuteInEditMode]
|
||||
public class GFoliageStamper : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private bool enableTerrainMask;
|
||||
public bool EnableTerrainMask
|
||||
{
|
||||
get
|
||||
{
|
||||
return enableTerrainMask;
|
||||
}
|
||||
set
|
||||
{
|
||||
enableTerrainMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int groupId;
|
||||
public int GroupId
|
||||
{
|
||||
get
|
||||
{
|
||||
return groupId;
|
||||
}
|
||||
set
|
||||
{
|
||||
groupId = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 position;
|
||||
public Vector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
position = value;
|
||||
transform.position = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Quaternion rotation;
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
rotation = value;
|
||||
transform.rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 scale;
|
||||
public Vector3 Scale
|
||||
{
|
||||
get
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
set
|
||||
{
|
||||
scale = value;
|
||||
transform.localScale = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D mask;
|
||||
public Texture2D Mask
|
||||
{
|
||||
get
|
||||
{
|
||||
return mask;
|
||||
}
|
||||
set
|
||||
{
|
||||
mask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve falloff;
|
||||
public AnimationCurve Falloff
|
||||
{
|
||||
get
|
||||
{
|
||||
return falloff;
|
||||
}
|
||||
set
|
||||
{
|
||||
falloff = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<GFoliageStampLayer> layers;
|
||||
public List<GFoliageStampLayer> Layers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (layers == null)
|
||||
{
|
||||
layers = new List<GFoliageStampLayer>();
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
set
|
||||
{
|
||||
layers = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int maskResolution;
|
||||
public int MaskResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return maskResolution;
|
||||
}
|
||||
set
|
||||
{
|
||||
maskResolution = Mathf.Clamp(Mathf.ClosestPowerOfTwo(value), GCommon.TEXTURE_SIZE_MIN, GCommon.TEXTURE_SIZE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect Rect
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3[] quad = new Vector3[4];
|
||||
GetQuad(quad);
|
||||
Rect r = GUtilities.GetRectContainsPoints(quad);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
private Texture2D falloffTexture;
|
||||
|
||||
private Vector3[] worldPoints = new Vector3[4];
|
||||
private Vector2[] uvPoints = new Vector2[4];
|
||||
|
||||
private Dictionary<string, RenderTexture> tempRt;
|
||||
private Dictionary<string, RenderTexture> TempRt
|
||||
{
|
||||
get
|
||||
{
|
||||
if (tempRt == null)
|
||||
{
|
||||
tempRt = new Dictionary<string, RenderTexture>();
|
||||
}
|
||||
return tempRt;
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
rotation = Quaternion.identity;
|
||||
scale = Vector3.one * 100;
|
||||
mask = null;
|
||||
falloff = AnimationCurve.EaseInOut(0, 1, 1, 0);
|
||||
maskResolution = 1024;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ReleaseResources();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ReleaseResources();
|
||||
}
|
||||
|
||||
private void ReleaseResources()
|
||||
{
|
||||
foreach (RenderTexture rt in TempRt.Values)
|
||||
{
|
||||
if (rt != null)
|
||||
{
|
||||
rt.Release();
|
||||
GUtilities.DestroyObject(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
Object.DestroyImmediate(falloffTexture);
|
||||
Internal_UpdateFalloffTexture();
|
||||
Internal_UpdateLayerTransitionTextures();
|
||||
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GCommon.OverlapTest(groupId, GetQuad()));
|
||||
|
||||
try
|
||||
{
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
Apply(t);
|
||||
}
|
||||
}
|
||||
catch (GProgressCancelledException)
|
||||
{
|
||||
Debug.Log("Stamp process canceled, result may be incorrect. Use History to clean up!");
|
||||
#if UNITY_EDITOR
|
||||
GCommonGUI.ClearProgressBar();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
if (Layers.Count == 0)
|
||||
return;
|
||||
|
||||
GetQuad(worldPoints);
|
||||
GetUvPoints(t, worldPoints, uvPoints);
|
||||
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvPoints);
|
||||
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
|
||||
return;
|
||||
|
||||
RenderTexture[] brushes = new RenderTexture[Layers.Count];
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
brushes[i] = GetRenderTexture("brush" + i.ToString());
|
||||
}
|
||||
Internal_RenderBrushes(brushes, t, uvPoints);
|
||||
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
StampLayer(t, brushes[i], i);
|
||||
}
|
||||
|
||||
bool willUpdateTree = Layers.Exists(l => l.StampTrees && l.TreeInstanceCount > 0);
|
||||
bool willUpdateGrass = Layers.Exists(l => l.StampGrasses && l.TreeInstanceCount > 0);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string title = "Stamping on " + t.name;
|
||||
string info = "Finishing up...";
|
||||
GCommonGUI.ProgressBar(title, info, 1);
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
t.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
|
||||
t.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
|
||||
if (willUpdateTree)
|
||||
t.UpdateTreesPosition();
|
||||
if (willUpdateGrass)
|
||||
t.UpdateGrassPatches();
|
||||
t.TerrainData.Foliage.ClearTreeDirtyRegions();
|
||||
t.TerrainData.Foliage.ClearGrassDirtyRegions();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
GCommonGUI.ClearProgressBar();
|
||||
#endif
|
||||
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
|
||||
}
|
||||
|
||||
private void StampLayer(GStylizedTerrain t, RenderTexture brush, int layerIndex)
|
||||
{
|
||||
GFoliageStampLayer layer = Layers[layerIndex];
|
||||
if (layer.Ignore)
|
||||
return;
|
||||
if (layer.TreeInstanceCount == 0)
|
||||
return;
|
||||
|
||||
Texture2D layerMask = GCommon.CreateTexture(MaskResolution, Color.clear);
|
||||
GCommon.CopyFromRT(layerMask, brush);
|
||||
|
||||
if (layer.StampTrees &&
|
||||
layer.TreeIndices.Count > 0 &&
|
||||
t.TerrainData.Foliage.Trees != null)
|
||||
{
|
||||
SpawnTreeOnTerrain(t, layerMask, layerIndex);
|
||||
}
|
||||
|
||||
if (layer.StampGrasses &&
|
||||
layer.GrassIndices.Count > 0 &&
|
||||
t.TerrainData.Foliage.Grasses != null)
|
||||
{
|
||||
SpawnGrassOnTerrain(t, layerMask, layerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnTreeOnTerrain(GStylizedTerrain t, Texture2D layerMask, int layerIndex)
|
||||
{
|
||||
GFoliageStampLayer layer = Layers[layerIndex];
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string title = "Stamping on " + t.name;
|
||||
string info = string.Format("Layer: {0}", !string.IsNullOrEmpty(layer.Name) ? layer.Name : layerIndex.ToString());
|
||||
int currentPercent = 0;
|
||||
int attemptPercent = 0;
|
||||
GCommonGUI.CancelableProgressBar(title, info, 0);
|
||||
#endif
|
||||
|
||||
int sampleCount = layer.TreeInstanceCount;
|
||||
NativeArray<bool> cullResult = new NativeArray<bool>(sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<GPrototypeInstanceInfo> foliageInfo = new NativeArray<GPrototypeInstanceInfo>(sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<int> selectedPrototypeIndices = new NativeArray<int>(layer.TreeIndices.ToArray(), Allocator.TempJob);
|
||||
GTextureNativeDataDescriptor<Color32> maskHandle = new GTextureNativeDataDescriptor<Color32>(layerMask);
|
||||
|
||||
GSampleInstanceJob job = new GSampleInstanceJob()
|
||||
{
|
||||
cullResult = cullResult,
|
||||
instanceInfo = foliageInfo,
|
||||
mask = maskHandle,
|
||||
selectedPrototypeIndices = selectedPrototypeIndices,
|
||||
|
||||
minRotation = layer.MinRotation,
|
||||
maxRotation = layer.MaxRotation,
|
||||
|
||||
minScale = layer.MinScale,
|
||||
maxScale = layer.MaxScale,
|
||||
|
||||
seed = Random.Range(int.MinValue, int.MaxValue)
|
||||
};
|
||||
|
||||
JobHandle jHandle = job.Schedule(sampleCount, 100);
|
||||
jHandle.Complete();
|
||||
|
||||
List<GTreeInstance> instances = new List<GTreeInstance>();
|
||||
for (int i = 0; i < sampleCount; ++i)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
attemptPercent = (int)(i * 100.0f / sampleCount);
|
||||
if (currentPercent != attemptPercent)
|
||||
{
|
||||
currentPercent = attemptPercent;
|
||||
GCommonGUI.CancelableProgressBar(title, string.Format("{0} ... {1}%", info, currentPercent), currentPercent / 100.0f);
|
||||
}
|
||||
#endif
|
||||
if (cullResult[i] == false)
|
||||
continue;
|
||||
GTreeInstance tree = foliageInfo[i].ToTreeInstance();
|
||||
instances.Add(tree);
|
||||
}
|
||||
t.TerrainData.Foliage.AddTreeInstances(instances);
|
||||
|
||||
cullResult.Dispose();
|
||||
foliageInfo.Dispose();
|
||||
selectedPrototypeIndices.Dispose();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
GCommonGUI.ClearProgressBar();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void SpawnGrassOnTerrain(GStylizedTerrain t, Texture2D layerMask, int layerIndex)
|
||||
{
|
||||
GFoliageStampLayer layer = Layers[layerIndex];
|
||||
#if UNITY_EDITOR
|
||||
string title = "Stamping on " + t.name;
|
||||
string info = string.Format("Layer: {0}", !string.IsNullOrEmpty(layer.Name) ? layer.Name : layerIndex.ToString());
|
||||
GCommonGUI.CancelableProgressBar(title, info, 1);
|
||||
#endif
|
||||
|
||||
int sampleCount = layer.GrassInstanceCount;
|
||||
NativeArray<bool> cullResultNA = new NativeArray<bool>(sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<GPrototypeInstanceInfo> foliageInfoNA = new NativeArray<GPrototypeInstanceInfo>(sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<int> selectedPrototypeIndicesNA = new NativeArray<int>(layer.GrassIndices.ToArray(), Allocator.TempJob);
|
||||
GTextureNativeDataDescriptor<Color32> maskHandleNA = new GTextureNativeDataDescriptor<Color32>(layerMask);
|
||||
|
||||
GSampleInstanceJob job = new GSampleInstanceJob()
|
||||
{
|
||||
cullResult = cullResultNA,
|
||||
instanceInfo = foliageInfoNA,
|
||||
mask = maskHandleNA,
|
||||
selectedPrototypeIndices = selectedPrototypeIndicesNA,
|
||||
|
||||
minRotation = layer.MinRotation,
|
||||
maxRotation = layer.MaxRotation,
|
||||
|
||||
minScale = layer.MinScale,
|
||||
maxScale = layer.MaxScale,
|
||||
|
||||
seed = Random.Range(int.MinValue, int.MaxValue)
|
||||
};
|
||||
|
||||
JobHandle jHandle = job.Schedule(sampleCount, 100);
|
||||
jHandle.Complete();
|
||||
|
||||
t.TerrainData.Foliage.AddGrassInstancesWithFilter(cullResultNA, foliageInfoNA);
|
||||
cullResultNA.Dispose();
|
||||
foliageInfoNA.Dispose();
|
||||
selectedPrototypeIndicesNA.Dispose();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
GCommonGUI.ClearProgressBar();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Internal_RenderBrushes(RenderTexture[] brushes, GStylizedTerrain t, Vector2[] uvPoints)
|
||||
{
|
||||
for (int i = 0; i < brushes.Length; ++i)
|
||||
{
|
||||
GStampLayerMaskRenderer.Render(
|
||||
brushes[i],
|
||||
Layers[i],
|
||||
t,
|
||||
Matrix4x4.TRS(Position, Rotation, Scale),
|
||||
Mask,
|
||||
falloffTexture,
|
||||
uvPoints,
|
||||
EnableTerrainMask);
|
||||
}
|
||||
}
|
||||
|
||||
public void Internal_UpdateFalloffTexture()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
GUtilities.DestroyObject(falloffTexture);
|
||||
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
|
||||
}
|
||||
|
||||
public void Internal_UpdateLayerTransitionTextures()
|
||||
{
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
Layers[i].UpdateCurveTextures();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3[] GetQuad()
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
Vector3[] quad = new Vector3[4]
|
||||
{
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f))
|
||||
};
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
public void GetQuad(Vector3[] quad)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
quad[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
quad[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
quad[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
quad[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
}
|
||||
|
||||
private void GetUvPoints(GStylizedTerrain t, Vector3[] worldPoint, Vector2[] uvPoint)
|
||||
{
|
||||
for (int i = 0; i < uvPoints.Length; ++i)
|
||||
{
|
||||
uvPoints[i] = t.WorldPointToUV(worldPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetBox(Vector3[] box)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
box[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
box[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
box[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
box[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
box[4] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, -0.5f));
|
||||
box[5] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, 0.5f));
|
||||
box[6] = matrix.MultiplyPoint(new Vector3(0.5f, 1, 0.5f));
|
||||
box[7] = matrix.MultiplyPoint(new Vector3(0.5f, 1, -0.5f));
|
||||
}
|
||||
|
||||
private RenderTexture GetRenderTexture(string key)
|
||||
{
|
||||
int resolution = MaskResolution;
|
||||
if (!TempRt.ContainsKey(key) ||
|
||||
TempRt[key] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
TempRt[key] = rt;
|
||||
}
|
||||
else if (TempRt[key].width != resolution || TempRt[key].height != resolution)
|
||||
{
|
||||
TempRt[key].Release();
|
||||
Object.DestroyImmediate(TempRt[key]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
TempRt[key] = rt;
|
||||
}
|
||||
|
||||
return TempRt[key];
|
||||
}
|
||||
|
||||
public void ClearTrees()
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GCommon.OverlapTest(GroupId, GetQuad()));
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
ClearTrees(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTrees(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
if (t.TerrainData.Foliage.Trees == null)
|
||||
return;
|
||||
Vector3 terrainSize = new Vector3(
|
||||
t.TerrainData.Geometry.Width,
|
||||
t.TerrainData.Geometry.Height,
|
||||
t.TerrainData.Geometry.Length);
|
||||
Vector3 scale = new Vector3(
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.x, Scale.x),
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.y, Scale.y),
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.z, Scale.z));
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(
|
||||
t.WorldPointToNormalized(Position),
|
||||
Rotation,
|
||||
scale);
|
||||
Matrix4x4 normalizeToStamp = matrix.inverse;
|
||||
|
||||
t.TerrainData.Foliage.RemoveTreeInstances(tree =>
|
||||
{
|
||||
Vector3 stampSpacePos = normalizeToStamp.MultiplyPoint(tree.Position);
|
||||
return
|
||||
stampSpacePos.x >= -0.5f && stampSpacePos.x <= 0.5f &&
|
||||
stampSpacePos.y >= 0f && stampSpacePos.y <= 1f &&
|
||||
stampSpacePos.z >= -0.5f && stampSpacePos.z <= 0.5f;
|
||||
});
|
||||
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
|
||||
}
|
||||
|
||||
public void ClearGrasses()
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GCommon.OverlapTest(GroupId, GetQuad()));
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
ClearGrasses(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearGrasses(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
if (t.TerrainData.Foliage.Grasses == null)
|
||||
return;
|
||||
Vector3 terrainSize = new Vector3(
|
||||
t.TerrainData.Geometry.Width,
|
||||
t.TerrainData.Geometry.Height,
|
||||
t.TerrainData.Geometry.Length);
|
||||
Vector3 scale = new Vector3(
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.x, Scale.x),
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.y, Scale.y),
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.z, Scale.z));
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(
|
||||
t.WorldPointToNormalized(Position),
|
||||
Rotation,
|
||||
scale);
|
||||
Matrix4x4 normalizeToStamp = matrix.inverse;
|
||||
|
||||
GGrassPatch[] patches = t.TerrainData.Foliage.GrassPatches;
|
||||
for (int i = 0; i < patches.Length; ++i)
|
||||
{
|
||||
patches[i].RemoveInstances(grass =>
|
||||
{
|
||||
Vector3 stampSpacePos = normalizeToStamp.MultiplyPoint(grass.Position);
|
||||
return
|
||||
stampSpacePos.x >= -0.5f && stampSpacePos.x <= 0.5f &&
|
||||
stampSpacePos.y >= 0f && stampSpacePos.y <= 1f &&
|
||||
stampSpacePos.z >= -0.5f && stampSpacePos.z <= 0.5f;
|
||||
});
|
||||
}
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc2df825bbad26448bbc53968b0fb698
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,371 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
[ExecuteInEditMode]
|
||||
public class GGeometryStamper : MonoBehaviour
|
||||
{
|
||||
public enum GStampChannel
|
||||
{
|
||||
Elevation, Visibility
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool enableTerrainMask = true;
|
||||
public bool EnableTerrainMask
|
||||
{
|
||||
get
|
||||
{
|
||||
return enableTerrainMask;
|
||||
}
|
||||
set
|
||||
{
|
||||
enableTerrainMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int groupId;
|
||||
public int GroupId
|
||||
{
|
||||
get
|
||||
{
|
||||
return groupId;
|
||||
}
|
||||
set
|
||||
{
|
||||
groupId = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 position;
|
||||
public Vector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
position = value;
|
||||
transform.position = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Quaternion rotation;
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
rotation = value;
|
||||
transform.rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 scale;
|
||||
public Vector3 Scale
|
||||
{
|
||||
get
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
set
|
||||
{
|
||||
scale = value;
|
||||
transform.localScale = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D stamp;
|
||||
public Texture2D Stamp
|
||||
{
|
||||
get
|
||||
{
|
||||
return stamp;
|
||||
}
|
||||
set
|
||||
{
|
||||
stamp = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private GStampChannel channel;
|
||||
public GStampChannel Channel
|
||||
{
|
||||
get
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
set
|
||||
{
|
||||
channel = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve falloff;
|
||||
public AnimationCurve Falloff
|
||||
{
|
||||
get
|
||||
{
|
||||
return falloff;
|
||||
}
|
||||
set
|
||||
{
|
||||
falloff = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool inverseStamp;
|
||||
public bool InverseStamp
|
||||
{
|
||||
get
|
||||
{
|
||||
return inverseStamp;
|
||||
}
|
||||
set
|
||||
{
|
||||
inverseStamp = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool useFalloffAsBlendFactor;
|
||||
public bool UseFalloffAsBlendFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return useFalloffAsBlendFactor;
|
||||
}
|
||||
set
|
||||
{
|
||||
useFalloffAsBlendFactor = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private GStampOperation operation;
|
||||
public GStampOperation Operation
|
||||
{
|
||||
get
|
||||
{
|
||||
return operation;
|
||||
}
|
||||
set
|
||||
{
|
||||
operation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float lerpFactor;
|
||||
public float LerpFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return lerpFactor;
|
||||
}
|
||||
set
|
||||
{
|
||||
lerpFactor = Mathf.Clamp01(value);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int additionalMeshResolution;
|
||||
public int AdditionalMeshResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return additionalMeshResolution;
|
||||
}
|
||||
set
|
||||
{
|
||||
additionalMeshResolution = Mathf.Clamp(value, 0, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private Texture2D falloffTexture;
|
||||
|
||||
public Rect Rect
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3[] quad = new Vector3[4];
|
||||
GetQuad(quad);
|
||||
Rect r = GUtilities.GetRectContainsPoints(quad);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
rotation = Quaternion.identity;
|
||||
scale = Vector3.one * 100;
|
||||
stamp = null;
|
||||
falloff = AnimationCurve.EaseInOut(0, 1, 1, 0);
|
||||
inverseStamp = false;
|
||||
useFalloffAsBlendFactor = true;
|
||||
|
||||
operation = GStampOperation.Max;
|
||||
lerpFactor = 0.5f;
|
||||
additionalMeshResolution = 0;
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
Object.DestroyImmediate(falloffTexture);
|
||||
Internal_UpdateFalloffTexture();
|
||||
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GCommon.OverlapTest(GroupId, GetQuad()));
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
DrawOnTexture(t);
|
||||
}
|
||||
foreach(GStylizedTerrain t in terrains)
|
||||
{
|
||||
UpdateTerrain(t);
|
||||
}
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
t.MatchEdges();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawOnTexture(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
int heightMapResolution = t.TerrainData.Geometry.HeightMapResolution;
|
||||
RenderTexture rt = new RenderTexture(heightMapResolution, heightMapResolution, 0, GGeometry.HeightMapRTFormat, RenderTextureReadWrite.Linear);
|
||||
Internal_DrawOnTexture(t, rt);
|
||||
|
||||
Color[] oldHeightMapColors = t.TerrainData.Geometry.HeightMap.GetPixels();
|
||||
RenderTexture.active = rt;
|
||||
t.TerrainData.Geometry.HeightMap.ReadPixels(new Rect(0, 0, heightMapResolution, heightMapResolution), 0, 0);
|
||||
t.TerrainData.Geometry.HeightMap.Apply();
|
||||
RenderTexture.active = null;
|
||||
Color[] newHeightMapColors = t.TerrainData.Geometry.HeightMap.GetPixels();
|
||||
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
|
||||
List<Rect> dirtyRects = new List<Rect>(GCommon.CompareTerrainTexture(t.TerrainData.Geometry.ChunkGridSize, oldHeightMapColors, newHeightMapColors));
|
||||
for (int i = 0; i < dirtyRects.Count; ++i)
|
||||
{
|
||||
t.TerrainData.Geometry.SetRegionDirty(dirtyRects[i]);
|
||||
t.TerrainData.Foliage.SetTreeRegionDirty(dirtyRects[i]);
|
||||
t.TerrainData.Foliage.SetGrassRegionDirty(dirtyRects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTerrain(GStylizedTerrain t)
|
||||
{
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Geometry);
|
||||
t.UpdateTreesPosition();
|
||||
t.UpdateGrassPatches();
|
||||
t.TerrainData.Foliage.ClearTreeDirtyRegions();
|
||||
t.TerrainData.Foliage.ClearGrassDirtyRegions();
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
|
||||
}
|
||||
|
||||
public void Internal_DrawOnTexture(GStylizedTerrain t, RenderTexture rt)
|
||||
{
|
||||
GCommon.CopyToRT(t.TerrainData.Geometry.HeightMap, rt);
|
||||
|
||||
Vector3[] worldCorner = GetQuad();
|
||||
Vector2[] uvCorners = new Vector2[worldCorner.Length];
|
||||
for (int i = 0; i < uvCorners.Length; ++i)
|
||||
{
|
||||
uvCorners[i] = t.WorldPointToUV(worldCorner[i]);
|
||||
}
|
||||
|
||||
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);
|
||||
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
|
||||
return;
|
||||
|
||||
Vector3 normalizedPos = t.WorldPointToNormalized(Position);
|
||||
float stampHeight = GUtilities.InverseLerpUnclamped(0, t.TerrainData.Geometry.Height, Scale.y);
|
||||
|
||||
Material mat = GInternalMaterials.StamperMaterial;
|
||||
mat.SetTexture("_HeightMap", t.TerrainData.Geometry.HeightMap);
|
||||
mat.SetTexture("_Stamp", Stamp);
|
||||
mat.SetTexture("_Falloff", falloffTexture);
|
||||
mat.SetInt("_Operation", (int)Operation);
|
||||
mat.SetFloat("_LerpFactor", LerpFactor);
|
||||
mat.SetFloat("_StampHeight", stampHeight);
|
||||
mat.SetFloat("_StampPositionY", normalizedPos.y);
|
||||
mat.SetFloat("_Inverse", InverseStamp ? 1 : 0);
|
||||
mat.SetFloat("_UseFalloffAsBlendFactor", UseFalloffAsBlendFactor ? 1 : 0);
|
||||
mat.SetFloat("_AdditionalMeshResolution", GCommon.SUB_DIV_STEP * AdditionalMeshResolution);
|
||||
if (EnableTerrainMask)
|
||||
{
|
||||
mat.SetTexture("_TerrainMask", t.TerrainData.Mask.MaskMapOrDefault);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat.SetTexture("_TerrainMask", Texture2D.blackTexture);
|
||||
}
|
||||
|
||||
int pass = (int)Channel;
|
||||
GCommon.DrawQuad(rt, uvCorners, mat, pass);
|
||||
}
|
||||
|
||||
public void Internal_UpdateFalloffTexture()
|
||||
{
|
||||
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
|
||||
}
|
||||
|
||||
public Vector3[] GetQuad()
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
Vector3[] quad = new Vector3[4]
|
||||
{
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f))
|
||||
};
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
public void GetQuad(Vector3[] quad)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
quad[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
quad[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
quad[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
quad[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
}
|
||||
|
||||
public void GetBox(Vector3[] box)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
box[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
box[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
box[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
box[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
box[4] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, -0.5f));
|
||||
box[5] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, 0.5f));
|
||||
box[6] = matrix.MultiplyPoint(new Vector3(0.5f, 1, 0.5f));
|
||||
box[7] = matrix.MultiplyPoint(new Vector3(0.5f, 1, -0.5f));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fa49e4eea7d11044997fe999472ea32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,181 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
public class GObjectStampLayer : GConditionalStampLayer
|
||||
{
|
||||
[SerializeField]
|
||||
private Color visualizeColor;
|
||||
public Color VisualizeColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return visualizeColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
visualizeColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
public List<GameObject> prototypes;
|
||||
public List<GameObject> Prototypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (prototypes == null)
|
||||
{
|
||||
prototypes = new List<GameObject>();
|
||||
}
|
||||
return prototypes;
|
||||
}
|
||||
set
|
||||
{
|
||||
prototypes = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<int> prototypeIndices;
|
||||
public List<int> PrototypeIndices
|
||||
{
|
||||
get
|
||||
{
|
||||
if (prototypeIndices == null)
|
||||
{
|
||||
prototypeIndices = new List<int>();
|
||||
}
|
||||
return prototypeIndices;
|
||||
}
|
||||
set
|
||||
{
|
||||
prototypeIndices = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int instanceCount;
|
||||
public int InstanceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return instanceCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
instanceCount = Mathf.Max(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float minRotation;
|
||||
public float MinRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return minRotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
minRotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float maxRotation;
|
||||
public float MaxRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return maxRotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
maxRotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool alignToSurface;
|
||||
public bool AlignToSurface
|
||||
{
|
||||
get
|
||||
{
|
||||
return alignToSurface;
|
||||
}
|
||||
set
|
||||
{
|
||||
alignToSurface = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private LayerMask worldRaycastMask;
|
||||
public LayerMask WorldRaycastMask
|
||||
{
|
||||
get
|
||||
{
|
||||
return worldRaycastMask;
|
||||
}
|
||||
set
|
||||
{
|
||||
worldRaycastMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GObjectStampLayer() : base() { }
|
||||
|
||||
public static GObjectStampLayer Create()
|
||||
{
|
||||
GObjectStampLayer layer = new GObjectStampLayer();
|
||||
layer.Prototypes = null;
|
||||
layer.PrototypeIndices = null;
|
||||
layer.InstanceCount = 100;
|
||||
layer.AlignToSurface = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
layer.VisualizeColor = GEditorSettings.Instance.stampTools.visualizeColor;
|
||||
layer.MinRotation = GEditorSettings.Instance.stampTools.minRotation;
|
||||
layer.MaxRotation = GEditorSettings.Instance.stampTools.maxRotation;
|
||||
layer.MinScale = GEditorSettings.Instance.stampTools.minScale;
|
||||
layer.MaxScale = GEditorSettings.Instance.stampTools.maxScale;
|
||||
#endif
|
||||
layer.WorldRaycastMask = 1;
|
||||
layer.UpdateCurveTextures();
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e205da52ebccbc944b2eb9105efedb36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,514 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
[ExecuteInEditMode]
|
||||
public class GObjectStamper : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private bool enableTerrainMask;
|
||||
public bool EnableTerrainMask
|
||||
{
|
||||
get
|
||||
{
|
||||
return enableTerrainMask;
|
||||
}
|
||||
set
|
||||
{
|
||||
enableTerrainMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int groupId;
|
||||
public int GroupId
|
||||
{
|
||||
get
|
||||
{
|
||||
return groupId;
|
||||
}
|
||||
set
|
||||
{
|
||||
groupId = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 position;
|
||||
public Vector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
position = value;
|
||||
transform.position = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Quaternion rotation;
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
rotation = value;
|
||||
transform.rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 scale;
|
||||
public Vector3 Scale
|
||||
{
|
||||
get
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
set
|
||||
{
|
||||
scale = value;
|
||||
transform.localScale = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D mask;
|
||||
public Texture2D Mask
|
||||
{
|
||||
get
|
||||
{
|
||||
return mask;
|
||||
}
|
||||
set
|
||||
{
|
||||
mask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve falloff;
|
||||
public AnimationCurve Falloff
|
||||
{
|
||||
get
|
||||
{
|
||||
return falloff;
|
||||
}
|
||||
set
|
||||
{
|
||||
falloff = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<GObjectStampLayer> layers;
|
||||
public List<GObjectStampLayer> Layers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (layers == null)
|
||||
{
|
||||
layers = new List<GObjectStampLayer>();
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
set
|
||||
{
|
||||
layers = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int maskResolution;
|
||||
public int MaskResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return maskResolution;
|
||||
}
|
||||
set
|
||||
{
|
||||
maskResolution = Mathf.Clamp(Mathf.ClosestPowerOfTwo(value), GCommon.TEXTURE_SIZE_MIN, GCommon.TEXTURE_SIZE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect Rect
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3[] quad = new Vector3[4];
|
||||
GetQuad(quad);
|
||||
Rect r = GUtilities.GetRectContainsPoints(quad);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
private Texture2D falloffTexture;
|
||||
|
||||
private Vector3[] worldPoints = new Vector3[4];
|
||||
private Vector2[] uvPoints = new Vector2[4];
|
||||
|
||||
private Dictionary<string, RenderTexture> tempRt;
|
||||
private Dictionary<string, RenderTexture> TempRt
|
||||
{
|
||||
get
|
||||
{
|
||||
if (tempRt == null)
|
||||
{
|
||||
tempRt = new Dictionary<string, RenderTexture>();
|
||||
}
|
||||
return tempRt;
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
rotation = Quaternion.identity;
|
||||
scale = Vector3.one * 100;
|
||||
mask = null;
|
||||
falloff = AnimationCurve.EaseInOut(0, 1, 1, 0);
|
||||
maskResolution = 1024;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ReleaseResources();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ReleaseResources();
|
||||
}
|
||||
|
||||
private void ReleaseResources()
|
||||
{
|
||||
foreach (RenderTexture rt in TempRt.Values)
|
||||
{
|
||||
if (rt != null)
|
||||
{
|
||||
rt.Release();
|
||||
GUtilities.DestroyObject(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
Object.DestroyImmediate(falloffTexture);
|
||||
Internal_UpdateFalloffTexture();
|
||||
Internal_UpdateLayerTransitionTextures();
|
||||
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GCommon.OverlapTest(GroupId, GetQuad()));
|
||||
|
||||
try
|
||||
{
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
Apply(t);
|
||||
}
|
||||
}
|
||||
catch (GProgressCancelledException)
|
||||
{
|
||||
Debug.Log("Stamp process canceled, result may be incorrect. Use History to clean up!");
|
||||
#if UNITY_EDITOR
|
||||
GCommonGUI.ClearProgressBar();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
if (Layers.Count == 0)
|
||||
return;
|
||||
|
||||
GetQuad(worldPoints);
|
||||
GetUvPoints(t, worldPoints, uvPoints);
|
||||
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvPoints);
|
||||
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
|
||||
return;
|
||||
|
||||
RenderTexture[] brushes = new RenderTexture[Layers.Count];
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
brushes[i] = GetRenderTexture("brush" + i.ToString());
|
||||
}
|
||||
Internal_RenderBrushes(brushes, t, uvPoints);
|
||||
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
StampLayer(t, brushes[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
private void StampLayer(GStylizedTerrain t, RenderTexture brush, int layerIndex)
|
||||
{
|
||||
GObjectStampLayer layer = Layers[layerIndex];
|
||||
if (layer.Ignore)
|
||||
return;
|
||||
if (layer.InstanceCount == 0)
|
||||
return;
|
||||
if (layer.Prototypes.Count == 0 ||
|
||||
layer.PrototypeIndices.Count == 0)
|
||||
return;
|
||||
|
||||
Texture2D layerMask = GCommon.CreateTexture(MaskResolution, Color.clear);
|
||||
GCommon.CopyFromRT(layerMask, brush);
|
||||
SpawnObjectOnTerrain(t, layerMask, layerIndex);
|
||||
GUtilities.DestroyObject(layerMask);
|
||||
}
|
||||
|
||||
private void SpawnObjectOnTerrain(GStylizedTerrain t, Texture2D layerMask, int layerIndex)
|
||||
{
|
||||
GObjectStampLayer layer = Layers[layerIndex];
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string title = "Stamping on " + t.name;
|
||||
string text = string.Format("Layer: {0}", !string.IsNullOrEmpty(layer.Name) ? layer.Name : layerIndex.ToString());
|
||||
GCommonGUI.CancelableProgressBar(title, text, 1);
|
||||
#endif
|
||||
|
||||
int sampleCount = layer.InstanceCount;
|
||||
NativeArray<bool> cullResult = new NativeArray<bool>(sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<GPrototypeInstanceInfo> instanceInfo = new NativeArray<GPrototypeInstanceInfo>(sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<int> selectedPrototypeIndices = new NativeArray<int>(layer.PrototypeIndices.ToArray(), Allocator.TempJob);
|
||||
GTextureNativeDataDescriptor<Color32> maskHandle = new GTextureNativeDataDescriptor<Color32>(layerMask);
|
||||
|
||||
{
|
||||
GSampleInstanceJob job = new GSampleInstanceJob()
|
||||
{
|
||||
cullResult = cullResult,
|
||||
instanceInfo = instanceInfo,
|
||||
mask = maskHandle,
|
||||
selectedPrototypeIndices = selectedPrototypeIndices,
|
||||
|
||||
minRotation = layer.MinRotation,
|
||||
maxRotation = layer.MaxRotation,
|
||||
|
||||
minScale = layer.MinScale,
|
||||
maxScale = layer.MaxScale,
|
||||
|
||||
seed = Random.Range(int.MinValue, int.MaxValue)
|
||||
};
|
||||
|
||||
JobHandle jHandle = job.Schedule(sampleCount, 100);
|
||||
jHandle.Complete();
|
||||
}
|
||||
|
||||
Vector3 terrainSize = t.TerrainData.Geometry.Size;
|
||||
NativeArray<RaycastCommand> raycastCommands = new NativeArray<RaycastCommand>(instanceInfo.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
NativeArray<RaycastHit> hits = new NativeArray<RaycastHit>(instanceInfo.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
for (int i = 0; i < raycastCommands.Length; ++i)
|
||||
{
|
||||
GPrototypeInstanceInfo info = instanceInfo[i];
|
||||
Vector3 from = new Vector3(t.transform.position.x + info.position.x * terrainSize.x, 10000, t.transform.position.z + info.position.z * terrainSize.z);
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
QueryParameters q = new QueryParameters(layer.WorldRaycastMask, false, QueryTriggerInteraction.Ignore, false);
|
||||
RaycastCommand cmd = new RaycastCommand(from, Vector3.down, q, float.MaxValue);
|
||||
#else
|
||||
RaycastCommand cmd = new RaycastCommand(from, Vector3.down, float.MaxValue, layer.WorldRaycastMask, 1);
|
||||
#endif
|
||||
raycastCommands[i] = cmd;
|
||||
}
|
||||
{
|
||||
JobHandle jHandle = RaycastCommand.ScheduleBatch(raycastCommands, hits, 10);
|
||||
jHandle.Complete();
|
||||
}
|
||||
|
||||
for (int i = 0; i < instanceInfo.Length; ++i)
|
||||
{
|
||||
GPrototypeInstanceInfo info = instanceInfo[i];
|
||||
RaycastHit h = hits[i];
|
||||
info.position.Set(info.position.x * terrainSize.x, h.collider != null ? h.point.y : 0, info.position.z * terrainSize.z);
|
||||
instanceInfo[i] = info;
|
||||
}
|
||||
|
||||
int count = instanceInfo.Length;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
GPrototypeInstanceInfo info = instanceInfo[i];
|
||||
if (cullResult[i] == false)
|
||||
continue;
|
||||
if (info.prototypeIndex < 0 || info.prototypeIndex >= layer.Prototypes.Count)
|
||||
continue;
|
||||
|
||||
GameObject prototype = layer.Prototypes[info.prototypeIndex];
|
||||
GameObject go = GSpawner.Spawn(t, prototype, Vector3.zero);
|
||||
go.transform.position = t.transform.position + info.position;
|
||||
go.transform.rotation = info.rotation;
|
||||
go.transform.localScale = info.scale;
|
||||
|
||||
if (layer.AlignToSurface)
|
||||
{
|
||||
RaycastHit hit = hits[i];
|
||||
if (hit.collider != null)
|
||||
{
|
||||
Quaternion q = Quaternion.FromToRotation(go.transform.up, hit.normal);
|
||||
go.transform.rotation *= q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raycastCommands.Dispose();
|
||||
hits.Dispose();
|
||||
|
||||
cullResult.Dispose();
|
||||
instanceInfo.Dispose();
|
||||
selectedPrototypeIndices.Dispose();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
GCommonGUI.ClearProgressBar();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Internal_RenderBrushes(RenderTexture[] brushes, GStylizedTerrain t, Vector2[] uvPoints)
|
||||
{
|
||||
for (int i = 0; i < brushes.Length; ++i)
|
||||
{
|
||||
GStampLayerMaskRenderer.Render(
|
||||
brushes[i],
|
||||
Layers[i],
|
||||
t,
|
||||
Matrix4x4.TRS(Position, Rotation, Scale),
|
||||
Mask,
|
||||
falloffTexture,
|
||||
uvPoints,
|
||||
EnableTerrainMask);
|
||||
}
|
||||
}
|
||||
|
||||
public void Internal_UpdateFalloffTexture()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
GUtilities.DestroyObject(falloffTexture);
|
||||
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
|
||||
}
|
||||
|
||||
public void Internal_UpdateLayerTransitionTextures()
|
||||
{
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
Layers[i].UpdateCurveTextures();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3[] GetQuad()
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
Vector3[] quad = new Vector3[4]
|
||||
{
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f))
|
||||
};
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
public void GetQuad(Vector3[] quad)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
quad[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
quad[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
quad[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
quad[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
}
|
||||
|
||||
private void GetUvPoints(GStylizedTerrain t, Vector3[] worldPoint, Vector2[] uvPoint)
|
||||
{
|
||||
for (int i = 0; i < uvPoints.Length; ++i)
|
||||
{
|
||||
uvPoints[i] = t.WorldPointToUV(worldPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetBox(Vector3[] box)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
box[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
box[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
box[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
box[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
box[4] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, -0.5f));
|
||||
box[5] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, 0.5f));
|
||||
box[6] = matrix.MultiplyPoint(new Vector3(0.5f, 1, 0.5f));
|
||||
box[7] = matrix.MultiplyPoint(new Vector3(0.5f, 1, -0.5f));
|
||||
}
|
||||
|
||||
private RenderTexture GetRenderTexture(string key)
|
||||
{
|
||||
int resolution = MaskResolution;
|
||||
if (!TempRt.ContainsKey(key) ||
|
||||
TempRt[key] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
TempRt[key] = rt;
|
||||
}
|
||||
else if (TempRt[key].width != resolution || TempRt[key].height != resolution)
|
||||
{
|
||||
TempRt[key].Release();
|
||||
Object.DestroyImmediate(TempRt[key]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
TempRt[key] = rt;
|
||||
}
|
||||
|
||||
return TempRt[key];
|
||||
}
|
||||
|
||||
public void ClearObjects()
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(GCommon.OverlapTest(GroupId, GetQuad()));
|
||||
foreach (GStylizedTerrain t in terrains)
|
||||
{
|
||||
ClearObjects(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearObjects(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
Vector3 terrainSize = new Vector3(
|
||||
t.TerrainData.Geometry.Width,
|
||||
t.TerrainData.Geometry.Height,
|
||||
t.TerrainData.Geometry.Length);
|
||||
Vector3 scale = new Vector3(
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.x, Scale.x),
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.y, Scale.y),
|
||||
GUtilities.InverseLerpUnclamped(0, terrainSize.z, Scale.z));
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(
|
||||
t.WorldPointToNormalized(Position),
|
||||
Rotation,
|
||||
scale);
|
||||
Matrix4x4 normalizeToStamp = matrix.inverse;
|
||||
|
||||
GSpawner.DestroyIf(t, (g) =>
|
||||
{
|
||||
Vector3 normalizePos = t.WorldPointToNormalized(g.transform.position);
|
||||
Vector3 stampSpacePos = normalizeToStamp.MultiplyPoint(normalizePos);
|
||||
return
|
||||
stampSpacePos.x >= -0.5f && stampSpacePos.x <= 0.5f &&
|
||||
stampSpacePos.y >= 0f && stampSpacePos.y <= 1f &&
|
||||
stampSpacePos.z >= -0.5f && stampSpacePos.z <= 0.5f;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 059eb2e48433fcb4d9e7de033a744202
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,108 @@
|
||||
#if GRIFFIN
|
||||
using Pinwheel.Griffin.TextureTool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
public static class GStampLayerMaskRenderer
|
||||
{
|
||||
private const string BLEND_HEIGHT_KW = "BLEND_HEIGHT";
|
||||
private const string BLEND_SLOPE_KW = "BLEND_SLOPE";
|
||||
private const string BLEND_NOISE_KW = "BLEND_NOISE";
|
||||
|
||||
public static void Render(
|
||||
RenderTexture rt,
|
||||
GConditionalStampLayer layer,
|
||||
GStylizedTerrain terrain,
|
||||
Matrix4x4 stamperTransform,
|
||||
Texture mask,
|
||||
Texture falloffTexture,
|
||||
Vector2[] uvPoints,
|
||||
bool useTerrainMask)
|
||||
{
|
||||
GCommon.ClearRT(rt);
|
||||
if (layer.Ignore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Material brushMat = Object.Instantiate(GInternalMaterials.TextureStamperBrushMaterial);
|
||||
|
||||
int resolution = Mathf.Max(rt.width, rt.height);
|
||||
//no need to release these maps
|
||||
RenderTexture heightMap = terrain.GetHeightMap(resolution);
|
||||
RenderTexture normalMap =
|
||||
layer.NormalMapMode == GNormalMapMode.Sharp ? terrain.GetSharpNormalMap(resolution) :
|
||||
layer.NormalMapMode == GNormalMapMode.Interpolated ? terrain.GetInterpolatedNormalMap(resolution) :
|
||||
layer.NormalMapMode == GNormalMapMode.PerPixel ? terrain.GetPerPixelNormalMap(resolution) : null;
|
||||
|
||||
brushMat.SetTexture("_HeightMap", heightMap);
|
||||
|
||||
Vector3 position = stamperTransform.MultiplyPoint(Vector3.zero);
|
||||
Vector3 scale = stamperTransform.lossyScale;
|
||||
|
||||
float stamperMinHeight = terrain.WorldPointToNormalized(position).y;
|
||||
float stamperMaxHeight = terrain.WorldPointToNormalized(position + Vector3.up * scale.y).y;
|
||||
brushMat.SetFloat("_StamperMinHeight", stamperMinHeight);
|
||||
brushMat.SetFloat("_StamperMaxHeight", stamperMaxHeight);
|
||||
|
||||
Vector3 terrainSize = new Vector3(
|
||||
terrain.TerrainData.Geometry.Width,
|
||||
terrain.TerrainData.Geometry.Height,
|
||||
terrain.TerrainData.Geometry.Length);
|
||||
|
||||
brushMat.SetTexture("_Mask", mask);
|
||||
brushMat.SetTexture("_Falloff", falloffTexture);
|
||||
brushMat.SetFloat("_MinHeight", GUtilities.InverseLerpUnclamped(0, terrainSize.y, layer.MinHeight));
|
||||
brushMat.SetFloat("_MaxHeight", GUtilities.InverseLerpUnclamped(0, terrainSize.y, layer.MaxHeight));
|
||||
brushMat.SetTexture("_HeightTransition", layer.heightTransitionTexture);
|
||||
brushMat.SetFloat("_MinSlope", layer.MinSlope * Mathf.Deg2Rad);
|
||||
brushMat.SetFloat("_MaxSlope", layer.MaxSlope * Mathf.Deg2Rad);
|
||||
brushMat.SetTexture("_SlopeTransition", layer.slopeTransitionTexture);
|
||||
brushMat.SetVector("_NoiseOrigin", layer.NoiseOrigin);
|
||||
brushMat.SetFloat("_NoiseFrequency", layer.NoiseFrequency);
|
||||
brushMat.SetInt("_NoiseOctaves", layer.NoiseOctaves);
|
||||
brushMat.SetFloat("_NoiseLacunarity", layer.NoiseLacunarity);
|
||||
brushMat.SetFloat("_NoisePersistence", layer.NoisePersistence);
|
||||
brushMat.SetTexture("_NoiseRemap", layer.noiseRemapTexture);
|
||||
brushMat.SetTexture("_NormalMap", normalMap);
|
||||
if (useTerrainMask)
|
||||
{
|
||||
brushMat.SetTexture("_TerrainMask", terrain.TerrainData.Mask.MaskMapOrDefault);
|
||||
}
|
||||
else
|
||||
{
|
||||
brushMat.SetTexture("_TerrainMask", Texture2D.blackTexture);
|
||||
}
|
||||
|
||||
GCommon.SetMaterialKeywordActive(brushMat, BLEND_HEIGHT_KW, layer.BlendHeight);
|
||||
GCommon.SetMaterialKeywordActive(brushMat, BLEND_SLOPE_KW, layer.BlendSlope);
|
||||
GCommon.SetMaterialKeywordActive(brushMat, BLEND_NOISE_KW, layer.BlendNoise);
|
||||
DrawOnBrushTexture(rt, uvPoints, brushMat, 0);
|
||||
}
|
||||
|
||||
private static void DrawOnBrushTexture(RenderTexture rt, Vector2[] quadCorners, Material mat, int pass)
|
||||
{
|
||||
RenderTexture.active = rt;
|
||||
GL.PushMatrix();
|
||||
mat.SetPass(pass);
|
||||
GL.LoadOrtho();
|
||||
GL.Begin(GL.QUADS);
|
||||
GL.MultiTexCoord(0, new Vector3(0, 0, 0));
|
||||
GL.Vertex3(quadCorners[0].x, quadCorners[0].y, 0);
|
||||
|
||||
GL.MultiTexCoord(0, new Vector3(0, 1, 0));
|
||||
GL.Vertex3(quadCorners[1].x, quadCorners[1].y, 0);
|
||||
|
||||
GL.MultiTexCoord(0, new Vector3(1, 1, 0));
|
||||
GL.Vertex3(quadCorners[2].x, quadCorners[2].y, 0);
|
||||
|
||||
GL.MultiTexCoord(0, new Vector3(1, 0, 0));
|
||||
GL.Vertex3(quadCorners[3].x, quadCorners[3].y, 0);
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
RenderTexture.active = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca6a4de51cb9ceb4dae58882a8f8574d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
#if GRIFFIN
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
public enum GStampOperation
|
||||
{
|
||||
Add, Subtract, ReverseSubtract, Max, Min, Lerp, Different
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15bb6698698c1264bb6b83f45fdf0812
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,16 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
public static class GStampToolUtilities
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ed08dce1f8e1a247b852b231e1f38a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
#if GRIFFIN
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
public enum GTextureStampChannel
|
||||
{
|
||||
AlbedoMetallicSmoothness, Splat
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0eee7cc1ac3c7a4429b9bfb9f54ba970
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,80 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
public class GTextureStampLayer : GConditionalStampLayer
|
||||
{
|
||||
[SerializeField]
|
||||
private Color color;
|
||||
public Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
return color;
|
||||
}
|
||||
set
|
||||
{
|
||||
color = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float metallic;
|
||||
public float Metallic
|
||||
{
|
||||
get
|
||||
{
|
||||
return metallic;
|
||||
}
|
||||
set
|
||||
{
|
||||
metallic = Mathf.Clamp01(value);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float smoothness;
|
||||
public float Smoothness
|
||||
{
|
||||
get
|
||||
{
|
||||
return smoothness;
|
||||
}
|
||||
set
|
||||
{
|
||||
smoothness = Mathf.Clamp01(value);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int splatIndex;
|
||||
public int SplatIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return splatIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
splatIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GTextureStampLayer() : base() { }
|
||||
|
||||
public static GTextureStampLayer Create()
|
||||
{
|
||||
GTextureStampLayer layer = new GTextureStampLayer();
|
||||
layer.Color = Color.white;
|
||||
layer.Metallic = 0;
|
||||
layer.Smoothness = 0;
|
||||
layer.SplatIndex = 0;
|
||||
layer.UpdateCurveTextures();
|
||||
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 733a7f8c1e0121948ad9c423e02276dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,605 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.StampTool
|
||||
{
|
||||
[System.Serializable]
|
||||
[ExecuteInEditMode]
|
||||
public class GTextureStamper : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
private bool editor_ShowLivePreview = true;
|
||||
public bool Editor_ShowLivePreview
|
||||
{
|
||||
get
|
||||
{
|
||||
return editor_ShowLivePreview;
|
||||
}
|
||||
set
|
||||
{
|
||||
editor_ShowLivePreview = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool editor_ShowBounds = true;
|
||||
public bool Editor_ShowBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
return editor_ShowBounds;
|
||||
}
|
||||
set
|
||||
{
|
||||
editor_ShowBounds = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[SerializeField]
|
||||
private bool enableTerrainMask;
|
||||
public bool EnableTerrainMask
|
||||
{
|
||||
get
|
||||
{
|
||||
return enableTerrainMask;
|
||||
}
|
||||
set
|
||||
{
|
||||
enableTerrainMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int groupId;
|
||||
public int GroupId
|
||||
{
|
||||
get
|
||||
{
|
||||
return groupId;
|
||||
}
|
||||
set
|
||||
{
|
||||
groupId = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 position;
|
||||
public Vector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
position = value;
|
||||
transform.position = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Quaternion rotation = Quaternion.identity;
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
rotation = value;
|
||||
transform.rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 scale;
|
||||
public Vector3 Scale
|
||||
{
|
||||
get
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
set
|
||||
{
|
||||
scale = value;
|
||||
transform.localScale = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D mask;
|
||||
public Texture2D Mask
|
||||
{
|
||||
get
|
||||
{
|
||||
return mask;
|
||||
}
|
||||
set
|
||||
{
|
||||
mask = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve falloff;
|
||||
public AnimationCurve Falloff
|
||||
{
|
||||
get
|
||||
{
|
||||
return falloff;
|
||||
}
|
||||
set
|
||||
{
|
||||
falloff = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private GTextureStampChannel channel;
|
||||
public GTextureStampChannel Channel
|
||||
{
|
||||
get
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
set
|
||||
{
|
||||
channel = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<GTextureStampLayer> layers;
|
||||
public List<GTextureStampLayer> Layers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (layers == null)
|
||||
{
|
||||
layers = new List<GTextureStampLayer>();
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
set
|
||||
{
|
||||
layers = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Rect Rect
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3[] quad = new Vector3[4];
|
||||
GetQuad(quad);
|
||||
Rect r = GUtilities.GetRectContainsPoints(quad);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
private Texture2D falloffTexture;
|
||||
|
||||
private Vector3[] worldPoints = new Vector3[4];
|
||||
private Vector2[] uvPoints = new Vector2[4];
|
||||
|
||||
private Dictionary<string, RenderTexture> tempRt;
|
||||
private Dictionary<string, RenderTexture> TempRt
|
||||
{
|
||||
get
|
||||
{
|
||||
if (tempRt == null)
|
||||
{
|
||||
tempRt = new Dictionary<string, RenderTexture>();
|
||||
}
|
||||
return tempRt;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material albedoPainterMaterial;
|
||||
private static Material AlbedoPainterMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (albedoPainterMaterial == null)
|
||||
{
|
||||
albedoPainterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.albedoPainterShader);
|
||||
}
|
||||
return albedoPainterMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material metallicPainterMaterial;
|
||||
private static Material MetallicPainterMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (metallicPainterMaterial == null)
|
||||
{
|
||||
metallicPainterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.metallicPainterShader);
|
||||
}
|
||||
return metallicPainterMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material smoothnessPainterMaterial;
|
||||
private static Material SmoothnessPainterMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (smoothnessPainterMaterial == null)
|
||||
{
|
||||
smoothnessPainterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.smoothnessPainterShader);
|
||||
}
|
||||
return smoothnessPainterMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material splatPainterMaterial;
|
||||
private static Material SplatPainterMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (splatPainterMaterial == null)
|
||||
{
|
||||
splatPainterMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splatPainterShader);
|
||||
}
|
||||
return splatPainterMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
rotation = Quaternion.identity;
|
||||
scale = Vector3.one * 100;
|
||||
mask = null;
|
||||
falloff = AnimationCurve.EaseInOut(0, 1, 1, 0);
|
||||
channel = GTextureStampChannel.AlbedoMetallicSmoothness;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ReleaseResources();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ReleaseResources();
|
||||
}
|
||||
|
||||
private void ReleaseResources()
|
||||
{
|
||||
foreach (RenderTexture rt in TempRt.Values)
|
||||
{
|
||||
if (rt != null)
|
||||
{
|
||||
rt.Release();
|
||||
GUtilities.DestroyObject(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
Object.DestroyImmediate(falloffTexture);
|
||||
Internal_UpdateFalloffTexture();
|
||||
Internal_UpdateLayerTransitionTextures();
|
||||
IEnumerator<GStylizedTerrain> terrains = GStylizedTerrain.ActiveTerrains.GetEnumerator();
|
||||
while (terrains.MoveNext())
|
||||
{
|
||||
GStylizedTerrain t = terrains.Current;
|
||||
if (groupId < 0 ||
|
||||
(groupId >= 0 && groupId == t.GroupId))
|
||||
{
|
||||
Apply(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply(GStylizedTerrain t)
|
||||
{
|
||||
if (t.TerrainData == null)
|
||||
return;
|
||||
if (Layers.Count == 0)
|
||||
return;
|
||||
if (Channel == GTextureStampChannel.AlbedoMetallicSmoothness)
|
||||
{
|
||||
ApplyAlbedoMetallicSmoothness(t);
|
||||
}
|
||||
else if (Channel == GTextureStampChannel.Splat)
|
||||
{
|
||||
ApplySplat(t);
|
||||
}
|
||||
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
|
||||
}
|
||||
|
||||
private void ApplySplat(GStylizedTerrain t)
|
||||
{
|
||||
int splatControlResolution = t.TerrainData.Shading.SplatControlResolution;
|
||||
int controlMapCount = t.TerrainData.Shading.SplatControlMapCount;
|
||||
RenderTexture[] rtControls = new RenderTexture[controlMapCount];
|
||||
for (int i = 0; i < controlMapCount; ++i)
|
||||
{
|
||||
rtControls[i] = new RenderTexture(splatControlResolution, splatControlResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
}
|
||||
|
||||
bool succeed = Internal_ApplySplat(t, rtControls);
|
||||
if (!succeed)
|
||||
{
|
||||
for (int i = 0; i < controlMapCount; ++i)
|
||||
{
|
||||
rtControls[i].Release();
|
||||
Object.DestroyImmediate(rtControls[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < controlMapCount; ++i)
|
||||
{
|
||||
Texture2D splatControl = t.TerrainData.Shading.GetSplatControl(i);
|
||||
RenderTexture.active = rtControls[i];
|
||||
splatControl.ReadPixels(new Rect(0, 0, splatControlResolution, splatControlResolution), 0, 0);
|
||||
splatControl.Apply();
|
||||
RenderTexture.active = null;
|
||||
|
||||
rtControls[i].Release();
|
||||
Object.DestroyImmediate(rtControls[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private RenderTexture[] RenderBrushes(GStylizedTerrain t, Vector2[] uvPoints, int brushResolution)
|
||||
{
|
||||
RenderTexture[] brushes = new RenderTexture[Layers.Count];
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
brushes[i] = GetRenderTexture("brush" + i.ToString(), brushResolution);
|
||||
GStampLayerMaskRenderer.Render(
|
||||
brushes[i],
|
||||
Layers[i],
|
||||
t,
|
||||
Matrix4x4.TRS(Position, Rotation, Scale),
|
||||
Mask,
|
||||
falloffTexture,
|
||||
uvPoints,
|
||||
EnableTerrainMask);
|
||||
}
|
||||
|
||||
return brushes;
|
||||
}
|
||||
|
||||
public bool Internal_ApplySplat(GStylizedTerrain t, RenderTexture[] rtControls)
|
||||
{
|
||||
GetQuad(worldPoints);
|
||||
GetUvPoints(t, worldPoints, uvPoints);
|
||||
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvPoints);
|
||||
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
|
||||
return false;
|
||||
|
||||
int brushResolution = t.TerrainData.Shading.SplatControlResolution;
|
||||
RenderTexture[] brushes = RenderBrushes(t, uvPoints, brushResolution);
|
||||
|
||||
RenderTexture[] bg = new RenderTexture[rtControls.Length];
|
||||
for (int i = 0; i < bg.Length; ++i)
|
||||
{
|
||||
bg[i] = GetRenderTexture("bg" + i.ToString(), brushResolution);
|
||||
Texture2D splatControl = t.TerrainData.Shading.GetSplatControlOrDefault(i);
|
||||
GCommon.CopyToRT(splatControl, bg[i]);
|
||||
GCommon.CopyToRT(splatControl, rtControls[i]);
|
||||
}
|
||||
|
||||
Material paintMaterial = SplatPainterMaterial;
|
||||
paintMaterial.SetFloat("_Opacity", 1);
|
||||
for (int i = 0; i < brushes.Length; ++i)
|
||||
{
|
||||
if (Layers[i].Ignore)
|
||||
continue;
|
||||
paintMaterial.SetTexture("_Mask", brushes[i]);
|
||||
int controlMapCount = rtControls.Length;
|
||||
for (int controlIndex = 0; controlIndex < controlMapCount; ++controlIndex)
|
||||
{
|
||||
GCommon.CopyToRT(bg[controlIndex], rtControls[controlIndex]);
|
||||
paintMaterial.SetTexture("_MainTex", bg[controlIndex]);
|
||||
if (Layers[i].SplatIndex / 4 == controlIndex)
|
||||
{
|
||||
paintMaterial.SetInt("_ChannelIndex", Layers[i].SplatIndex % 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
paintMaterial.SetInt("_ChannelIndex", -1);
|
||||
}
|
||||
int pass = 0;
|
||||
GCommon.DrawQuad(rtControls[controlIndex], GCommon.FullRectUvPoints, paintMaterial, pass);
|
||||
GCommon.CopyToRT(rtControls[controlIndex], bg[controlIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplyAlbedoMetallicSmoothness(GStylizedTerrain t)
|
||||
{
|
||||
int albedoMapResolution = t.TerrainData.Shading.AlbedoMapResolution;
|
||||
RenderTexture albedoRt = new RenderTexture(albedoMapResolution, albedoMapResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
|
||||
int metallicMapResolution = t.TerrainData.Shading.MetallicMapResolution;
|
||||
RenderTexture metallicRt = new RenderTexture(metallicMapResolution, metallicMapResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
|
||||
bool succeed = Internal_ApplyAlbedoMetallicSmoothness(t, albedoRt, metallicRt);
|
||||
if (!succeed)
|
||||
return;
|
||||
|
||||
RenderTexture.active = albedoRt;
|
||||
t.TerrainData.Shading.AlbedoMap.ReadPixels(new Rect(0, 0, albedoMapResolution, albedoMapResolution), 0, 0);
|
||||
t.TerrainData.Shading.AlbedoMap.Apply();
|
||||
RenderTexture.active = null;
|
||||
|
||||
RenderTexture.active = metallicRt;
|
||||
t.TerrainData.Shading.MetallicMap.ReadPixels(new Rect(0, 0, metallicMapResolution, metallicMapResolution), 0, 0);
|
||||
t.TerrainData.Shading.MetallicMap.Apply();
|
||||
RenderTexture.active = null;
|
||||
|
||||
albedoRt.Release();
|
||||
GUtilities.DestroyObject(albedoRt);
|
||||
|
||||
metallicRt.Release();
|
||||
GUtilities.DestroyObject(metallicRt);
|
||||
}
|
||||
|
||||
public bool Internal_ApplyAlbedoMetallicSmoothness(GStylizedTerrain t, RenderTexture albedoRt, RenderTexture metallicRt)
|
||||
{
|
||||
GetQuad(worldPoints);
|
||||
GetUvPoints(t, worldPoints, uvPoints);
|
||||
Rect dirtyRect = GUtilities.GetRectContainsPoints(uvPoints);
|
||||
if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
|
||||
return false;
|
||||
|
||||
int brushResolution = Mathf.Max(t.TerrainData.Shading.AlbedoMapResolution, t.TerrainData.Shading.MetallicMapResolution);
|
||||
RenderTexture[] brushes = RenderBrushes(t, uvPoints, brushResolution);
|
||||
|
||||
RenderTexture bg0 = GetRenderTexture("bg0", brushResolution);
|
||||
Texture2D albedoMap = t.TerrainData.Shading.AlbedoMapOrDefault;
|
||||
GCommon.CopyToRT(albedoMap, bg0);
|
||||
GCommon.CopyToRT(albedoMap, albedoRt);
|
||||
|
||||
Material mat = AlbedoPainterMaterial;
|
||||
mat.SetFloat("_Opacity", 1);
|
||||
for (int i = 0; i < brushes.Length; ++i)
|
||||
{
|
||||
if (Layers[i].Ignore)
|
||||
continue;
|
||||
mat.SetTexture("_MainTex", bg0);
|
||||
mat.SetTexture("_Mask", brushes[i]);
|
||||
mat.SetColor("_Color", Layers[i].Color);
|
||||
int pass = 0;
|
||||
GCommon.DrawQuad(albedoRt, GCommon.FullRectUvPoints, mat, pass);
|
||||
GCommon.CopyToRT(albedoRt, bg0);
|
||||
}
|
||||
|
||||
RenderTexture bg1 = GetRenderTexture("bg1", brushResolution);
|
||||
Texture2D metallicMap = t.TerrainData.Shading.MetallicMapOrDefault;
|
||||
GCommon.CopyToRT(metallicMap, bg1);
|
||||
GCommon.CopyToRT(metallicMap, metallicRt);
|
||||
|
||||
mat = MetallicPainterMaterial;
|
||||
mat.SetFloat("_Opacity", 1);
|
||||
for (int i = 0; i < brushes.Length; ++i)
|
||||
{
|
||||
if (Layers[i].Ignore)
|
||||
continue;
|
||||
mat.SetTexture("_MainTex", bg1);
|
||||
mat.SetTexture("_Mask", brushes[i]);
|
||||
mat.SetColor("_Color", Color.white * Layers[i].Metallic);
|
||||
int pass = 3; //fragSet
|
||||
GCommon.DrawQuad(metallicRt, GCommon.FullRectUvPoints, mat, pass);
|
||||
GCommon.CopyToRT(metallicRt, bg1);
|
||||
}
|
||||
|
||||
mat = SmoothnessPainterMaterial;
|
||||
mat.SetFloat("_Opacity", 1);
|
||||
for (int i = 0; i < brushes.Length; ++i)
|
||||
{
|
||||
if (Layers[i].Ignore)
|
||||
continue;
|
||||
mat.SetTexture("_MainTex", bg1);
|
||||
mat.SetTexture("_Mask", brushes[i]);
|
||||
mat.SetColor("_Color", Color.white * Layers[i].Smoothness);
|
||||
int pass = 3; //fragSet
|
||||
GCommon.DrawQuad(metallicRt, GCommon.FullRectUvPoints, mat, pass);
|
||||
GCommon.CopyToRT(metallicRt, bg1);
|
||||
}
|
||||
|
||||
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Internal_UpdateFalloffTexture()
|
||||
{
|
||||
if (falloffTexture != null)
|
||||
GUtilities.DestroyObject(falloffTexture);
|
||||
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
|
||||
}
|
||||
|
||||
public void Internal_UpdateLayerTransitionTextures()
|
||||
{
|
||||
for (int i = 0; i < Layers.Count; ++i)
|
||||
{
|
||||
Layers[i].UpdateCurveTextures();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3[] GetQuad()
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
Vector3[] quad = new Vector3[4]
|
||||
{
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f)),
|
||||
matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f))
|
||||
};
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
public void GetQuad(Vector3[] quad)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
quad[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
quad[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
quad[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
quad[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
}
|
||||
|
||||
private void GetUvPoints(GStylizedTerrain t, Vector3[] worldPoint, Vector2[] uvPoint)
|
||||
{
|
||||
for (int i = 0; i < uvPoints.Length; ++i)
|
||||
{
|
||||
uvPoints[i] = t.WorldPointToUV(worldPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetBox(Vector3[] box)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(Position, Rotation, Scale);
|
||||
box[0] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f));
|
||||
box[1] = matrix.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f));
|
||||
box[2] = matrix.MultiplyPoint(new Vector3(0.5f, 0, 0.5f));
|
||||
box[3] = matrix.MultiplyPoint(new Vector3(0.5f, 0, -0.5f));
|
||||
box[4] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, -0.5f));
|
||||
box[5] = matrix.MultiplyPoint(new Vector3(-0.5f, 1, 0.5f));
|
||||
box[6] = matrix.MultiplyPoint(new Vector3(0.5f, 1, 0.5f));
|
||||
box[7] = matrix.MultiplyPoint(new Vector3(0.5f, 1, -0.5f));
|
||||
}
|
||||
|
||||
private RenderTexture GetRenderTexture(string key, int resolution)
|
||||
{
|
||||
if (!TempRt.ContainsKey(key) ||
|
||||
TempRt[key] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
TempRt[key] = rt;
|
||||
}
|
||||
else if (TempRt[key].width != resolution || TempRt[key].height != resolution)
|
||||
{
|
||||
TempRt[key].Release();
|
||||
Object.DestroyImmediate(TempRt[key]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
TempRt[key] = rt;
|
||||
}
|
||||
|
||||
return TempRt[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e25ed844b59c434e9d2c74c099405cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user