champions-of-luneraria/Assets/Luneraria/Editor/Setup/LuneProjectSetup.cs
Martin Stickelbroeck c386206876 U5.5-pilot Figuren: Meshy-Kohlenbeißer, Lava-Glut, GPU-Skinning
Pilot für eine generierte Figur: der Kohlenbeißer g01 kommt als Meshy-Ergebnis
in den Port und ersetzt dort das Phase-4-Modell. Eine Figur genügt, um Pipeline,
Bild und Leistung zu bewerten; Anatomie, Gesichter und Haare bleiben
Künstler-Sculpts oder lizenzierte Packs (Vorgabe vom 1. Oktober).

- Meshy-Pipeline unter tools/meshy/: API-Client (meshy.py, Ledger, Schlüssel
  nie geloggt) und Blender-Generator (build_figure.py) aus den unveränderten
  Downloads in assets-source/meshy/g01/. Export über den Manifest-Eintrag
  kohlenbeisser mit "replaces": "g01"; LuneUnitLibraryBuilder hängt die Figur
  zusätzlich unter der Godot-ID ein.
- UV-Material AI_Kohlenbeisser (Familie authored): die Karten der Figur selbst,
  Lava-Glut aus der Albedo-Alpha (Meshy 7.1 ohne Emission-Map) mit _Glow 1,25.
- Schatten ohne Normalen-Versatz (LuneShadowBias, _ShadowNormalScale 0): die
  UV-Naht-Kopien tragen geknickte Normalen; URP ApplyShadowBias zog helle
  Nähte durch den Schatten. Die Figur wirft nur mit Tiefen-Bias.
- GPU-Batched-Skinning (PlayerSettings.meshDeformation, LuneProjectSetup):
  35 Meshy-Kohlenbeißer kosteten mit CPU-Skinning rund 1 ms Haupt- und
  1,7 ms Render-Thread je Bild.
- LuneBenchmark: -luneBenchmarkRound und -luneBenchmarkArmy (Figuren-Stress).
  LuneAssetPreview: LUNE_ONLY für Vorher/Nachher, Trefferframe aus der
  Kontaktzeit des Manifests. FigureViewsTests (Explicit) für die vier
  Spielmomente der Figur.
- Tests: EditMode 29/29, PlayMode 14/14 (6 Explicit), Verify 718/0,
  Player-Selbsttest 30/30, Kerntests 457/457, Fingerabdrücke unverändert
  (DCE95C96556F3A1A, 7B8E43AEFC0D60C5).
- Leistung (A/B im selben Fenster, 35x g01): Meshy -21 % Mid (109,7 zu 138,8)
  und -13 % Karte (91,7 zu 105,6) gegenüber Phase-4. GPU-Skinning hebt Mid
  um 7-27 % und entlastet Haupt- und Render-Thread. Standard-Partie ab Runde 21
  ohne g01 im Bild: drei Varianten bis 6 %.
- Doku: U5-VISUAL (U5.5), PORT-QA, PORT-STATUS, KNOWN-BEHAVIOR-DIFFERENCES;
  Bilder screenshots/u5.5/ und u5.5-gpu/, Rohdaten logs/u5.5/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:14:59 +02:00

