Files
Fishing2/Packages/com.jbooth.microverse/Scripts/Editor/CopyPasteStampEditor.cs
2025-06-09 23:23:13 +08:00

760 lines
35 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using Unity.Mathematics;
using Unity.Collections;
namespace JBooth.MicroVerseCore
{
[CustomEditor(typeof(CopyPasteStamp), true)]
[CanEditMultipleObjects]
class CopyPasteStampEditor : Editor
{
static Vector3 WorldToTerrain(Terrain terrain, Vector3 worldPos)
{
Vector3 ret = new Vector3();
Vector3 terPosition = terrain.transform.position;
ret.x = ((worldPos.x - terPosition.x) / terrain.terrainData.size.x);
ret.z = ((worldPos.z - terPosition.z) / terrain.terrainData.size.z);
return ret;
}
static void GenerateMega(Terrain terrain, out RenderTexture indexes, out RenderTexture weights)
{
Material mat = new Material(Shader.Find("Hidden/MicroVerse/SplatToMega"));
indexes = RenderTexture.GetTemporary(terrain.terrainData.alphamapWidth, terrain.terrainData.alphamapHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
weights = RenderTexture.GetTemporary(terrain.terrainData.alphamapWidth, terrain.terrainData.alphamapHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
indexes.name = "GenerateMega::indexes";
weights.name = "GenerateMega::weights";
indexes.filterMode = FilterMode.Point;
indexes.wrapMode = TextureWrapMode.Clamp;
weights.wrapMode = TextureWrapMode.Clamp;
// splats
Texture2D[] splatMaps = terrain.terrainData.alphamapTextures;
int count = terrain.terrainData.alphamapTextureCount;
for (int i = 0; i < terrain.terrainData.alphamapTextureCount; ++i)
{
mat.SetTexture("_Control" + i, splatMaps[i]);
}
if (count > 7) { mat.EnableKeyword("_MAX32TEXTURES"); }
else if (count > 6) { mat.EnableKeyword("_MAX28TEXTURES"); }
else if (count > 5) { mat.EnableKeyword("_MAX24TEXTURES"); }
else if (count > 4) { mat.EnableKeyword("_MAX20TEXTURES"); }
else if (count > 3) { mat.EnableKeyword("_MAX16TEXTURES"); }
else if (count > 2) { mat.EnableKeyword("_MAX12TEXTURES"); }
else if (count > 1) { mat.EnableKeyword("_MAX8TEXTURES"); }
else { mat.EnableKeyword("_MAX4TEXTURES"); }
RenderBuffer[] _mrt = new RenderBuffer[2];
_mrt[0] = indexes.colorBuffer;
_mrt[1] = weights.colorBuffer;
Graphics.SetRenderTarget(_mrt, indexes.depthBuffer);
Graphics.Blit(null, mat, 0);
RenderTexture.active = null;
DestroyImmediate(mat);
}
static int FindIndex(TreePrototype proto, List<TreePrototypeSerializable> protos, TreePrototype[] terrainProtos)
{
for (int i = 0; i < terrainProtos.Length; ++i)
{
var p = terrainProtos[i];
if (proto.prefab == p.prefab)
{
for (int x = 0; x < protos.Count; ++x)
{
if (protos[x].prefab == proto.prefab)
return x;
}
}
}
return -1;
}
public static CopyStamp.TreeCopyData CaptureTrees(Terrain[] terrains, Bounds bounds, Transform trans)
{
var culledInstances = new List<TreeInstance>(1024);
List<TreePrototypeSerializable> prototypes = new List<TreePrototypeSerializable>();
foreach (var t in terrains)
{
var tb = TerrainUtil.ComputeTerrainBounds(t);
if (tb.Intersects(bounds))
{
// add any missing prototypes
var terrainProtos = t.terrainData.treePrototypes;
foreach (var proto in terrainProtos)
{
var tps = new TreePrototypeSerializable(proto);
if (!prototypes.Contains(tps))
{
prototypes.Add(tps);
}
}
Vector2 center = new Vector2(bounds.center.x, bounds.center.z);
center.x -= t.transform.position.x;
center.y -= t.transform.position.z;
center.x /= t.terrainData.size.x;
center.y /= t.terrainData.size.z;
Vector2 range = new Vector2(bounds.size.x, bounds.size.z);
range.x /= t.terrainData.size.x;
range.y /= t.terrainData.size.z;
center.x -= range.x / 2;
center.y -= range.y / 2;
Rect cellRect = new Rect(center, range);
var instances = t.terrainData.treeInstances;
for (int x = 0; x < instances.Length; ++x)
{
var i = instances[x];
if (cellRect.Contains(new Vector2(i.position.x, i.position.z)))
{
i.position.x -= cellRect.xMin;
i.position.z -= cellRect.yMin;
i.position.x *= 1.0f / cellRect.size.x;
i.position.z *= 1.0f / cellRect.size.y;
i.prototypeIndex = FindIndex(terrainProtos[i.prototypeIndex], prototypes, terrainProtos);
culledInstances.Add(i);
}
}
}
}
int count = culledInstances.Count;
int yCount = count / 512 + 1;
var posTex = new Texture2D(512, yCount, TextureFormat.RGBAHalf, false, true);
var randTex = new Texture2D(512, yCount, TextureFormat.RGBAHalf, false, true);
for (int x = 0; x < culledInstances.Count; ++x)
{
var i = culledInstances[x];
Color pos = new Color();
Color rand = new Color();
pos.r = i.position.x;
pos.g = i.position.y;
pos.b = i.position.z;
pos.a = 1;
rand.r = i.prototypeIndex;
rand.g = i.heightScale;
rand.b = i.widthScale;
rand.a = i.rotation;
int xp = x % 512;
int yp = x / 512;
posTex.SetPixel(xp, yp, pos);
randTex.SetPixel(xp, yp, rand);
}
for (int x = culledInstances.Count; x < 512 * yCount; ++x)
{
int xp = x % 512;
int yp = x / 512;
posTex.SetPixel(xp, yp, Color.clear);
randTex.SetPixel(xp, yp, Color.clear);
}
posTex.Apply(false, false);
randTex.Apply(false, false);
CopyStamp.TreeCopyData treeData = new CopyStamp.TreeCopyData();
treeData.prototypes = prototypes.ToArray();
treeData.positionsData = posTex.GetRawTextureData(); ;
treeData.randomsData = randTex.GetRawTextureData();
treeData.dataSize = new Vector2Int(posTex.width, posTex.height);
return treeData;
}
public static CopyStamp.DetailCopyData CaptureDetails(Terrain[] terrains, Bounds bounds, Transform trans)
{
CopyStamp.DetailCopyData cd = new CopyStamp.DetailCopyData();
Dictionary<DetailPrototypeSerializable, RenderTexture> bufferMap = new Dictionary<DetailPrototypeSerializable, RenderTexture>();
Material copyMat = new Material(Shader.Find("Hidden/MicroVerse/CopyStamp"));
foreach (var t in terrains)
{
var tb = TerrainUtil.ComputeTerrainBounds(t);
if (tb.Intersects(bounds))
{
var terrainProtos = t.terrainData.detailPrototypes;
for (int protoIdx = 0; protoIdx < terrainProtos.Length; ++protoIdx)
{
var proto = terrainProtos[protoIdx];
var dps = new DetailPrototypeSerializable(proto);
int res = t.terrainData.detailResolution;
int[,] data = t.terrainData.GetDetailLayer(0, 0, res, res, protoIdx);
// Due to terrible unity API, hack
// directly copy into byte[], then into texture,
// then have the shader just read the 8 bytes is uses
// of the R8 which this data actually represents. Fuckin Unity..
byte[] bytes = new byte[data.Length * sizeof(int)];
System.Buffer.BlockCopy(data, 0, bytes, 0, bytes.Length);
Texture2D dataTex = new Texture2D(res, res, TextureFormat.RGBA32, false, true);
dataTex.LoadRawTextureData(bytes);
dataTex.Apply(false, false);
// now we can just sample the R channel for the data to make the stamp
var scale = trans.lossyScale;
float fPixelsX = scale.x * t.terrainData.detailResolution / t.terrainData.size.x;
float fPixelsY = scale.z * t.terrainData.detailResolution / t.terrainData.size.z;
int pixelsX = Mathf.FloorToInt(fPixelsX);
int pixelsY = Mathf.FloorToInt(fPixelsY);
Vector3 ourPos = WorldToTerrain(t, trans.position);
Vector2 uv = new Vector2(ourPos.x, ourPos.z);
copyMat.SetVector("_UVCenter", uv);
RenderTexture old = null;
if (bufferMap.ContainsKey(dps))
{
old = bufferMap[dps];
}
else
{
old = RenderTexture.GetTemporary(pixelsX, pixelsY, 0, UnityEngine.Experimental.Rendering.GraphicsFormat.R8_UNorm);
bufferMap[dps] = old;
old.name = "CopyPasteStampEditor::DetailRT";
old.wrapMode = TextureWrapMode.Clamp;
RenderTexture.active = old;
GL.Clear(false, true, Color.clear);
}
var tempRT = RenderTexture.GetTemporary(old.descriptor);
copyMat.SetVector("_UVRange", new Vector2(fPixelsX, fPixelsY) / new Vector2(res*2, res*2));
copyMat.SetTexture("_CurrentBuffer", old);
copyMat.SetTexture("_Source", dataTex);
Graphics.Blit(null, tempRT, copyMat);
bufferMap[dps] = tempRT;
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(old);
}
}
}
DestroyImmediate(copyMat);
foreach (var key in bufferMap.Keys)
{
var buffer = bufferMap[key];
CopyStamp.DetailCopyData.Layer layer = new CopyStamp.DetailCopyData.Layer();
layer.prototype = key;
var tex = new Texture2D(buffer.width, buffer.height, TextureFormat.R8, false, true);
RenderTexture.active = buffer;
tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
RenderTexture.active = null;
tex.Apply(false, true);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(buffer);
layer.dataSize = new Vector2Int(tex.width, tex.height);
layer.bytes = tex.GetRawTextureData();
cd.layers.Add(layer);
}
return cd;
}
public static void Capture(CopyPasteStamp cpStamp, string path)
{
MicroVerse.instance.SyncTerrainList();
Material copyMat = new Material(Shader.Find("Hidden/MicroVerse/CopyStamp"));
var terrains = MicroVerse.instance.terrains;
RenderTexture heightBuffer = null;
RenderTexture indexBuffer = null;
RenderTexture weightBuffer = null;
RenderTexture holeBuffer = null;
Vector2 renorm = new Vector2(0, 1);
List<TerrainLayer> layers = new List<TerrainLayer>();
var origTerrainScale = cpStamp.transform.localScale;
foreach (var terrain in terrains)
{
var tl = terrain.terrainData.terrainLayers;
// height stamp
{
CopyPasteStamp.SetTerrainScale(cpStamp.transform, terrain, terrain.terrainData.heightmapResolution);
var cpBounds = cpStamp.GetBounds();
if (cpBounds.Intersects(TerrainUtil.ComputeTerrainBounds(terrain)))
{
Vector3 ourPos = WorldToTerrain(terrain, cpStamp.transform.position);
Vector2 uv = new Vector2(ourPos.x, ourPos.z);
copyMat.SetVector("_UVCenter", uv);
var scale = cpStamp.transform.lossyScale;
float realSize = terrain.terrainData.heightmapScale.y * 2.0f;
renorm.y = realSize / scale.y;
renorm.x = cpStamp.transform.position.y / realSize;
float fPixelsX = scale.x * terrain.terrainData.heightmapResolution / terrain.terrainData.size.x;
float fPixelsY = scale.z * terrain.terrainData.heightmapResolution / terrain.terrainData.size.z;
int pixelsX = Mathf.FloorToInt(fPixelsX);
int pixelsY = Mathf.FloorToInt(fPixelsY);
RenderTexture heightSource = terrain.terrainData.heightmapTexture;
if (heightBuffer == null)
{
var desc = heightSource.descriptor;
desc.width = pixelsX;
desc.height = pixelsY;
heightBuffer = RenderTexture.GetTemporary(desc);
heightBuffer.name = "CopyPasteStampEditor::heights";
heightBuffer.wrapMode = TextureWrapMode.Clamp;
heightBuffer.filterMode = FilterMode.Point;
RenderTexture.active = heightBuffer;
GL.Clear(false, true, Color.clear);
}
RenderTexture heightTemp = RenderTexture.GetTemporary(heightBuffer.descriptor);
heightTemp.name = "CopyPasteStampEditor::heightTemp";
Graphics.Blit(heightBuffer, heightTemp);
heightTemp.wrapMode = TextureWrapMode.Clamp;
copyMat.SetVector("_UVRange", new Vector2(fPixelsX, fPixelsY) / new Vector2(heightSource.height * 2, heightSource.width * 2));
copyMat.SetTexture("_CurrentBuffer", heightTemp);
copyMat.SetTexture("_Source", heightSource);
copyMat.EnableKeyword("_COPYHEIGHT");
copyMat.SetFloat("_YOffset", (cpStamp.transform.position.y - terrain.GetPosition().y) / realSize);
Graphics.Blit(heightSource, heightBuffer, copyMat);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(heightTemp);
copyMat.DisableKeyword("_COPYHEIGHT");
}
cpStamp.transform.localScale = origTerrainScale;
}
// holes
{
CopyPasteStamp.SetTerrainScale(cpStamp.transform, terrain, terrain.terrainData.holesResolution);
var cpBounds = cpStamp.GetBounds();
if (cpBounds.Intersects(TerrainUtil.ComputeTerrainBounds(terrain)))
{
Texture holeSource = terrain.terrainData.holesTexture;
Vector3 ourPos = WorldToTerrain(terrain, cpStamp.transform.position);
Vector2 uv = new Vector2(ourPos.x, ourPos.z);
copyMat.SetVector("_UVCenter", uv);
var scale = cpStamp.transform.lossyScale;
float realSize = terrain.terrainData.heightmapScale.y * 2.0f;
float fHolePixelsX = scale.x * terrain.terrainData.holesResolution / terrain.terrainData.size.x;
float fHolePixelsY = scale.z * terrain.terrainData.holesResolution / terrain.terrainData.size.z;
int holePixelsX = Mathf.FloorToInt(fHolePixelsX);
int holePixelsY = Mathf.FloorToInt(fHolePixelsY);
if (holeBuffer == null)
{
holeBuffer = RenderTexture.GetTemporary(holePixelsX, holePixelsY, 0, UnityEngine.Experimental.Rendering.GraphicsFormat.R8_UNorm);
holeBuffer.name = "CopyPasteStampEditor::holeBuffer";
holeBuffer.wrapMode = TextureWrapMode.Clamp;
holeBuffer.filterMode = FilterMode.Point;
RenderTexture.active = holeBuffer;
GL.Clear(false, true, Color.white);
}
RenderTexture holeTemp = RenderTexture.GetTemporary(holeBuffer.descriptor);
holeTemp.wrapMode = TextureWrapMode.Clamp;
holeTemp.filterMode = FilterMode.Point;
Graphics.Blit(holeBuffer, holeTemp);
copyMat.SetVector("_UVRange", new Vector2(fHolePixelsX, fHolePixelsY) / new Vector2(holeSource.width * 2, holeSource.height * 2));
copyMat.SetTexture("_CurrentBuffer", holeTemp);
copyMat.SetTexture("_Source", holeSource);
Graphics.Blit(holeSource, holeBuffer, copyMat);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(holeTemp);
}
cpStamp.transform.localScale = origTerrainScale;
}
// splat
{
foreach (var tlr in tl)
{
if (!layers.Contains(tlr))
{
layers.Add(tlr);
}
}
CopyPasteStamp.SetTerrainScale(cpStamp.transform, terrain, terrain.terrainData.alphamapHeight);
var cpBounds = cpStamp.GetBounds();
if (cpBounds.Intersects(TerrainUtil.ComputeTerrainBounds(terrain)))
{
Vector3 ourPos = WorldToTerrain(terrain, cpStamp.transform.position);
Vector2 uv = new Vector2(ourPos.x, ourPos.z);
copyMat.SetVector("_UVCenter", uv);
var scale = cpStamp.transform.lossyScale;
float realSize = terrain.terrainData.heightmapScale.y * 2.0f;
float fSplatPixelsX = scale.x * terrain.terrainData.alphamapResolution / terrain.terrainData.size.x;
float fSplatPixelsY = scale.z * terrain.terrainData.alphamapResolution / terrain.terrainData.size.z;
int splatPixelsX = Mathf.FloorToInt(fSplatPixelsX);
int splatPixelsY = Mathf.FloorToInt(fSplatPixelsY);
int splatRes = terrain.terrainData.alphamapResolution;
if (weightBuffer == null)
{
weightBuffer = RenderTexture.GetTemporary(splatPixelsX, splatPixelsY, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
heightBuffer.name = "CopyPasteStampEditor::weightBuffer";
weightBuffer.wrapMode = TextureWrapMode.Clamp;
weightBuffer.filterMode = FilterMode.Point;
RenderTexture.active = weightBuffer;
GL.Clear(false, true, Color.clear);
}
if (indexBuffer == null)
{
indexBuffer = RenderTexture.GetTemporary(splatPixelsX, splatPixelsY, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
heightBuffer.name = "CopyPasteStampEditor::indexBuffer";
indexBuffer.wrapMode = TextureWrapMode.Clamp;
indexBuffer.filterMode = FilterMode.Point;
RenderTexture.active = indexBuffer;
GL.Clear(false, true, Color.clear);
}
RenderTexture indexSource;
RenderTexture weightSource;
GenerateMega(terrain, out indexSource, out weightSource);
RenderTexture indexTemp = RenderTexture.GetTemporary(indexBuffer.descriptor);
indexTemp.name = "CopyPasteStampEditor::indexTemp";
indexTemp.wrapMode = TextureWrapMode.Clamp;
indexTemp.filterMode = FilterMode.Point;
Graphics.Blit(indexBuffer, indexTemp);
copyMat.SetVector("_UVRange", new Vector2(fSplatPixelsX, fSplatPixelsY) / new Vector2(indexSource.width * 2, indexSource.height * 2));
copyMat.SetTexture("_CurrentBuffer", indexTemp);
copyMat.SetTexture("_Source", indexSource);
Graphics.Blit(indexSource, indexBuffer, copyMat);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(indexTemp);
RenderTexture weightTemp = RenderTexture.GetTemporary(weightBuffer.descriptor);
weightTemp.wrapMode = TextureWrapMode.Clamp;
weightTemp.filterMode = FilterMode.Point;
Graphics.Blit(weightBuffer, weightTemp);
copyMat.SetVector("_UVRange", new Vector2(fSplatPixelsX, fSplatPixelsY) / new Vector2(weightSource.width * 2, weightSource.height * 2));
copyMat.SetTexture("_CurrentBuffer", weightTemp);
copyMat.SetTexture("_Source", weightSource);
Graphics.Blit(weightSource, weightBuffer, copyMat);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(weightTemp);
}
cpStamp.transform.localScale = origTerrainScale;
}
}
Texture2D heightTex = null;
if (cpStamp.copyHeights)
{
heightTex = new Texture2D(heightBuffer.width, heightBuffer.height, TextureFormat.R16, false, true);
heightTex.wrapMode = TextureWrapMode.Clamp;
RenderTexture.active = heightBuffer;
heightTex.ReadPixels(new Rect(0, 0, heightTex.width, heightTex.height), 0, 0);
RenderTexture.active = null;
heightTex.Apply(false, true);
}
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(heightBuffer);
Texture2D weightTex = null;
Texture2D indexTex = null;
Texture2D holeTex = null;
TerrainLayer[] terrainLayers = null;
if (cpStamp.copyTexturing)
{
weightTex = new Texture2D(weightBuffer.width, weightBuffer.height, TextureFormat.ARGB32, false, true);
RenderTexture.active = weightBuffer;
weightTex.ReadPixels(new Rect(0, 0, weightBuffer.width, weightBuffer.height), 0, 0);
RenderTexture.active = null;
weightTex.Apply(false, true);
indexTex = new Texture2D(indexBuffer.width, indexBuffer.height, TextureFormat.ARGB32, false, true);
RenderTexture.active = indexBuffer;
indexTex.ReadPixels(new Rect(0, 0, indexBuffer.width, indexBuffer.height), 0, 0);
RenderTexture.active = null;
indexTex.Apply(false, true);
terrainLayers = layers.Distinct().ToArray();
}
if (cpStamp.copyHoles)
{
holeTex = new Texture2D(holeBuffer.width, holeBuffer.height, TextureFormat.R8, false, true);
RenderTexture.active = holeBuffer;
holeTex.ReadPixels(new Rect(0, 0, holeBuffer.width, holeBuffer.height), 0, 0);
RenderTexture.active = null;
holeTex.Apply(false, true);
}
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(indexBuffer);
RenderTexture.ReleaseTemporary(weightBuffer);
CopyStamp.TreeCopyData tcd = null;
CopyStamp.DetailCopyData dcd = null;
if (cpStamp.copyTrees)
{
tcd = CaptureTrees(terrains, cpStamp.GetBounds(), cpStamp.transform);
}
if (cpStamp.copyDetails)
{
dcd = CaptureDetails(terrains, cpStamp.GetBounds(), cpStamp.transform);
}
if (cpStamp.stamp == null)
{
var cp = CopyStamp.Create(heightTex, indexTex, weightTex, holeTex, terrainLayers, renorm, tcd,
dcd);
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
if (!path.EndsWith(".asset"))
path += ".asset";
path = path.Replace("..", ".");
AssetDatabase.CreateAsset(cp, AssetDatabase.GenerateUniqueAssetPath(path));
cpStamp.stamp = AssetDatabase.LoadAssetAtPath<CopyStamp>(path);
}
else
{
Undo.RecordObject(cpStamp.stamp, "Update CopyStamp Data");
cpStamp.stamp.layers = terrainLayers;
cpStamp.stamp.heightRenorm = renorm;
cpStamp.stamp.heightData = heightTex != null ? heightTex.GetRawTextureData() : null;
cpStamp.stamp.indexData = indexTex != null ? indexTex.GetRawTextureData() : null;
cpStamp.stamp.weightData = weightTex != null ? weightTex.GetRawTextureData() : null;
cpStamp.stamp.holeData = holeTex != null ? holeTex.GetRawTextureData() : null;
if (heightTex != null) cpStamp.stamp.heightSize = new Vector2Int(heightTex.width, heightTex.height);
if (indexTex != null && weightTex != null) cpStamp.stamp.indexWeightSize = new Vector2Int(indexTex.width, indexTex.height);
if (holeTex != null) cpStamp.stamp.holeSize = new Vector2Int(holeTex.width, holeTex.height);
cpStamp.stamp.treeData = tcd;
cpStamp.stamp.detailData = dcd;
// Wipe the unpacked values so that Unpack can do its work
cpStamp.stamp.heightMap = null;
cpStamp.stamp.indexMap = null;
cpStamp.stamp.weightMap = null;
cpStamp.stamp.holeMap = null;
EditorUtility.SetDirty(cpStamp.stamp);
}
}
public static string lastDir = "Assets/";
public override void OnInspectorGUI()
{
GUIUtil.DrawHeaderLogo();
serializedObject.Update();
var cpStamp = (CopyPasteStamp)target;
if (cpStamp.GetComponentInParent<MicroVerse>() == null)
{
EditorGUILayout.HelpBox("Stamp is not under MicroVerse in the hierarchy, will have no effect", MessageType.Warning);
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("copyHeights"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("copyTexturing"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("copyHoles"));
#if __MICROVERSE_VEGETATION__
EditorGUILayout.PropertyField(serializedObject.FindProperty("copyTrees"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("copyDetails"));
#endif
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serializedObject.FindProperty("pixelQuantization"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("applyHeights"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("applyTexturing"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("applyHoles"));
#if __MICROVERSE_VEGETATION__
EditorGUILayout.PropertyField(serializedObject.FindProperty("applyTrees"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("applyDetails"));
#endif
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
MicroVerse.instance?.Invalidate(cpStamp.GetBounds());
}
GUIUtil.DrawSeparator();
if (GUILayout.Button("Create New Copy Object"))
{
var path = EditorUtility.SaveFilePanel("Save Copy Stamp", lastDir, "CopyStamp", "asset");
if (string.IsNullOrEmpty(path))
{
return;
}
path = path.Replace("\\", "/");
path = path.Substring(path.IndexOf("/Assets") + 1);
lastDir = path.Substring(0, path.LastIndexOf("/"));
Capture(cpStamp, path);
}
if (cpStamp.stamp != null)
{
if (GUILayout.Button("Re-Copy"))
{
var path = AssetDatabase.GetAssetPath(cpStamp.stamp);
if (string.IsNullOrEmpty(path))
{
return;
}
path = path.Replace("\\", "/");
path = path.Substring(path.IndexOf("/Assets") + 1);
Capture(cpStamp, path);
}
}
if (MicroVerse.instance != null)
{
if (MicroVerse.instance.enabled)
{
if (GUILayout.Button("Disable MicroVerse"))
{
MicroVerse.instance.enabled = false;
}
}
else if (GUILayout.Button("Enable MicroVerse"))
{
MicroVerse.instance.enabled = true;
}
}
GUIUtil.DrawSeparator();
using var changeScope = new EditorGUI.ChangeCheckScope();
EditorGUILayout.PropertyField(serializedObject.FindProperty("stamp"));
if (cpStamp.heightStamp != null)
{
SerializedObject hso = new SerializedObject(cpStamp.heightStamp);
hso.Update();
EditorGUILayout.PropertyField(hso.FindProperty("mode"));
hso.ApplyModifiedProperties();
GUIUtil.DrawFalloffFilter(cpStamp.heightStamp, cpStamp.heightStamp.falloff, cpStamp.transform, false);
serializedObject.ApplyModifiedProperties();
}
if (changeScope.changed)
{
MicroVerse.instance?.Invalidate(cpStamp.GetBounds());
}
}
private void OnSceneGUI()
{
var stamp = (CopyPasteStamp)target;
if (stamp.heightStamp.falloff.filterType == FalloffFilter.FilterType.PaintMask)
{
GUIUtil.DoPaintSceneView(stamp, SceneView.currentDrawingSceneView, stamp.heightStamp.falloff.paintMask, stamp.GetBounds(), stamp.transform);
}
}
private void OnEnable()
{
EditorApplication.update += OnUpdate;
SceneView.duringSceneGui += OnSceneRepaint;
}
private void OnDisable()
{
EditorApplication.update -= OnUpdate;
SceneView.duringSceneGui -= OnSceneRepaint;
}
static Texture2D overlayTexCopy = null;
static Texture2D overlayTexPaste = null;
private void OnSceneRepaint(SceneView sceneView)
{
RenderTexture.active = sceneView.camera.activeTexture;
if (MicroVerse.instance != null)
{
if (overlayTexCopy == null)
{
overlayTexCopy = Resources.Load<Texture2D>("microverse_stamp_copy");
}
if (overlayTexPaste == null)
{
overlayTexPaste = Resources.Load<Texture2D>("microverse_stamp_paste");
}
var terrains = MicroVerse.instance.terrains;
var cp = (target as CopyPasteStamp);
if (cp == null) return;
if (cp.heightStamp == null) return;
Color color = Color.gray;
if (MicroVerse.instance != null)
{
color = MicroVerse.instance.options.colors.copyStampColor;
}
PreviewRenderer.DrawStampPreview(cp, terrains, cp.transform, cp.heightStamp.falloff, color, cp.stamp == null ? overlayTexCopy : overlayTexPaste);
}
}
class CachedTransform
{
public Vector3 pos;
public Quaternion quat;
public Vector3 scale;
public void Capture(Transform t)
{
pos = t.position;
quat = t.rotation;
scale = t.lossyScale;
}
public bool Compare(Transform t)
{
if (t.position != pos) return false;
if (t.rotation != quat) return false;
if (t.localScale != t.lossyScale) return false;
return true;
}
}
Dictionary<CopyPasteStamp, CachedTransform> cachedTransforms = new Dictionary<CopyPasteStamp, CachedTransform>();
private void OnUpdate()
{
foreach (var target in targets)
{
if (target == null)
continue;
var stamp = (CopyPasteStamp)target;
if (stamp.stamp == null)
continue;
if (!cachedTransforms.ContainsKey(stamp))
{
CachedTransform t = new CachedTransform();
t.Capture(stamp.transform);
cachedTransforms[stamp] = t;
}
var ct = cachedTransforms[stamp];
if (!ct.Compare(stamp.transform))
{
ct.Capture(stamp.transform);
var r = stamp.transform.localRotation.eulerAngles;
r.x = 0;
r.z = 0;
stamp.transform.localRotation = Quaternion.Euler(r);
stamp.GetComponentInParent<MicroVerse>()?.Invalidate(stamp.GetBounds());
}
}
}
}
}