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,320 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine.Rendering;
namespace Pinwheel.Griffin.SplineTool
{
[GDisplayName("Foliage Remover")]
public class GFoliageRemover : GSplineModifier
{
[SerializeField]
private AnimationCurve falloff;
public AnimationCurve Falloff
{
get
{
if (falloff == null)
{
falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return falloff;
}
set
{
falloff = value;
}
}
[SerializeField]
private bool removeTrees;
public bool RemoveTrees
{
get
{
return removeTrees;
}
set
{
removeTrees = value;
}
}
[SerializeField]
private bool removeGrasses;
public bool RemoveGrasses
{
get
{
return removeGrasses;
}
set
{
removeGrasses = value;
}
}
[SerializeField]
private List<int> treePrototypeIndices;
public List<int> TreePrototypeIndices
{
get
{
if (treePrototypeIndices == null)
{
treePrototypeIndices = new List<int>();
}
return treePrototypeIndices;
}
set
{
treePrototypeIndices = value;
}
}
[SerializeField]
private List<int> grassPrototypeIndices;
public List<int> GrassPrototypeIndices
{
get
{
if (grassPrototypeIndices == null)
{
grassPrototypeIndices = new List<int>();
}
return grassPrototypeIndices;
}
set
{
grassPrototypeIndices = value;
}
}
[SerializeField]
private Texture2D falloffNoise;
public Texture2D FalloffNoise
{
get
{
return falloffNoise;
}
set
{
falloffNoise = value;
}
}
[SerializeField]
private Vector2 falloffNoiseSize;
public Vector2 FalloffNoiseSize
{
get
{
return falloffNoiseSize;
}
set
{
falloffNoiseSize = 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);
}
}
private Texture2D falloffTexture;
private Material maskMaterial;
private Material MaskMaterial
{
get
{
if (maskMaterial == null)
{
maskMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splineMaskShader);
}
return maskMaterial;
}
}
private static readonly int FALL_OFF = Shader.PropertyToID("_Falloff");
private static readonly int FALL_OFF_NOISE = Shader.PropertyToID("_FalloffNoise");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
public override void Apply()
{
if (SplineCreator == null)
return;
if (falloffTexture != null)
Object.DestroyImmediate(falloffTexture);
Internal_UpdateFalloffTexture();
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(SplineCreator.SweepTest());
foreach (GStylizedTerrain t in terrains)
{
Apply(t);
}
}
private void Apply(GStylizedTerrain t)
{
if (t.TerrainData == null)
return;
RenderTexture rt = new RenderTexture(MaskResolution, MaskResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Internal_Apply(t, rt);
Texture2D mask = GCommon.CreateTexture(MaskResolution, Color.clear);
GCommon.CopyFromRT(mask, rt);
mask.wrapMode = TextureWrapMode.Clamp;
if (RemoveTrees)
{
RemoveTreeOnTerrain(t, mask);
}
if (RemoveGrasses)
{
RemoveGrassOnTerrain(t, mask);
}
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
rt.Release();
GUtilities.DestroyObject(rt);
GUtilities.DestroyObject(mask);
}
private void RemoveTreeOnTerrain(GStylizedTerrain t, Texture2D mask)
{
NativeArray<Vector2> positionsNA = t.TerrainData.Foliage.GetTreesPositionArray();
NativeArray<bool> resultNA = new NativeArray<bool>(positionsNA.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
GTextureNativeDataDescriptor<Color32> maskNA = new GTextureNativeDataDescriptor<Color32>(mask);
GMaskCullingJob job = new GMaskCullingJob()
{
positions = positionsNA,
result = resultNA,
mask = maskNA
};
JobHandle jHandle = job.Schedule(positionsNA.Length, 100);
jHandle.Complete();
HashSet<int> selectedPrototypes = new HashSet<int>(TreePrototypeIndices);
int index = 0;
t.TerrainData.Foliage.RemoveTreeInstances(tree =>
{
int i = index;
index += 1;
if (!selectedPrototypes.Contains(tree.prototypeIndex))
return false;
return resultNA[i];
});
positionsNA.Dispose();
resultNA.Dispose();
}
private void RemoveGrassOnTerrain(GStylizedTerrain t, Texture2D mask)
{
GGrassPatch[] patches = t.TerrainData.Foliage.GrassPatches;
GMaskCullingDataHolder[] data = new GMaskCullingDataHolder[patches.Length];
JobHandle[] jHandles = new JobHandle[patches.Length];
GTextureNativeDataDescriptor<Color32> maskNA = new GTextureNativeDataDescriptor<Color32>(mask);
for (int i = 0; i < patches.Length; ++i)
{
GMaskCullingDataHolder d = new GMaskCullingDataHolder();
d.positionsNA = patches[i].GetGrassPositionArray();
d.resultNA = new NativeArray<bool>(d.positionsNA.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
data[i] = d;
GMaskCullingJob job = new GMaskCullingJob()
{
positions = d.positionsNA,
result = d.resultNA,
mask = maskNA
};
JobHandle h = job.Schedule(d.positionsNA.Length, 100);
jHandles[i] = h;
}
HashSet<int> selectedPrototypes = new HashSet<int>(GrassPrototypeIndices);
for (int i = 0; i < patches.Length; ++i)
{
JobHandle h = jHandles[i];
h.Complete();
GMaskCullingDataHolder d = data[i];
int index = 0;
patches[i].RemoveInstances(g =>
{
int indexClone = index;
index += 1;
if (!selectedPrototypes.Contains(g.prototypeIndex))
return false;
return d.resultNA[indexClone];
});
d.positionsNA.Dispose();
d.resultNA.Dispose();
}
}
private Material PrepareMaterial(GStylizedTerrain t, RenderTexture rt)
{
GCommon.ClearRT(rt);
Material mat = MaskMaterial;
mat.SetTexture(FALL_OFF, falloffTexture);
mat.SetTexture(FALL_OFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture);
mat.SetTextureScale(FALL_OFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALL_OFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
return mat;
}
public void Internal_Apply(GStylizedTerrain t, RenderTexture rt)
{
Material mat = PrepareMaterial(t, rt);
SplineCreator.DrawOnTexture(rt, t.Bounds, mat);
}
public void Reset()
{
SplineCreator = GetComponent<GSplineCreator>();
Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
RemoveTrees = true;
RemoveGrasses = true;
TreePrototypeIndices = null;
GrassPrototypeIndices = null;
MaskResolution = 1024;
}
public void Internal_UpdateFalloffTexture()
{
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,415 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Burst;
using Unity.Collections;
using UnityEngine.Rendering;
namespace Pinwheel.Griffin.SplineTool
{
[GDisplayName("Foliage Spawner")]
public class GFoliageSpawner : GSplineModifier
{
[SerializeField]
private AnimationCurve falloff;
public AnimationCurve Falloff
{
get
{
if (falloff == null)
{
falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return falloff;
}
set
{
falloff = value;
}
}
[SerializeField]
private bool spawnTrees;
public bool SpawnTrees
{
get
{
return spawnTrees;
}
set
{
spawnTrees = value;
}
}
[SerializeField]
private bool spawnGrasses;
public bool SpawnGrasses
{
get
{
return spawnGrasses;
}
set
{
spawnGrasses = value;
}
}
[SerializeField]
private List<int> treePrototypeIndices;
public List<int> TreePrototypeIndices
{
get
{
if (treePrototypeIndices == null)
{
treePrototypeIndices = new List<int>();
}
return treePrototypeIndices;
}
set
{
treePrototypeIndices = value;
}
}
[SerializeField]
private List<int> grassPrototypeIndices;
public List<int> GrassPrototypeIndices
{
get
{
if (grassPrototypeIndices == null)
{
grassPrototypeIndices = new List<int>();
}
return grassPrototypeIndices;
}
set
{
grassPrototypeIndices = value;
}
}
[SerializeField]
private Texture2D falloffNoise;
public Texture2D FalloffNoise
{
get
{
return falloffNoise;
}
set
{
falloffNoise = value;
}
}
[SerializeField]
private Vector2 falloffNoiseSize;
public Vector2 FalloffNoiseSize
{
get
{
return falloffNoiseSize;
}
set
{
falloffNoiseSize = 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);
}
}
[SerializeField]
private float treeDensity;
public float TreeDensity
{
get
{
return treeDensity;
}
set
{
treeDensity = Mathf.Max(0, value);
}
}
[SerializeField]
private float grassDensity;
public float GrassDensity
{
get
{
return grassDensity;
}
set
{
grassDensity = 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;
}
}
private Texture2D falloffTexture;
private Material maskMaterial;
private Material MaskMaterial
{
get
{
if (maskMaterial == null)
{
maskMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splineMaskShader);
}
return maskMaterial;
}
}
private static readonly int FALL_OFF = Shader.PropertyToID("_Falloff");
private static readonly int FALL_OFF_NOISE = Shader.PropertyToID("_FalloffNoise");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
public override void Apply()
{
if (SplineCreator == null)
return;
if (falloffTexture != null)
Object.DestroyImmediate(falloffTexture);
Internal_UpdateFalloffTexture();
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(SplineCreator.SweepTest());
foreach (GStylizedTerrain t in terrains)
{
Apply(t);
}
}
private void Apply(GStylizedTerrain t)
{
if (t.TerrainData == null)
return;
RenderTexture rt = new RenderTexture(MaskResolution, MaskResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Internal_Apply(t, rt);
Texture2D mask = GCommon.CreateTexture(MaskResolution, Color.clear);
GCommon.CopyFromRT(mask, rt);
mask.wrapMode = TextureWrapMode.Clamp;
t.TerrainData.Foliage.SetTreeRegionDirty(SplineCreator.SweepDirtyRect(t));
t.TerrainData.Foliage.SetGrassRegionDirty(SplineCreator.SweepDirtyRect(t));
if (SpawnTrees &&
t.TerrainData.Foliage.Trees != null &&
TreePrototypeIndices.Count > 0)
{
SpawnTreesOnTerrain(t, mask);
t.UpdateTreesPosition();
}
if (SpawnGrasses &&
t.TerrainData.Foliage.Grasses != null &&
GrassPrototypeIndices.Count > 0)
{
SpawnGrassesOnTerrain(t, mask);
t.UpdateGrassPatches();
}
t.TerrainData.Foliage.ClearTreeDirtyRegions();
t.TerrainData.Foliage.ClearGrassDirtyRegions();
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
rt.Release();
GUtilities.DestroyObject(rt);
GUtilities.DestroyObject(mask);
}
private void SpawnTreesOnTerrain(GStylizedTerrain t, Texture2D mask)
{
int sampleCount = Mathf.FloorToInt(TreeDensity * t.TerrainData.Geometry.Width * t.TerrainData.Geometry.Length);
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>(TreePrototypeIndices.ToArray(), Allocator.TempJob);
GTextureNativeDataDescriptor<Color32> maskHandle = new GTextureNativeDataDescriptor<Color32>(mask);
GSampleInstanceJob job = new GSampleInstanceJob()
{
cullResult = cullResult,
instanceInfo = foliageInfo,
mask = maskHandle,
selectedPrototypeIndices = selectedPrototypeIndices,
minRotation = minRotation,
maxRotation = maxRotation,
minScale = minScale,
maxScale = 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 (cullResult[i] == false)
continue;
GTreeInstance tree = foliageInfo[i].ToTreeInstance();
instances.Add(tree);
}
t.TerrainData.Foliage.AddTreeInstances(instances);
cullResult.Dispose();
foliageInfo.Dispose();
selectedPrototypeIndices.Dispose();
}
private void SpawnGrassesOnTerrain(GStylizedTerrain t, Texture2D mask)
{
int sampleCount = Mathf.FloorToInt(GrassDensity * t.TerrainData.Geometry.Width * t.TerrainData.Geometry.Length);
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>(GrassPrototypeIndices.ToArray(), Allocator.TempJob);
GTextureNativeDataDescriptor<Color32> maskHandleNA = new GTextureNativeDataDescriptor<Color32>(mask);
GSampleInstanceJob job = new GSampleInstanceJob()
{
cullResult = cullResultNA,
instanceInfo = foliageInfoNA,
mask = maskHandleNA,
selectedPrototypeIndices = selectedPrototypeIndicesNA,
minRotation = minRotation,
maxRotation = maxRotation,
minScale = minScale,
maxScale = 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();
}
private Material PrepareMaterial(GStylizedTerrain t, RenderTexture rt)
{
GCommon.ClearRT(rt);
Material mat = MaskMaterial;
mat.SetTexture(FALL_OFF, falloffTexture);
mat.SetTexture(FALL_OFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture);
mat.SetTextureScale(FALL_OFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALL_OFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
return mat;
}
public void Internal_Apply(GStylizedTerrain t, RenderTexture rt)
{
Material mat = PrepareMaterial(t, rt);
SplineCreator.DrawOnTexture(rt, t.Bounds, maskMaterial);
}
public void Reset()
{
SplineCreator = GetComponent<GSplineCreator>();
Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
SpawnTrees = true;
SpawnGrasses = true;
MaskResolution = 1024;
TreePrototypeIndices = null;
GrassPrototypeIndices = null;
MinRotation = 0;
MaxRotation = 360;
MinScale = new Vector3(0.7f, 0.8f, 0.7f);
MaxScale = new Vector3(1.2f, 1.5f, 1.2f);
}
public void Internal_UpdateFalloffTexture()
{
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,224 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Pinwheel.Griffin.SplineTool
{
[GDisplayName("Object Remover")]
public class GObjectRemover : GSplineModifier
{
[SerializeField]
private AnimationCurve falloff;
public AnimationCurve Falloff
{
get
{
if (falloff == null)
{
falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return falloff;
}
set
{
falloff = value;
}
}
[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> prototypeIndices;
public List<int> PrototypeIndices
{
get
{
if (prototypeIndices == null)
{
prototypeIndices = new List<int>();
}
return prototypeIndices;
}
set
{
prototypeIndices = value;
}
}
[SerializeField]
private Texture2D falloffNoise;
public Texture2D FalloffNoise
{
get
{
return falloffNoise;
}
set
{
falloffNoise = value;
}
}
[SerializeField]
private Vector2 falloffNoiseSize;
public Vector2 FalloffNoiseSize
{
get
{
return falloffNoiseSize;
}
set
{
falloffNoiseSize = 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);
}
}
private Texture2D falloffTexture;
private Material maskMaterial;
private Material MaskMaterial
{
get
{
if (maskMaterial == null)
{
maskMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splineMaskShader);
}
return maskMaterial;
}
}
private static readonly int FALL_OFF = Shader.PropertyToID("_Falloff");
private static readonly int FALL_OFF_NOISE = Shader.PropertyToID("_FalloffNoise");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
public override void Apply()
{
if (SplineCreator == null)
return;
if (falloffTexture != null)
Object.DestroyImmediate(falloffTexture);
Internal_UpdateFalloffTexture();
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(SplineCreator.SweepTest());
foreach (GStylizedTerrain t in terrains)
{
Apply(t);
}
}
private void Apply(GStylizedTerrain t)
{
if (t.TerrainData == null)
return;
if (PrototypeIndices.Count == 0)
return;
if (Prototypes.Count == 0)
return;
RenderTexture rt = new RenderTexture(MaskResolution, MaskResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Internal_Apply(t, rt);
Texture2D mask = GCommon.CreateTexture(MaskResolution, Color.clear);
GCommon.CopyFromRT(mask, rt);
mask.wrapMode = TextureWrapMode.Clamp;
Color[] maskColors = mask.GetPixels();
RemoveObjectFromTerrain(t, maskColors);
rt.Release();
GUtilities.DestroyObject(rt);
GUtilities.DestroyObject(mask);
}
private void RemoveObjectFromTerrain(GStylizedTerrain t, Color[] maskData)
{
for (int i = 0; i < PrototypeIndices.Count; ++i)
{
int prototypeIndex = PrototypeIndices[i];
if (prototypeIndex < 0 || prototypeIndex >= Prototypes.Count)
continue;
GameObject g = Prototypes[prototypeIndex];
if (g == null)
continue;
GSpawner.DestroyIf(t, g, (instance) =>
{
Vector2 uv = t.WorldPointToUV(instance.transform.position);
float alpha = GUtilities.GetColorBilinear(maskData, MaskResolution, MaskResolution, uv).r;
return Random.value <= alpha;
});
}
}
private Material PrepareMaterial(GStylizedTerrain t, RenderTexture rt)
{
GCommon.ClearRT(rt);
Material mat = GInternalMaterials.FoliageRemoverMaterial;
mat.SetTexture(FALL_OFF, falloffTexture);
mat.SetTexture(FALL_OFF_NOISE, FalloffNoise);
mat.SetTextureScale(FALL_OFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALL_OFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
return mat;
}
public void Internal_Apply(GStylizedTerrain t, RenderTexture rt)
{
Material mat = PrepareMaterial(t, rt);
SplineCreator.DrawOnTexture(rt, t.Bounds, mat);
}
public void Reset()
{
SplineCreator = GetComponent<GSplineCreator>();
Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
Prototypes = null;
PrototypeIndices = null;
MaskResolution = 1024;
}
public void Internal_UpdateFalloffTexture()
{
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,399 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
using UnityEngine.Rendering;
namespace Pinwheel.Griffin.SplineTool
{
[GDisplayName("Object Spawner")]
public class GObjectSpawner : GSplineModifier
{
[SerializeField]
private AnimationCurve falloff;
public AnimationCurve Falloff
{
get
{
if (falloff == null)
{
falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return falloff;
}
set
{
falloff = value;
}
}
[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> prototypeIndices;
public List<int> PrototypeIndices
{
get
{
if (prototypeIndices == null)
{
prototypeIndices = new List<int>();
}
return prototypeIndices;
}
set
{
prototypeIndices = value;
}
}
[SerializeField]
private Texture2D falloffNoise;
public Texture2D FalloffNoise
{
get
{
return falloffNoise;
}
set
{
falloffNoise = value;
}
}
[SerializeField]
private Vector2 falloffNoiseSize;
public Vector2 FalloffNoiseSize
{
get
{
return falloffNoiseSize;
}
set
{
falloffNoiseSize = 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);
}
}
[SerializeField]
private float density;
public float Density
{
get
{
return density;
}
set
{
density = 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;
}
}
private Texture2D falloffTexture;
private Material maskMaterial;
private Material MaskMaterial
{
get
{
if (maskMaterial == null)
{
maskMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splineMaskShader);
}
return maskMaterial;
}
}
private static readonly int FALL_OFF = Shader.PropertyToID("_Falloff");
private static readonly int FALL_OFF_NOISE = Shader.PropertyToID("_FalloffNoise");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
public override void Apply()
{
if (SplineCreator == null)
return;
if (falloffTexture != null)
Object.DestroyImmediate(falloffTexture);
Internal_UpdateFalloffTexture();
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(SplineCreator.SweepTest());
foreach (GStylizedTerrain t in terrains)
{
Apply(t);
}
}
private void Apply(GStylizedTerrain t)
{
if (t.TerrainData == null)
return;
if (PrototypeIndices.Count == 0)
return;
if (Prototypes.Count == 0)
return;
RenderTexture rt = new RenderTexture(MaskResolution, MaskResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Internal_Apply(t, rt);
Texture2D mask = GCommon.CreateTexture(MaskResolution, Color.clear);
GCommon.CopyFromRT(mask, rt);
mask.wrapMode = TextureWrapMode.Clamp;
SpawnObjectsOnTerrain(t, mask);
rt.Release();
GUtilities.DestroyObject(rt);
GUtilities.DestroyObject(mask);
}
private void SpawnObjectsOnTerrain(GStylizedTerrain t, Texture2D mask)
{
int sampleCount = Mathf.FloorToInt(Density * t.TerrainData.Geometry.Width * t.TerrainData.Geometry.Length);
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>(PrototypeIndices.ToArray(), Allocator.TempJob);
GTextureNativeDataDescriptor<Color32> maskHandle = new GTextureNativeDataDescriptor<Color32>(mask);
{
GSampleInstanceJob job = new GSampleInstanceJob()
{
cullResult = cullResult,
instanceInfo = instanceInfo,
mask = maskHandle,
selectedPrototypeIndices = selectedPrototypeIndices,
minRotation = minRotation,
maxRotation = maxRotation,
minScale = minScale,
maxScale = 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(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, 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;
GameObject prototype = 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 (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();
}
private Material PrepareMaterial(GStylizedTerrain t, RenderTexture rt)
{
GCommon.ClearRT(rt);
Material mat = MaskMaterial;
mat.SetTexture(FALL_OFF, falloffTexture);
mat.SetTexture(FALL_OFF_NOISE, FalloffNoise);
mat.SetTextureScale(FALL_OFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALL_OFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
return mat;
}
public void Internal_Apply(GStylizedTerrain t, RenderTexture rt)
{
Material mat = PrepareMaterial(t, rt);
SplineCreator.DrawOnTexture(rt, t.Bounds, mat);
}
public void Reset()
{
SplineCreator = GetComponent<GSplineCreator>();
Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
Prototypes = null;
PrototypeIndices = null;
Density = 1;
MaskResolution = 1024;
MinRotation = 0;
MaxRotation = 360;
MinScale = new Vector3(0.7f, 0.8f, 0.7f);
MaxScale = new Vector3(1.2f, 1.5f, 1.2f);
WorldRaycastMask = 1;
}
public void Internal_UpdateFalloffTexture()
{
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,385 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Pinwheel.Griffin.SplineTool
{
[GDisplayName("Path Painter")]
public class GPathPainter : GSplineModifier
{
public enum PaintChannel
{
AlbedoAndMetallic, Splat
}
[SerializeField]
private AnimationCurve falloff;
public AnimationCurve Falloff
{
get
{
if (falloff == null)
{
falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return falloff;
}
set
{
falloff = value;
}
}
[SerializeField]
private PaintChannel channel;
public PaintChannel Channel
{
get
{
return channel;
}
set
{
channel = value;
}
}
[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 = Mathf.Max(0, value);
}
}
[SerializeField]
private Texture2D falloffNoise;
public Texture2D FalloffNoise
{
get
{
return falloffNoise;
}
set
{
falloffNoise = value;
}
}
[SerializeField]
private Vector2 falloffNoiseSize;
public Vector2 FalloffNoiseSize
{
get
{
return falloffNoiseSize;
}
set
{
falloffNoiseSize = value;
}
}
private Texture2D falloffTexture;
private Material applyAlbedoMaterial;
private Material ApplyAlbedoMaterial
{
get
{
if (applyAlbedoMaterial == null)
{
applyAlbedoMaterial = new Material(GRuntimeSettings.Instance.internalShaders.pathPainterAlbedoShader);
}
return applyAlbedoMaterial;
}
}
private Material applyMetallicSmoothnessMaterial;
private Material ApplyMetallicSmoothnessMaterial
{
get
{
if (applyMetallicSmoothnessMaterial == null)
{
applyMetallicSmoothnessMaterial = new Material(GRuntimeSettings.Instance.internalShaders.pathPainterMetallicSmoothnessShader);
}
return applyMetallicSmoothnessMaterial;
}
}
private Material applySplatMaterial;
private Material ApplySplatMaterial
{
get
{
if (applySplatMaterial == null)
{
applySplatMaterial = new Material(GRuntimeSettings.Instance.internalShaders.pathPainterSplatShader);
}
return applySplatMaterial;
}
}
private void Reset()
{
SplineCreator = GetComponent<GSplineCreator>();
Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
Channel = PaintChannel.AlbedoAndMetallic;
Color = Color.white;
Metallic = 0;
Smoothness = 0;
}
public override void Apply()
{
if (SplineCreator == null)
return;
if (falloffTexture != null)
Object.DestroyImmediate(falloffTexture);
Internal_UpdateFalloffTexture();
List<GOverlapTestResult> sweepTests = SplineCreator.SweepTest();
foreach (GOverlapTestResult st in sweepTests)
{
if (st.IsOverlapped)
{
Apply(st.Terrain);
}
}
}
private void Apply(GStylizedTerrain t)
{
if (t.TerrainData == null)
return;
if (Channel == PaintChannel.AlbedoAndMetallic)
{
ApplyAlbedoAndMetallic(t);
}
else if (Channel == PaintChannel.Splat)
{
ApplySplat(t);
}
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
}
private void ApplyAlbedoAndMetallic(GStylizedTerrain t)
{
int albedoResolution = t.TerrainData.Shading.AlbedoMapResolution;
RenderTexture rtAlbedo = new RenderTexture(albedoResolution, albedoResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Internal_ApplyAlbedo(t, rtAlbedo);
RenderTexture.active = rtAlbedo;
t.TerrainData.Shading.AlbedoMap.ReadPixels(new Rect(0, 0, albedoResolution, albedoResolution), 0, 0);
t.TerrainData.Shading.AlbedoMap.Apply();
RenderTexture.active = null;
rtAlbedo.Release();
Object.DestroyImmediate(rtAlbedo);
int metallicResolution = t.TerrainData.Shading.MetallicMapResolution;
RenderTexture rtMetallic = new RenderTexture(metallicResolution, metallicResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Internal_ApplyMetallic(t, rtMetallic);
RenderTexture.active = rtMetallic;
t.TerrainData.Shading.MetallicMap.ReadPixels(new Rect(0, 0, metallicResolution, metallicResolution), 0, 0);
t.TerrainData.Shading.MetallicMap.Apply();
RenderTexture.active = null;
rtMetallic.Release();
Object.DestroyImmediate(rtMetallic);
}
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
private static readonly int FALLOFF = Shader.PropertyToID("_Falloff");
private static readonly int FALLOFF_NOISE = Shader.PropertyToID("_FalloffNoise");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static readonly int COLOR = Shader.PropertyToID("_Color");
private static readonly int METALLIC = Shader.PropertyToID("_Metallic");
private static readonly int SMOOTHNESS = Shader.PropertyToID("_Smoothness");
private static readonly int CHANNEL_INDEX = Shader.PropertyToID("_ChannelIndex");
private Material PrepareAlbedoMat(GStylizedTerrain t, RenderTexture rtAlbedo)
{
GCommon.CopyToRT(t.TerrainData.Shading.AlbedoMapOrDefault, rtAlbedo);
Material mat = ApplyAlbedoMaterial;
mat.SetTexture(MAIN_TEX, t.TerrainData.Shading.AlbedoMapOrDefault);
mat.SetTexture(FALLOFF, falloffTexture);
mat.SetTexture(FALLOFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture);
mat.SetTextureScale(FALLOFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALLOFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
mat.SetColor(COLOR, Color);
return mat;
}
public void Internal_ApplyAlbedo(GStylizedTerrain t, RenderTexture rtAlbedo)
{
Material mat = PrepareAlbedoMat(t, rtAlbedo);
SplineCreator.DrawOnTexture(rtAlbedo, t.Bounds, mat);
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
}
private Material PrepareMetallicMat(GStylizedTerrain t, RenderTexture rtMetallic)
{
GCommon.CopyToRT(t.TerrainData.Shading.MetallicMapOrDefault, rtMetallic);
Material mat = ApplyMetallicSmoothnessMaterial;
mat.SetTexture(MAIN_TEX, t.TerrainData.Shading.MetallicMapOrDefault);
mat.SetTexture(FALLOFF, falloffTexture);
mat.SetTexture(FALLOFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture);
mat.SetTextureScale(FALLOFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALLOFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
mat.SetFloat(METALLIC, Metallic);
mat.SetFloat(SMOOTHNESS, Smoothness);
return mat;
}
public void Internal_ApplyMetallic(GStylizedTerrain t, RenderTexture rtMetallic)
{
Material mat = PrepareMetallicMat(t, rtMetallic);
SplineCreator.DrawOnTexture(rtMetallic, t.Bounds, mat);
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
}
public void Internal_UpdateFalloffTexture()
{
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
}
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);
}
Internal_ApplySplat(t, rtControls);
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 Material PrepareSplatMat(GStylizedTerrain t)
{
Material mat = ApplySplatMaterial;
mat.SetTexture(FALLOFF, falloffTexture);
mat.SetTexture(FALLOFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture);
mat.SetTextureScale(FALLOFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALLOFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
return mat;
}
public void Internal_ApplySplat(GStylizedTerrain t, RenderTexture[] rtControls)
{
Material mat = PrepareSplatMat(t);
for (int i = 0; i < rtControls.Length; ++i)
{
Texture2D splatControl = t.TerrainData.Shading.GetSplatControlOrDefault(i);
GCommon.CopyToRT(splatControl, rtControls[i]);
mat.SetTexture(MAIN_TEX, splatControl);
if (SplatIndex / 4 == i)
{
mat.SetInt(CHANNEL_INDEX, SplatIndex % 4);
}
else
{
mat.SetInt(CHANNEL_INDEX, -1);
}
SplineCreator.DrawOnTexture(rtControls[i], t.Bounds, mat);
}
t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Shading);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,264 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Pinwheel.Griffin.SplineTool
{
[GDisplayName("Ramp Maker")]
public class GRampMaker : GSplineModifier
{
[SerializeField]
private AnimationCurve falloff;
public AnimationCurve Falloff
{
get
{
if (falloff == null)
{
falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
return falloff;
}
set
{
falloff = value;
}
}
[SerializeField]
private bool lowerHeight;
public bool LowerHeight
{
get
{
return lowerHeight;
}
set
{
lowerHeight = value;
}
}
[SerializeField]
private bool raiseHeight;
public bool RaiseHeight
{
get
{
return raiseHeight;
}
set
{
raiseHeight = value;
}
}
[SerializeField]
private int additionalMeshResolution;
public int AdditionalMeshResolution
{
get
{
return additionalMeshResolution;
}
set
{
additionalMeshResolution = Mathf.Clamp(value, 0, 10);
}
}
[SerializeField]
private float heightOffset;
public float HeightOffset
{
get
{
return heightOffset;
}
set
{
heightOffset = value;
}
}
[SerializeField]
private int stepCount;
public int StepCount
{
get
{
return stepCount;
}
set
{
stepCount = Mathf.Max(1, value);
}
}
[SerializeField]
private Texture2D falloffNoise;
public Texture2D FalloffNoise
{
get
{
return falloffNoise;
}
set
{
falloffNoise = value;
}
}
[SerializeField]
private Vector2 falloffNoiseSize;
public Vector2 FalloffNoiseSize
{
get
{
return falloffNoiseSize;
}
set
{
falloffNoiseSize = value;
}
}
private Texture2D falloffTexture;
private Material rampMaterial;
private Material RampMaterial
{
get
{
if (rampMaterial == null)
{
rampMaterial = new Material(GRuntimeSettings.Instance.internalShaders.rampMakerShader);
}
return rampMaterial;
}
}
public override void Apply()
{
if (SplineCreator == null)
return;
if (falloffTexture != null)
Object.DestroyImmediate(falloffTexture);
Internal_UpdateFalloffTexture();
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(SplineCreator.SweepTest());
for (int i = 0; i < terrains.Count; ++i)
{
DrawOnTexture(terrains[i]);
}
for (int i = 0; i < terrains.Count; ++i)
{
UpdateTerrain(terrains[i]);
}
for (int i = 0; i < terrains.Count; ++i)
{
terrains[i].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);
}
private static readonly int HEIGHT_MAP = Shader.PropertyToID("_HeightMap");
private static readonly int FALLOFF = Shader.PropertyToID("_Falloff");
private static readonly int HEIGHT_OFFSET = Shader.PropertyToID("_HeightOffset");
private static readonly int LOWER_HEIGHT = Shader.PropertyToID("_LowerHeight");
private static readonly int RAISE_HEIGHT = Shader.PropertyToID("_RaiseHeight");
private static readonly int ADDITIONAL_MESH_RESOLUTION = Shader.PropertyToID("_AdditionalMeshResolution");
private static readonly int FALLOFF_NOISE = Shader.PropertyToID("_FalloffNoise");
private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask");
private static readonly int STEP_COUNT = Shader.PropertyToID("_StepCount");
private Material PrepareMaterial(GStylizedTerrain t, RenderTexture rt)
{
GCommon.CopyToRT(t.TerrainData.Geometry.HeightMap, rt);
Material mat = RampMaterial;
mat.SetTexture(HEIGHT_MAP, t.TerrainData.Geometry.HeightMap);
mat.SetTexture(FALLOFF, falloffTexture);
mat.SetFloat(HEIGHT_OFFSET, HeightOffset);
mat.SetInt(LOWER_HEIGHT, LowerHeight ? 1 : 0);
mat.SetInt(RAISE_HEIGHT, RaiseHeight ? 1 : 0);
mat.SetFloat(ADDITIONAL_MESH_RESOLUTION, GCommon.SUB_DIV_STEP * AdditionalMeshResolution);
mat.SetTexture(FALLOFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture);
mat.SetTextureScale(FALLOFF_NOISE, new Vector2(
FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0,
FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0));
mat.SetTextureOffset(FALLOFF_NOISE, Vector2.zero);
if (SplineCreator.EnableTerrainMask)
{
mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault);
}
else
{
mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture);
}
mat.SetInt(STEP_COUNT, StepCount);
return mat;
}
public void Internal_DrawOnTexture(GStylizedTerrain t, RenderTexture rt)
{
Material mat = PrepareMaterial(t, rt);
SplineCreator.DrawOnTexture(rt, t.Bounds, mat);
}
public void Internal_UpdateFalloffTexture()
{
falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1);
}
public void Reset()
{
SplineCreator = GetComponent<GSplineCreator>();
Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1);
LowerHeight = true;
RaiseHeight = true;
HeightOffset = 0;
StepCount = 1000;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,308 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using System;
using Unity.Collections;
namespace Pinwheel.Griffin.SplineTool
{
[System.Serializable]
public class GSpline : IDisposable
{
[SerializeField]
private List<GSplineAnchor> anchors;
public List<GSplineAnchor> Anchors
{
get
{
if (anchors == null)
{
anchors = new List<GSplineAnchor>();
}
return anchors;
}
}
[SerializeField]
private List<GSplineSegment> segments;
public List<GSplineSegment> Segments
{
get
{
if (segments == null)
{
segments = new List<GSplineSegment>();
}
return segments;
}
}
public bool HasBranch
{
get
{
return CheckHasBranch();
}
}
public bool IsSegmentValid(int segmentIndex)
{
GSplineSegment s = Segments[segmentIndex];
if (s == null)
return false;
bool startIndexValid =
s.StartIndex >= 0 &&
s.StartIndex < Anchors.Count &&
Anchors[s.StartIndex] != null;
bool endIndexValid =
s.EndIndex >= 0 &&
s.EndIndex < Anchors.Count &&
Anchors[s.EndIndex] != null;
return startIndexValid && endIndexValid;
}
public void AddAnchor(GSplineAnchor a)
{
Anchors.Add(a);
}
public void RemoveAnchor(int index)
{
GSplineAnchor a = Anchors[index];
List<int> segmentIndices = FindSegments(index);
for (int i = 0; i < segmentIndices.Count; ++i)
{
int sIndex = segmentIndices[i];
Segments[sIndex].Dispose();
}
Segments.RemoveAll(s => s.StartIndex == index || s.EndIndex == index);
Segments.ForEach(s =>
{
if (s.StartIndex > index)
s.StartIndex -= 1;
if (s.EndIndex > index)
s.EndIndex -= 1;
});
Anchors.RemoveAt(index);
}
public GSplineSegment AddSegment(int startIndex, int endIndex)
{
GSplineSegment s = Segments.Find(s0 =>
(s0.StartIndex == startIndex && s0.EndIndex == endIndex) ||
(s0.StartIndex == endIndex && s0.EndIndex == startIndex));
if (s != null)
return s;
GSplineSegment newSegment = new GSplineSegment();
newSegment.StartIndex = startIndex;
newSegment.EndIndex = endIndex;
Segments.Add(newSegment);
GSplineAnchor startAnchor = Anchors[newSegment.StartIndex];
GSplineAnchor endAnchor = Anchors[newSegment.EndIndex];
Vector3 direction = (endAnchor.Position - startAnchor.Position).normalized;
float length = (endAnchor.Position - startAnchor.Position).magnitude / 3;
newSegment.StartTangent = startAnchor.Position + direction * length;
newSegment.EndTangent = endAnchor.Position - direction * length;
return newSegment;
}
public void RemoveSegment(int index)
{
Segments[index].Dispose();
Segments.RemoveAt(index);
}
public Vector3 EvaluatePosition(int segmentIndex, float t)
{
GSplineSegment s = Segments[segmentIndex];
GSplineAnchor startAnchor = Anchors[s.StartIndex];
GSplineAnchor endAnchor = Anchors[s.EndIndex];
Vector3 p0 = startAnchor.Position;
Vector3 p1 = s.StartTangent;
Vector3 p2 = s.EndTangent;
Vector3 p3 = endAnchor.Position;
t = Mathf.Clamp01(t);
float oneMinusT = 1 - t;
Vector3 p =
oneMinusT * oneMinusT * oneMinusT * p0 +
3 * oneMinusT * oneMinusT * t * p1 +
3 * oneMinusT * t * t * p2 +
t * t * t * p3;
return p;
}
public Quaternion EvaluateRotation(int segmentIndex, float t)
{
GSplineSegment s = Segments[segmentIndex];
GSplineAnchor startAnchor = Anchors[s.StartIndex];
GSplineAnchor endAnchor = Anchors[s.EndIndex];
return Quaternion.Lerp(startAnchor.Rotation, endAnchor.Rotation, t);
}
public Vector3 EvaluateScale(int segmentIndex, float t)
{
GSplineSegment s = Segments[segmentIndex];
GSplineAnchor startAnchor = Anchors[s.StartIndex];
GSplineAnchor endAnchor = Anchors[s.EndIndex];
return Vector3.Lerp(startAnchor.Scale, endAnchor.Scale, t);
}
public Vector3 EvaluateUpVector(int segmentIndex, float t)
{
Quaternion rotation = EvaluateRotation(segmentIndex, t);
Matrix4x4 matrix = Matrix4x4.Rotate(rotation);
return matrix.MultiplyVector(Vector3.up);
}
public Matrix4x4 TRS(int segmentIndex, float t)
{
Vector3 pos = EvaluatePosition(segmentIndex, t);
Quaternion rotation = EvaluateRotation(segmentIndex, t);
Vector3 scale = EvaluateScale(segmentIndex, t);
return Matrix4x4.TRS(pos, rotation, scale);
}
private bool CheckHasBranch()
{
int[] count = new int[Anchors.Count];
for (int i = 0; i < Segments.Count; ++i)
{
if (!IsSegmentValid(i))
continue;
GSplineSegment s = Segments[i];
count[s.StartIndex] += 1;
count[s.EndIndex] += 1;
if (count[s.StartIndex] > 2 || count[s.EndIndex] > 2)
return true;
}
return false;
}
public List<int> FindSegments(int anchorIndex)
{
List<int> indices = new List<int>();
for (int i = 0; i < Segments.Count; ++i)
{
if (Segments[i].StartIndex == anchorIndex ||
Segments[i].EndIndex == anchorIndex)
{
indices.Add(i);
}
}
return indices;
}
public void Dispose()
{
for (int i = 0; i < Segments.Count; ++i)
{
if (Segments[i] != null)
{
Segments[i].Dispose();
}
}
}
public int[] SmoothTangents(params int[] anchorIndices)
{
int[] anchorRanks = new int[Anchors.Count];
Vector3[] directions = new Vector3[Anchors.Count];
float[] segmentLengths = new float[Segments.Count];
for (int i = 0; i < Segments.Count; ++i)
{
GSplineSegment s = Segments[i];
anchorRanks[s.StartIndex] += 1;
anchorRanks[s.EndIndex] += 1;
GSplineAnchor aStart = Anchors[s.StartIndex];
GSplineAnchor aEnd = Anchors[s.EndIndex];
Vector3 startToEnd = aEnd.Position - aStart.Position;
Vector3 d = Vector3.Normalize(startToEnd);
directions[s.StartIndex] += d;
directions[s.EndIndex] += d;
segmentLengths[i] = startToEnd.magnitude;
}
for (int i = 0; i < directions.Length; ++i)
{
if (anchorRanks[i] == 0)
continue;
directions[i] = Vector3.Normalize(directions[i] / anchorRanks[i]);
}
if (anchorIndices == null || anchorIndices.Length == 0)
{
anchorIndices = GUtilities.GetIndicesArray(Anchors.Count);
}
for (int i = 0; i < anchorIndices.Length; ++i)
{
int index = anchorIndices[i];
if (anchorRanks[index] > 0)
{
Quaternion rot = Quaternion.LookRotation(directions[index], Vector3.up);
Anchors[index].Rotation = rot;
}
}
List<int> segmentIndices = new List<int>();
for (int i = 0; i < Segments.Count; ++i)
{
GSplineSegment s = Segments[i];
for (int j = 0; j < anchorIndices.Length; ++j)
{
int anchorIndex = anchorIndices[j];
if (s.StartIndex == anchorIndex || s.EndIndex == anchorIndex)
{
segmentIndices.Add(i);
}
}
}
for (int i = 0; i < segmentIndices.Count; ++i)
{
int index = segmentIndices[i];
GSplineSegment s = Segments[index];
GSplineAnchor aStart = Anchors[s.StartIndex];
GSplineAnchor aEnd = Anchors[s.EndIndex];
float sLength = segmentLengths[index];
float tangentLength = sLength * 0.33f;
Vector3 dirStart = directions[s.StartIndex];
Vector3 dirEnd = directions[s.EndIndex];
s.StartTangent = aStart.Position + dirStart * tangentLength;
s.EndTangent = aEnd.Position - dirEnd * tangentLength;
}
return segmentIndices.ToArray();
}
public NativeArray<GSplineAnchor.GSweepTestData> GetAnchorSweepTestData()
{
NativeArray<GSplineAnchor.GSweepTestData> data = new NativeArray<GSplineAnchor.GSweepTestData>(Anchors.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < Anchors.Count; ++i)
{
data[i] = Anchors[i].SweepTestData;
}
return data;
}
public NativeArray<GSplineSegment.GSweepTestData> GetSegmentSweepTestData()
{
NativeArray<GSplineSegment.GSweepTestData> data = new NativeArray<GSplineSegment.GSweepTestData>(Segments.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < Segments.Count; ++i)
{
data[i] = Segments[i].SweepTestData;
}
return data;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,77 @@
#if GRIFFIN
using UnityEngine;
namespace Pinwheel.Griffin.SplineTool
{
[System.Serializable]
public class GSplineAnchor
{
[SerializeField]
private Vector3 position;
public Vector3 Position
{
get
{
return position;
}
set
{
position = value;
}
}
[SerializeField]
private Quaternion rotation;
public Quaternion Rotation
{
get
{
return rotation;
}
set
{
rotation = value;
}
}
[SerializeField]
private Vector3 scale;
public Vector3 Scale
{
get
{
return scale;
}
set
{
scale = value;
}
}
public GSweepTestData SweepTestData
{
get
{
return new GSweepTestData()
{
position = this.position,
scale = this.scale
};
}
}
public GSplineAnchor(Vector3 pos)
{
position = pos;
rotation = Quaternion.identity;
scale = Vector3.one;
}
public struct GSweepTestData
{
public Vector3 position;
public Vector3 scale;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,443 @@
#if GRIFFIN
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor.SceneManagement;
#endif
using UnityEngine.Rendering;
#if GRIFFIN_URP
using UnityEngine.Rendering.Universal;
#endif
namespace Pinwheel.Griffin.SplineTool
{
[System.Serializable]
[ExecuteInEditMode]
public partial class GSplineCreator : MonoBehaviour
{
public const string SPLINE_LAYER = "POLARIS SPLINE";
public delegate void SplineChangedHandler(GSplineCreator sender);
public static event SplineChangedHandler Editor_SplineChanged;
[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 Vector3 positionOffset;
public Vector3 PositionOffset
{
get
{
return positionOffset;
}
set
{
positionOffset = value;
}
}
[SerializeField]
private Quaternion initialRotation;
public Quaternion InitialRotation
{
get
{
return initialRotation;
}
set
{
initialRotation = value;
}
}
[SerializeField]
private Vector3 initialScale;
public Vector3 InitialScale
{
get
{
return initialScale;
}
set
{
initialScale = value;
}
}
[SerializeField]
private int smoothness;
public int Smoothness
{
get
{
return smoothness;
}
set
{
smoothness = Mathf.Max(2, value);
}
}
[SerializeField]
private float width;
public float Width
{
get
{
return width;
}
set
{
width = Mathf.Max(0, value);
}
}
[SerializeField]
private float falloffWidth;
public float FalloffWidth
{
get
{
return falloffWidth;
}
set
{
falloffWidth = Mathf.Max(0, value);
}
}
[SerializeField]
private GSpline spline;
public GSpline Spline
{
get
{
if (spline == null)
{
spline = new GSpline();
}
return spline;
}
set
{
spline = value;
}
}
#if UNITY_EDITOR
private List<Vector4> vertices;
public List<Vector4> Editor_Vertices
{
get
{
if (vertices == null)
vertices = new List<Vector4>();
return vertices;
}
set
{
vertices = value;
}
}
#endif
public void Reset()
{
PositionOffset = Vector3.up * 5;
InitialRotation = Quaternion.identity;
InitialScale = Vector3.one;
Smoothness = 20;
Width = 1;
FalloffWidth = 1;
}
private void OnEnable()
{
UpdateMeshes();
}
private void OnDisable()
{
CleanUp();
}
public void UpdateMeshes()
{
List<GSplineSegment> segments = Spline.Segments;
for (int i = 0; i < segments.Count; ++i)
{
UpdateMesh(i);
}
}
public void UpdateMesh(int sIndex)
{
List<GSplineSegment> segments = Spline.Segments;
if (sIndex < 0 || sIndex >= segments.Count)
throw new System.IndexOutOfRangeException("segmentIndex is out of range");
GSplineSegment s = segments[sIndex];
List<Vector3> vertices = new List<Vector3>();
List<Color> colors = new List<Color>();
List<int> triangles = new List<int>();
float tStep = 1f / (Smoothness - 1);
for (int tIndex = 0; tIndex < Smoothness - 1; ++tIndex)
{
float t0 = tIndex * tStep;
Vector3 translation0 = Spline.EvaluatePosition(sIndex, t0);
Quaternion rotation0 = Spline.EvaluateRotation(sIndex, t0);
Vector3 scale0 = Spline.EvaluateScale(sIndex, t0);
float t1 = (tIndex + 1) * tStep;
Vector3 translation1 = Spline.EvaluatePosition(sIndex, t1);
Quaternion rotation1 = Spline.EvaluateRotation(sIndex, t1);
Vector3 scale1 = Spline.EvaluateScale(sIndex, t1);
Matrix4x4 matrix0 = Matrix4x4.TRS(translation0, rotation0, scale0);
Matrix4x4 matrix1 = Matrix4x4.TRS(translation1, rotation1, scale1);
Vector3 bl, tl, tr, br;
float halfWidth = Width * 0.5f;
if (FalloffWidth > 0)
{
//Left falloff
bl = matrix0.MultiplyPoint(new Vector3(-halfWidth - FalloffWidth, 0, 0));
tl = matrix1.MultiplyPoint(new Vector3(-halfWidth - FalloffWidth, 0, 0));
tr = matrix1.MultiplyPoint(new Vector3(-halfWidth, 0, 0));
br = matrix0.MultiplyPoint(new Vector3(-halfWidth, 0, 0));
vertices.Add(bl);
vertices.Add(tl);
vertices.Add(tr);
colors.Add(Color.clear);
colors.Add(Color.clear);
colors.Add(Color.white);
vertices.Add(bl);
vertices.Add(tr);
vertices.Add(br);
colors.Add(Color.clear);
colors.Add(Color.white);
colors.Add(Color.white);
}
if (Width > 0)
{
//Center
bl = matrix0.MultiplyPoint(new Vector3(-halfWidth, 0, 0));
tl = matrix1.MultiplyPoint(new Vector3(-halfWidth, 0, 0));
tr = matrix1.MultiplyPoint(new Vector3(halfWidth, 0, 0));
br = matrix0.MultiplyPoint(new Vector3(halfWidth, 0, 0));
vertices.Add(bl);
vertices.Add(tl);
vertices.Add(tr);
colors.Add(Color.white);
colors.Add(Color.white);
colors.Add(Color.white);
vertices.Add(bl);
vertices.Add(tr);
vertices.Add(br);
colors.Add(Color.white);
colors.Add(Color.white);
colors.Add(Color.white);
}
if (FalloffWidth > 0)
{
//Right falloff
bl = matrix0.MultiplyPoint(new Vector3(halfWidth, 0, 0));
tl = matrix1.MultiplyPoint(new Vector3(halfWidth, 0, 0));
tr = matrix1.MultiplyPoint(new Vector3(halfWidth + FalloffWidth, 0, 0));
br = matrix0.MultiplyPoint(new Vector3(halfWidth + FalloffWidth, 0, 0));
vertices.Add(bl);
vertices.Add(tl);
vertices.Add(tr);
colors.Add(Color.white);
colors.Add(Color.white);
colors.Add(Color.clear);
vertices.Add(bl);
vertices.Add(tr);
vertices.Add(br);
colors.Add(Color.white);
colors.Add(Color.clear);
colors.Add(Color.clear);
}
}
Mesh m = s.Mesh;
m.Clear();
m.SetVertices(vertices);
m.SetColors(colors);
m.SetTriangles(GUtilities.GetIndicesArray(vertices.Count), 0);
m.uv = null;
m.normals = null;
m.tangents = null;
}
public void UpdateMesh(IEnumerable<int> indices)
{
IEnumerator<int> i = indices.GetEnumerator();
while (i.MoveNext())
{
UpdateMesh(i.Current);
}
}
public void CleanUp()
{
Spline.Dispose();
}
[System.Obsolete]
public List<Vector4> GenerateVerticesWithFalloff()
{
List<Vector4> vertices = new List<Vector4>();
return vertices;
}
public IEnumerable<Rect> SweepDirtyRect(GStylizedTerrain terrain)
{
if (terrain.TerrainData == null)
return new List<Rect>();
int gridSize = terrain.TerrainData.Geometry.ChunkGridSize;
List<Rect> uvRects = new List<Rect>();
for (int x = 0; x < gridSize; ++x)
{
for (int z = 0; z < gridSize; ++z)
{
uvRects.Add(GCommon.GetUvRange(gridSize, x, z));
}
}
HashSet<Rect> dirtyRects = new HashSet<Rect>();
Vector3 terrainSize = new Vector3(
terrain.TerrainData.Geometry.Width,
terrain.TerrainData.Geometry.Height,
terrain.TerrainData.Geometry.Length);
float splineSize = Mathf.Max(1, Width + FalloffWidth * 2);
Vector2 sweepRectSize = new Vector2(
Mathf.InverseLerp(0, terrainSize.x, splineSize),
Mathf.InverseLerp(0, terrainSize.z, splineSize));
Rect sweepRect = new Rect();
sweepRect.size = sweepRectSize;
int segmentCount = Spline.Segments.Count;
for (int sIndex = 0; sIndex < segmentCount; ++sIndex)
{
float tStep = 1f / (Smoothness - 1);
for (int tIndex = 0; tIndex < Smoothness - 1; ++tIndex)
{
float t = tIndex * tStep;
Vector3 worldPos = transform.TransformPoint(Spline.EvaluatePosition(sIndex, t));
Vector3 scale = transform.TransformVector(Spline.EvaluateScale(sIndex, t));
float maxScaleComponent = Mathf.Max(Mathf.Abs(scale.x), Mathf.Abs(scale.y), Mathf.Abs(scale.z));
Vector3 normalizedPos = terrain.WorldPointToNormalized(worldPos);
sweepRect.center = new Vector2(normalizedPos.x, normalizedPos.z);
sweepRect.size = sweepRectSize * maxScaleComponent;
for (int rIndex = 0; rIndex < uvRects.Count; ++rIndex)
{
if (uvRects[rIndex].Overlaps(sweepRect))
{
dirtyRects.Add(uvRects[rIndex]);
}
}
}
}
return dirtyRects;
}
public bool OverlapTest(GStylizedTerrain terrain)
{
Rect terrainRect = terrain.Rect;
float splineSize = Mathf.Max(1, Width + FalloffWidth * 2);
Vector2 sweepRectSize = Vector2.one * splineSize;
Rect sweepRect = new Rect();
sweepRect.size = sweepRectSize;
int segmentCount = Spline.Segments.Count;
for (int sIndex = 0; sIndex < segmentCount; ++sIndex)
{
float tStep = 1f / (Smoothness - 1);
for (int tIndex = 0; tIndex < Smoothness - 1; ++tIndex)
{
float t = tIndex * tStep;
Vector3 worldPos = Spline.EvaluatePosition(sIndex, t);
Vector3 scale = Spline.EvaluateScale(sIndex, t);
float maxScaleComponent = Mathf.Max(Mathf.Abs(scale.x), Mathf.Abs(scale.y), Mathf.Abs(scale.z));
sweepRect.center = new Vector2(worldPos.x, worldPos.z);
sweepRect.size = sweepRectSize * maxScaleComponent;
if (sweepRect.Overlaps(terrainRect))
{
return true;
}
}
}
return false;
}
public static void MarkSplineChanged(GSplineCreator sender)
{
if (Editor_SplineChanged != null)
{
Editor_SplineChanged.Invoke(sender);
}
}
public void DrawOnTexture(RenderTexture rt, Bounds worldBounds, Material mat)
{
Matrix4x4 worldToNormalized = Matrix4x4.TRS(
worldBounds.min,
Quaternion.identity,
worldBounds.size).inverse;
mat.SetMatrix("_WorldToNormalized", worldToNormalized);
mat.SetPass(0);
RenderTexture.active = rt;
List<GSplineSegment> segments = Spline.Segments;
for (int i = 0; i < segments.Count; ++i)
{
Mesh m = segments[i].Mesh;
Graphics.DrawMeshNow(m, transform.localToWorldMatrix);
}
RenderTexture.active = null;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,241 @@
#if GRIFFIN
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
using Unity.Mathematics;
namespace Pinwheel.Griffin.SplineTool
{
public partial class GSplineCreator : MonoBehaviour
{
public NativeArray<Rect> GenerateSweepRectsNA()
{
NativeArray<GSplineAnchor.GSweepTestData> anchors = Spline.GetAnchorSweepTestData();
NativeArray<GSplineSegment.GSweepTestData> segments = Spline.GetSegmentSweepTestData();
NativeArray<Rect> rects = new NativeArray<Rect>(Smoothness * Spline.Segments.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
GGenerateSweepRectsJob job = new GGenerateSweepRectsJob()
{
anchors = anchors,
segments = segments,
rects = rects,
smoothness = Smoothness,
splineWidth = Width,
splineFalloffWidth = FalloffWidth,
localToWorldMatrix = transform.localToWorldMatrix
};
JobHandle jHandle = job.Schedule(segments.Length, 1);
jHandle.Complete();
anchors.Dispose();
segments.Dispose();
return rects;
}
public List<GOverlapTestResult> SweepTest()
{
List<GOverlapTestResult> results = new List<GOverlapTestResult>();
GCommon.ForEachTerrain(GroupId, (t) =>
{
if (t.TerrainData == null)
return;
GOverlapTestResult r = new GOverlapTestResult();
r.Terrain = t;
results.Add(r);
});
NativeArray<Rect> terrainRects = new NativeArray<Rect>(results.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < terrainRects.Length; ++i)
{
terrainRects[i] = results[i].Terrain.Rect;
}
NativeArray<Rect> splineRects = GenerateSweepRectsNA();
NativeArray<bool> terrainTestResult = new NativeArray<bool>(results.Count, Allocator.TempJob, NativeArrayOptions.ClearMemory);
{
GRectTestJob rectTestJob = new GRectTestJob()
{
rectToTest = terrainRects,
sweepRects = splineRects,
smoothness = Smoothness,
result = terrainTestResult
};
JobHandle jHandle = rectTestJob.Schedule(Spline.Segments.Count, 1);
jHandle.Complete();
}
for (int i = 0; i < results.Count; ++i)
{
GOverlapTestResult r = results[i];
r.IsOverlapped = terrainTestResult[i];
results[i] = r;
}
terrainRects.Dispose();
terrainTestResult.Dispose();
List<JobHandle> chunkTestHandles = new List<JobHandle>();
List<NativeArray<Rect>> chunkRectsHandles = new List<NativeArray<Rect>>();
List<NativeArray<bool>> chunkTestResultsHandles = new List<NativeArray<bool>>();
for (int i = 0; i < results.Count; ++i)
{
GOverlapTestResult r = results[i];
if (!r.IsOverlapped)
{
chunkTestHandles.Add(default);
chunkRectsHandles.Add(default);
chunkTestResultsHandles.Add(default);
continue;
}
NativeArray<Rect> chunkRects = r.Terrain.GetChunkRectsNA();
NativeArray<bool> chunkTestResults = new NativeArray<bool>(chunkRects.Length, Allocator.TempJob, NativeArrayOptions.ClearMemory);
GRectTestJob job = new GRectTestJob()
{
rectToTest = chunkRects,
sweepRects = splineRects,
result = chunkTestResults,
smoothness = Smoothness
};
JobHandle jHandle = job.Schedule(Spline.Segments.Count, 1);
chunkTestHandles.Add(jHandle);
chunkRectsHandles.Add(chunkRects);
chunkTestResultsHandles.Add(chunkTestResults);
}
for (int i = 0; i < results.Count; ++i)
{
GOverlapTestResult r = results[i];
if (!r.IsOverlapped)
{
continue;
}
chunkTestHandles[i].Complete();
r.IsChunkOverlapped = chunkTestResultsHandles[i].ToArray();
results[i] = r;
chunkRectsHandles[i].Dispose();
chunkTestResultsHandles[i].Dispose();
}
splineRects.Dispose();
return results;
}
#if GRIFFIN_BURST
[BurstCompile(CompileSynchronously = false)]
#endif
public struct GGenerateSweepRectsJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<GSplineAnchor.GSweepTestData> anchors;
[ReadOnly]
public NativeArray<GSplineSegment.GSweepTestData> segments;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeArray<Rect> rects;
public int smoothness;
public float splineWidth;
public float splineFalloffWidth;
public Matrix4x4 localToWorldMatrix;
public void Execute(int segmentIndex)
{
float splineSize = Mathf.Max(1, splineWidth + splineFalloffWidth * 2);
Vector2 sweepRectSize = Vector2.one * splineSize * 1.41f;
Rect sweepRect = new Rect();
sweepRect.size = sweepRectSize;
for (int i = 0; i < smoothness; ++i)
{
float t = Mathf.InverseLerp(0, smoothness - 1, i);
Vector3 localPos = EvaluatePosition(segmentIndex, t);
Vector3 worldPos = localToWorldMatrix.MultiplyPoint(localPos);
Vector3 localScale = EvaluateScale(segmentIndex, t);
Vector3 worldScale = localToWorldMatrix.MultiplyVector(localScale);
float maxScaleComponent = Mathf.Max(Mathf.Max(Mathf.Abs(worldScale.x), Mathf.Abs(worldScale.y)), Mathf.Abs(worldScale.z));
sweepRect.center = new Vector2(worldPos.x, worldPos.z);
sweepRect.size = sweepRectSize * maxScaleComponent;
rects[segmentIndex * smoothness + i] = sweepRect;
}
}
public Vector3 EvaluatePosition(int segmentIndex, float t)
{
GSplineSegment.GSweepTestData s = segments[segmentIndex];
GSplineAnchor.GSweepTestData startAnchor = anchors[s.startIndex];
GSplineAnchor.GSweepTestData endAnchor = anchors[s.endIndex];
Vector3 p0 = startAnchor.position;
Vector3 p1 = s.startTangent;
Vector3 p2 = s.endTangent;
Vector3 p3 = endAnchor.position;
t = Mathf.Clamp01(t);
float oneMinusT = 1 - t;
Vector3 p =
oneMinusT * oneMinusT * oneMinusT * p0 +
3 * oneMinusT * oneMinusT * t * p1 +
3 * oneMinusT * t * t * p2 +
t * t * t * p3;
return p;
}
public Vector3 EvaluateScale(int segmentIndex, float t)
{
GSplineSegment.GSweepTestData s = segments[segmentIndex];
GSplineAnchor.GSweepTestData startAnchor = anchors[s.startIndex];
GSplineAnchor.GSweepTestData endAnchor = anchors[s.endIndex];
return Vector3.Lerp(startAnchor.scale, endAnchor.scale, t);
}
}
public struct GRectTestJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<Rect> rectToTest;
[ReadOnly]
public NativeArray<Rect> sweepRects;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeArray<bool> result;
public int smoothness;
public void Execute(int segmentIndex)
{
for (int i = 0; i < rectToTest.Length; ++i)
{
Rect testRect = rectToTest[i];
for (int j = 0; j < smoothness; ++j)
{
Rect sweepRect = sweepRects[segmentIndex * smoothness + j];
if (sweepRect.Overlaps(testRect))
{
result[i] = true;
break;
}
}
}
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 65ebd6df5257b9e4fa1187a13eef663d
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.SplineTool
{
[System.Serializable]
[AddComponentMenu("")]
[RequireComponent(typeof(GSplineCreator))]
public abstract class GSplineModifier : MonoBehaviour
{
[SerializeField]
private GSplineCreator splineCreator;
public GSplineCreator SplineCreator
{
get
{
return splineCreator;
}
set
{
splineCreator = value;
}
}
public abstract void Apply();
}
}
#endif

View File

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

View File

@@ -0,0 +1,120 @@
#if GRIFFIN
using UnityEngine;
using System;
namespace Pinwheel.Griffin.SplineTool
{
[System.Serializable]
public class GSplineSegment : IDisposable
{
[SerializeField]
private int startIndex;
public int StartIndex
{
get
{
return startIndex;
}
set
{
startIndex = value;
}
}
[SerializeField]
private int endIndex;
public int EndIndex
{
get
{
return endIndex;
}
set
{
endIndex = value;
}
}
[SerializeField]
private Vector3 startTangent;
public Vector3 StartTangent
{
get
{
return startTangent;
}
set
{
startTangent = value;
}
}
[SerializeField]
private Vector3 endTangent;
public Vector3 EndTangent
{
get
{
return endTangent;
}
set
{
endTangent = value;
}
}
[SerializeField]
private Mesh mesh;
public Mesh Mesh
{
get
{
if (mesh == null)
{
mesh = new Mesh();
mesh.name = "Spline Segment";
mesh.MarkDynamic();
}
return mesh;
}
}
public GSweepTestData SweepTestData
{
get
{
return new GSweepTestData()
{
startIndex = this.startIndex,
endIndex = this.endIndex,
startTangent = this.startTangent,
endTangent = this.endTangent
};
}
}
public void FlipDirection()
{
int tmp = startIndex;
startIndex = endIndex;
endIndex = tmp;
}
public void Dispose()
{
if (mesh != null)
{
GUtilities.DestroyObject(mesh);
}
}
public struct GSweepTestData
{
public int startIndex;
public int endIndex;
public Vector3 startTangent;
public Vector3 endTangent;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,47 @@
#if GRIFFIN
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Pinwheel.Griffin.SplineTool
{
public static class GSplineToolUtilities
{
private static Material copyTextureMaterial;
private static Material CopyTextureMaterial
{
get
{
if (copyTextureMaterial == null)
{
copyTextureMaterial = new Material(GRuntimeSettings.Instance.internalShaders.copyTextureShader);
}
return copyTextureMaterial;
}
}
public static List<GStylizedTerrain> OverlapTest(int groupId, GSplineCreator spline)
{
List<GStylizedTerrain> terrains = new List<GStylizedTerrain>();
GCommon.ForEachTerrain(groupId, (t) =>
{
if (spline.OverlapTest(t))
{
terrains.Add(t);
}
});
return terrains;
}
public static void CopyTexture(Texture src, RenderTexture dst)
{
CopyTextureMaterial.SetVector("_StartUV", Vector4.zero);
CopyTextureMaterial.SetVector("_EndUV", Vector4.one);
CopyTextureMaterial.SetColor("_DefaultColor", Color.black);
CopyTextureMaterial.SetTexture("_MainTex", src);
GCommon.DrawQuad(dst, GCommon.FullRectUvPoints, CopyTextureMaterial, 0);
}
}
}
#endif

View File

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