134 lines
6.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace Luneraria.EditorTools
{
/// <summary>
/// Rendering setup of the project (URP asset and renderer, linear colour space, SSAO since
/// U5.4, skinning on the GPU since U5.5), idempotent.
/// Unity -batchmode -projectPath . -executeMethod Luneraria.EditorTools.LuneProjectSetup.ConfigureRendering
/// </summary>
public static class LuneProjectSetup
{
public const string SettingsFolder = "Assets/Luneraria/Settings/Rendering";
public const string PipelinePath = SettingsFolder + "/Luneraria_URP.asset";
public const string RendererPath = SettingsFolder + "/Luneraria_URP_Renderer.asset";
public static void ConfigureRendering()
{
int code = 0;
try
{
Configure();
}
catch (System.Exception error)
{
Debug.LogError("LUNE_SETUP failed: " + error);
code = 1;
}
EditorApplication.Exit(code);
}
public static UniversalRenderPipelineAsset Configure()
{
Directory.CreateDirectory(SettingsFolder);
AssetDatabase.Refresh();
var asset = AssetDatabase.LoadAssetAtPath<UniversalRenderPipelineAsset>(PipelinePath);
if (asset == null)
{
var renderer = ScriptableObject.CreateInstance<UniversalRendererData>();
AssetDatabase.CreateAsset(renderer, RendererPath);
ResourceReloader.ReloadAllNullIn(renderer, UniversalRenderPipelineAsset.packagePath);
asset = UniversalRenderPipelineAsset.Create(renderer);
AssetDatabase.CreateAsset(asset, PipelinePath);
}
// The Godot sun reaches 190 m with four splits (scripts/luneraria/view/lune_world.gd).
asset.shadowDistance = 190f;
asset.shadowCascadeCount = 4;
asset.supportsHDR = true;
asset.msaaSampleCount = 4;
asset.useSRPBatcher = true;
EditorUtility.SetDirty(asset);
Ssao(AssetDatabase.LoadAssetAtPath<UniversalRendererData>(RendererPath));
GraphicsSettings.defaultRenderPipeline = asset;
int current = QualitySettings.GetQualityLevel();
for (int level = 0; level < QualitySettings.names.Length; level++)
{
QualitySettings.SetQualityLevel(level, false);
QualitySettings.renderPipeline = asset;
}
QualitySettings.SetQualityLevel(current, false);
PlayerSettings.colorSpace = ColorSpace.Linear;
// U5.5: skinned figures deform on the GPU in batches (compute shaders). With CPU
// skinning 35 Meshy Kohlenbeißer (23,000 vertices each) cost about 1 ms of main and
// 1.7 ms of render thread per frame.
PlayerSettings.meshDeformation = MeshDeformation.GPUBatched;
MoveInto("Assets/UniversalRenderPipelineGlobalSettings.asset", SettingsFolder + "/UniversalRenderPipelineGlobalSettings.asset");
MoveInto("Assets/DefaultVolumeProfile.asset", SettingsFolder + "/DefaultVolumeProfile.asset");
AssetDatabase.SaveAssets();
Debug.Log("LUNE_SETUP rendering: " + PipelinePath + ", colour space " + PlayerSettings.colorSpace + ", skinning " + PlayerSettings.meshDeformation);
return asset;
}
/// <summary>SSAO intensity, radius (m) and fade distance (m) for the top-down camera (U5.4).</summary>
public const float SsaoIntensity = 1.2f;
public const float SsaoRadius = .35f;
public const float SsaoFalloff = 260f;
/// <summary>
/// Screen-space ambient occlusion (U5.4): contact shadows of figures and buildings on the
/// ground and occlusion in crevices, the depth cue of current renderers. Radius in metres
/// for figures of 1–2 m seen from 20–100 m; it fades only beyond the overview distance.
/// The feature is a sub-asset of the renderer and listed in its feature map; updated in place.
/// </summary>
static void Ssao(UniversalRendererData renderer)
{
if (renderer == null)
return;
var feature = renderer.rendererFeatures.OfType<ScreenSpaceAmbientOcclusion>().FirstOrDefault();
if (feature == null)
{
feature = ScriptableObject.CreateInstance<ScreenSpaceAmbientOcclusion>();
feature.name = "ScreenSpaceAmbientOcclusion";
AssetDatabase.AddObjectToAsset(feature, renderer);
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(feature, out string _, out long localId);
var data = new SerializedObject(renderer);
SerializedProperty list = data.FindProperty("m_RendererFeatures");
list.arraySize++;
list.GetArrayElementAtIndex(list.arraySize - 1).objectReferenceValue = feature;
SerializedProperty map = data.FindProperty("m_RendererFeatureMap");
map.arraySize++;
map.GetArrayElementAtIndex(map.arraySize - 1).longValue = localId;
data.ApplyModifiedPropertiesWithoutUndo();
}
var settings = new SerializedObject(feature);
settings.FindProperty("m_Settings.Intensity").floatValue = SsaoIntensity;
settings.FindProperty("m_Settings.Radius").floatValue = SsaoRadius;
settings.FindProperty("m_Settings.Falloff").floatValue = SsaoFalloff;
settings.FindProperty("m_Settings.DirectLightingStrength").floatValue = .25f;
// Depth source after the opaques: URP copies the depth it already has, so SSAO needs no
// second geometry pass (the depth-normals prepass cost 19–28 % frame rate on the M5).
settings.FindProperty("m_Settings.Source").enumValueIndex = 0;
settings.FindProperty("m_Settings.Downsample").boolValue = true;
settings.FindProperty("m_Settings.AfterOpaque").boolValue = true;
settings.ApplyModifiedPropertiesWithoutUndo();
feature.SetActive(System.Environment.GetEnvironmentVariable("LUNE_SSAO") != "0");
EditorUtility.SetDirty(feature);
EditorUtility.SetDirty(renderer);
}
static void MoveInto(string from, string to)
{
if (File.Exists(from) && !File.Exists(to))
{
string error = AssetDatabase.MoveAsset(from, to);
if (!string.IsNullOrEmpty(error))
Debug.LogWarning("LUNE_SETUP move " + from + ": " + error);
}
}
}
}