summaryrefslogtreecommitdiffstats
path: root/Scripts/Editor
diff options
context:
space:
mode:
Diffstat (limited to 'Scripts/Editor')
-rw-r--r--Scripts/Editor/GenerateNoise.cs210
1 files changed, 210 insertions, 0 deletions
diff --git a/Scripts/Editor/GenerateNoise.cs b/Scripts/Editor/GenerateNoise.cs
new file mode 100644
index 0000000..324b5b5
--- /dev/null
+++ b/Scripts/Editor/GenerateNoise.cs
@@ -0,0 +1,210 @@
+using UnityEngine;
+using UnityEditor;
+
+public class GenerateNoise : EditorWindow
+{
+ enum NoiseType { WhiteNoise, WorleyNoise, WorleyEdgeSDF }
+ enum DistanceMetric { L1, L2, LInfinity }
+
+ NoiseType _type = NoiseType.WhiteNoise;
+ int _resolution = 32;
+ int _texelsPerCell = 8;
+ DistanceMetric _metric = DistanceMetric.L2;
+ float _sdfRadius = 0.5f;
+
+ [MenuItem("Tools/yum_food/Generate 3D Noise")]
+ static void Open() => GetWindow<GenerateNoise>("Generate 3D Noise");
+
+ void OnGUI()
+ {
+ EditorGUILayout.LabelField("Noise Settings", EditorStyles.boldLabel);
+ _type = (NoiseType)EditorGUILayout.EnumPopup("Type", _type);
+ if (_type == NoiseType.WhiteNoise)
+ {
+ _resolution = EditorGUILayout.IntField("Resolution", _resolution);
+ _resolution = Mathf.Clamp(_resolution, 2, 512);
+ }
+ else
+ {
+ _resolution = EditorGUILayout.IntField("Cell Count", _resolution);
+ _resolution = Mathf.Clamp(_resolution, 2, 512);
+ _texelsPerCell = EditorGUILayout.IntSlider("Texels Per Cell", _texelsPerCell, 2, 64);
+ _metric = (DistanceMetric)EditorGUILayout.EnumPopup("Distance Metric", _metric);
+ if (_type == NoiseType.WorleyEdgeSDF)
+ _sdfRadius = EditorGUILayout.Slider("SDF Radius (cells)", _sdfRadius, 0.1f, 2f);
+ EditorGUILayout.HelpBox($"Texture resolution: {_resolution * _texelsPerCell}\u00b3", MessageType.None);
+ }
+
+ EditorGUILayout.Space();
+
+ if (GUILayout.Button("Generate"))
+ DoGenerate();
+ }
+
+ void DoGenerate()
+ {
+ int res = _type == NoiseType.WhiteNoise ? _resolution : _resolution * _texelsPerCell;
+ Texture3D tex;
+
+ switch (_type)
+ {
+ case NoiseType.WorleyNoise:
+ {
+ tex = new Texture3D(res, res, res, TextureFormat.R8, false);
+ GenerateWorleyField(res, _texelsPerCell, _metric, out var f1, out _);
+ var pixels = new byte[res * res * res];
+ for (int i = 0; i < pixels.Length; i++)
+ pixels[i] = (byte)Mathf.RoundToInt(f1[i] * 255f);
+ tex.SetPixelData(pixels, 0);
+ break;
+ }
+ case NoiseType.WorleyEdgeSDF:
+ {
+ tex = new Texture3D(res, res, res, TextureFormat.R8, false);
+ GenerateWorleyField(res, _texelsPerCell, _metric, out var f1, out var f2);
+ var pixels = new byte[res * res * res];
+ GenerateEdgeSDF(pixels, f1, f2, _sdfRadius);
+ tex.SetPixelData(pixels, 0);
+ break;
+ }
+ default:
+ {
+ tex = new Texture3D(res, res, res, TextureFormat.RGBA32, false);
+ var pixels = new Color32[res * res * res];
+ GenerateWhiteNoise(pixels);
+ tex.SetPixels32(pixels);
+ break;
+ }
+ }
+
+ tex.wrapMode = TextureWrapMode.Repeat;
+ tex.filterMode = FilterMode.Bilinear;
+ tex.Apply();
+
+ string name;
+ switch (_type)
+ {
+ case NoiseType.WorleyNoise: name = $"WorleyNoise3D_{_metric}_{res}"; break;
+ case NoiseType.WorleyEdgeSDF: name = $"WorleyEdgeSDF3D_{_metric}_{res}"; break;
+ default: name = $"WhiteNoise3D_{res}"; break;
+ }
+
+ string path = $"Assets/yum_food/3ner/Textures/{name}.asset";
+ if (!AssetDatabase.IsValidFolder("Assets/yum_food/3ner/Textures"))
+ AssetDatabase.CreateFolder("Assets/yum_food/3ner", "Textures");
+
+ AssetDatabase.CreateAsset(tex, path);
+ AssetDatabase.SaveAssets();
+ EditorGUIUtility.PingObject(tex);
+ Debug.Log($"[GenerateNoise] Saved {_type} ({res}\u00b3) to {path}");
+ }
+
+ static void GenerateWhiteNoise(Color32[] pixels)
+ {
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ byte r = (byte)Random.Range(0, 256);
+ byte g = (byte)Random.Range(0, 256);
+ byte b = (byte)Random.Range(0, 256);
+ byte a = (byte)Random.Range(0, 256);
+ pixels[i] = new Color32(r, g, b, a);
+ }
+ }
+
+ static float Distance(Vector3 a, Vector3 b, DistanceMetric metric)
+ {
+ float dx = Mathf.Abs(a.x - b.x);
+ float dy = Mathf.Abs(a.y - b.y);
+ float dz = Mathf.Abs(a.z - b.z);
+ switch (metric)
+ {
+ case DistanceMetric.L1: return dx + dy + dz;
+ case DistanceMetric.LInfinity: return Mathf.Max(dx, Mathf.Max(dy, dz));
+ default: return Mathf.Sqrt(dx * dx + dy * dy + dz * dz);
+ }
+ }
+
+ static void GenerateWorleyField(int res, int texelsPerCell, DistanceMetric metric,
+ out float[] f1, out float[] f2)
+ {
+ int cellCount = Mathf.Max(1, res / texelsPerCell);
+ float cellSize = (float)res / cellCount;
+
+ var points = new Vector3[cellCount * cellCount * cellCount];
+ for (int cz = 0; cz < cellCount; cz++)
+ for (int cy = 0; cy < cellCount; cy++)
+ for (int cx = 0; cx < cellCount; cx++)
+ {
+ int ci = cx + cellCount * (cy + cellCount * cz);
+ points[ci] = new Vector3(
+ (cx + Random.value) * cellSize,
+ (cy + Random.value) * cellSize,
+ (cz + Random.value) * cellSize);
+ }
+
+ float maxDist = cellSize;
+ int count = res * res * res;
+ f1 = new float[count];
+ f2 = new float[count];
+
+ for (int z = 0; z < res; z++)
+ for (int y = 0; y < res; y++)
+ for (int x = 0; x < res; x++)
+ {
+ var pos = new Vector3(x + 0.5f, y + 0.5f, z + 0.5f);
+
+ int cx0 = Mathf.FloorToInt(pos.x / cellSize);
+ int cy0 = Mathf.FloorToInt(pos.y / cellSize);
+ int cz0 = Mathf.FloorToInt(pos.z / cellSize);
+
+ float d1 = float.MaxValue;
+ float d2 = float.MaxValue;
+
+ for (int dz = -1; dz <= 1; dz++)
+ for (int dy = -1; dy <= 1; dy++)
+ for (int dx = -1; dx <= 1; dx++)
+ {
+ int ncx = cx0 + dx;
+ int ncy = cy0 + dy;
+ int ncz = cz0 + dz;
+
+ int wcx = ((ncx % cellCount) + cellCount) % cellCount;
+ int wcy = ((ncy % cellCount) + cellCount) % cellCount;
+ int wcz = ((ncz % cellCount) + cellCount) % cellCount;
+
+ Vector3 pt = points[wcx + cellCount * (wcy + cellCount * wcz)];
+ pt.x += (ncx - wcx) * cellSize;
+ pt.y += (ncy - wcy) * cellSize;
+ pt.z += (ncz - wcz) * cellSize;
+
+ float dist = Distance(pos, pt, metric);
+ if (dist < d1)
+ {
+ d2 = d1;
+ d1 = dist;
+ }
+ else if (dist < d2)
+ {
+ d2 = dist;
+ }
+ }
+
+ int i = x + res * (y + res * z);
+ f1[i] = Mathf.Clamp01(d1 / maxDist);
+ f2[i] = Mathf.Clamp01(d2 / maxDist);
+ }
+ }
+
+ // Convert F2-F1 into a signed distance field relative to the cell boundary (F2-F1 = 0).
+ // Output: 0.5 = on the boundary, 0 = clamped near-side, 1 = clamped far-side.
+ // sdfRadius controls how much F2-F1 range maps to [0,1] — smaller = more precision near edges.
+ static void GenerateEdgeSDF(byte[] pixels, float[] f1, float[] f2, float sdfRadius)
+ {
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ float sd = f2[i] - f1[i];
+ float normalized = sd / sdfRadius * 0.5f + 0.5f;
+ pixels[i] = (byte)Mathf.RoundToInt(Mathf.Clamp01(normalized) * 255f);
+ }
+ }
+}