1
This commit is contained in:
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd27750a10fa218439a41930340f667e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e9a4232228dd4f4480b2f5292116c03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 477f6433bcaf6d54287a4619712eb326
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 492182ded4bf3ca4a8e1877a2a4b2715
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15ec7fdd853ca5641b6efcfecbefe1d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d05c440aa2a3b5479029d9e6ff291fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e0e6e19142609a47af208887b94f58a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fa5a6db87b870a4b94b58314fd876b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 137ab8e8e9b1f14428f8759c2ee12475
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65ebd6df5257b9e4fa1187a13eef663d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb26e124481e563458097017aa315e1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 623d253a1e88a1d419ad5935c977c2e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37cca031af1a9d14b814c67f979bd38a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user