DrawAhead.AssignRockMeshes ranks rocks by unworn size onto meshes in ascending radius; RockScale divides the gameplay radius by the mesh's measured radius; ChunkAt is the one rock position for the renderer and the reticle (ChunkHeight folded into it). InstancedMesh.Radius is filled and verified by RockMeshRadius. Tests pin the assignment, the scale and that the renderer and reticle call DrawAhead (finding owner-not-pinned). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
3.4 KiB
C#
62 lines
3.4 KiB
C#
using System;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
|
|
// The radius of each rock mesh ZoneRenderer draws (ruling rock-meshes-by-size): the farthest vertex from the mesh origin, which bounds the
|
|
// mesh's top under any rotation. DrawAhead.RockScale divides the gameplay radius by it, so the drawn rock is the gameplay rock. It is stored on
|
|
// InstancedMesh.Radius in ARPG.unity, because a build cannot read vertices of a mesh without Read/Write import.
|
|
//
|
|
// Unity -batchmode -nographics -projectPath <project> -executeMethod Aetheria.EditorTools.RockMeshRadius.Fill -logFile <log>
|
|
// Unity -batchmode -nographics -projectPath <project> -executeMethod Aetheria.EditorTools.RockMeshRadius.Verify -logFile <log>
|
|
//
|
|
// Fill measures every AsteroidMeshes entry and saves the scene; Verify fails (exit 1) when any stored radius is not the measured one.
|
|
namespace Aetheria.EditorTools
|
|
{
|
|
public static class RockMeshRadius
|
|
{
|
|
private const string Scene = "Assets/Scenes/ARPG.unity";
|
|
private const float Tolerance = 1e-4f;
|
|
|
|
public static float Measure(Mesh mesh) => Mathf.Sqrt(mesh.vertices.Max(v => v.sqrMagnitude));
|
|
|
|
public static void Fill() => Run(fill: true);
|
|
|
|
public static void Verify() => Run(fill: false);
|
|
|
|
private static void Run(bool fill)
|
|
{
|
|
try
|
|
{
|
|
var scene = EditorSceneManager.OpenScene(Scene, OpenSceneMode.Single);
|
|
var renderer = UnityEngine.Object.FindObjectsByType<ZoneRenderer>(FindObjectsInactive.Include, FindObjectsSortMode.None)
|
|
.Single(r => r.gameObject.scene == scene);
|
|
var meshes = renderer.AsteroidMeshes;
|
|
if (meshes == null || meshes.Length == 0) throw new InvalidOperationException("ARPG.unity's ZoneRenderer has no AsteroidMeshes.");
|
|
var bad = meshes.Where(m => m.Mesh == null).ToArray();
|
|
if (bad.Length > 0) throw new InvalidOperationException($"{bad.Length} AsteroidMeshes entries have no mesh.");
|
|
var measured = meshes.Select(m => Measure(m.Mesh)).ToArray();
|
|
var failures = meshes.Select((m, i) => (m, i)).Where(p => !(measured[p.i] > 0f) || Mathf.Abs(p.m.Radius - measured[p.i]) > Tolerance).ToArray();
|
|
if (fill)
|
|
{
|
|
for (var i = 0; i < meshes.Length; i++) meshes[i].Radius = measured[i];
|
|
EditorUtility.SetDirty(renderer);
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
if (!EditorSceneManager.SaveScene(scene)) throw new InvalidOperationException("The scene did not save.");
|
|
}
|
|
else if (failures.Length > 0)
|
|
throw new InvalidOperationException("Stored radius differs from measured: " + string.Join(", ",
|
|
failures.Select(f => $"{f.m.Mesh.name} stored {f.m.Radius} measured {measured[f.i]}")));
|
|
var sorted = measured.OrderBy(r => r).ToArray();
|
|
Debug.Log($"[RockMeshRadius] {(fill ? "filled" : "ok")}: {sorted.Length} meshes, radius min {sorted[0]:F3} median {sorted[sorted.Length / 2]:F3} max {sorted[^1]:F3}");
|
|
EditorApplication.Exit(0);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
EditorApplication.Exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|