diff --git a/.gitignore b/.gitignore index 811814afa..ac9c49142 100644 --- a/.gitignore +++ b/.gitignore @@ -94,7 +94,7 @@ crashlytics-build.properties Packages/packages-lock.json GameData/run.cc GameData/player.cc -Vault/Aetheria/.obsidian/plugins/copilot/data.json +GameData/RemovedMods/ # Agent scratch, sandboxed toolchain caches, and throwaway checkouts. These had # grown to 47 untracked entries; the worktree must stay legible, so they never diff --git a/.lfsconfig b/.lfsconfig new file mode 100644 index 000000000..d9ce439fc --- /dev/null +++ b/.lfsconfig @@ -0,0 +1,2 @@ +[lfs] + url = https://eureka.gamecult.org/forge/GameCult/Aetheria.git/info/lfs diff --git a/Assets/Content/Prefabs/Mine.prefab b/Assets/Content/Prefabs/Mine.prefab index dbc1e0915..912986ee7 100644 --- a/Assets/Content/Prefabs/Mine.prefab +++ b/Assets/Content/Prefabs/Mine.prefab @@ -146,6 +146,5 @@ MonoBehaviour: ActiveEmission: 100 ArmedEmission: 1000 RotationSpeed: 1 - GridOffset: 0 HitEffect: {fileID: 1121192042590650, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3} HitEffectScale: 10 diff --git a/Assets/Content/Prefabs/Stations/PlanetOutpost.prefab b/Assets/Content/Prefabs/Stations/PlanetOutpost.prefab index 6ce31f34f..e3d20ceb7 100644 --- a/Assets/Content/Prefabs/Stations/PlanetOutpost.prefab +++ b/Assets/Content/Prefabs/Stations/PlanetOutpost.prefab @@ -81,87 +81,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &7783244517717541194 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 7783244517717541195} - - component: {fileID: 7783244517717541175} - - component: {fileID: 7783244517717541174} - m_Layer: 9 - m_Name: Gravity Well - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &7783244517717541195 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7783244517717541194} - m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 7783244518521719148} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &7783244517717541175 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7783244517717541194} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &7783244517717541174 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7783244517717541194} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 44a8bbb89acb4e34c9f41a9da5842bb4, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &7783244518317531027 GameObject: m_ObjectHideFlags: 0 @@ -289,7 +208,6 @@ Transform: m_Children: - {fileID: 589192530128514016} - {fileID: 7783244518317531036} - - {fileID: 7783244517717541195} - {fileID: 7783244516809707565} m_Father: {fileID: 0} m_RootOrder: 0 @@ -307,7 +225,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Body: {fileID: 7783244518317531038} - GravityWell: {fileID: 7783244517717541174} Icon: {fileID: 7783244516809707566} Generator: {fileID: 741236912059895623} --- !u!114 &741236912059895623 diff --git a/Assets/Editor/AetheriaBuild.cs b/Assets/Editor/AetheriaBuild.cs index 7d3c891d2..3cf434ee4 100644 --- a/Assets/Editor/AetheriaBuild.cs +++ b/Assets/Editor/AetheriaBuild.cs @@ -8,12 +8,12 @@ using UnityEditor.AddressableAssets.Settings; using UnityEditor.Build.Reporting; using Debug = UnityEngine.Debug; -// The one way to make a Win64 player directory. Owns what the directory contains: Aetheria.exe and its data, +// The one way to make a player directory, for Windows or Linux. Owns what the directory contains: the player and its data, // GameData/Aetheria.cc, GameData/Narrative/**, GameData/Mods/** and ModTools/AetherDb. Manual builds, smokes and // release cuts all call this and stage nothing themselves. // // Unity -batchmode -quit -projectPath -executeMethod AetheriaBuild.Build -// -aetheriaVersion -aetheriaOut -logFile +// -aetheriaVersion -aetheriaOut [-aetheriaTarget win64|linux64] -logFile // // AetherDb is published from tools/AetherDb, so the headless build's CULTLIB_ROOT / CULTMATH_ROOT (Directory.Build.props) // must be set for dotnet when the defaults do not hold the pinned revisions. @@ -22,6 +22,14 @@ public static class AetheriaBuild { private static readonly Regex SemVer = new Regex(@"^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$"); + // A target: the player Unity builds, its executable, and the runtime AetherDb is published for. + private static (BuildTarget target, string player, string runtime, string aetherDb) Target(string name) => name switch + { + "win64" => (BuildTarget.StandaloneWindows64, "Aetheria.exe", "win-x64", "AetherDb.exe"), + "linux64" => (BuildTarget.StandaloneLinux64, "Aetheria.x86_64", "linux-x64", "AetherDb"), + _ => throw new InvalidOperationException($"-aetheriaTarget {name}: expected win64 or linux64"), + }; + public static void Build() { var step = "arguments"; @@ -30,6 +38,7 @@ public static class AetheriaBuild var version = Argument("-aetheriaVersion"); if (!SemVer.IsMatch(version)) throw new InvalidOperationException("-aetheriaVersion is not a semver"); var output = Path.GetFullPath(Argument("-aetheriaOut")); + var target = Target(Argument("-aetheriaTarget", "win64")); var repoRoot = Directory.GetParent(UnityEngine.Application.dataPath).FullName; step = "version"; @@ -46,8 +55,8 @@ public static class AetheriaBuild var report = BuildPipeline.BuildPlayer(new BuildPlayerOptions { scenes = scenes, - locationPathName = Path.Combine(output, "Aetheria.exe"), - target = BuildTarget.StandaloneWindows64, + locationPathName = Path.Combine(output, target.player), + target = target.target, options = BuildOptions.None, }); if (report.summary.result != BuildResult.Succeeded || report.summary.totalErrors != 0) @@ -57,7 +66,7 @@ public static class AetheriaBuild PlayerStaging.Stage(repoRoot, output); step = "aetherdb"; - Publish(repoRoot, Path.Combine(output, "ModTools")); + Publish(repoRoot, Path.Combine(output, "ModTools"), target.runtime, target.aetherDb); step = "burst"; var burst = Path.Combine(output, "Aetheria_BurstDebugInformation_DoNotShip"); @@ -74,11 +83,11 @@ public static class AetheriaBuild EditorApplication.Exit(0); } - private static void Publish(string repoRoot, string modTools) + private static void Publish(string repoRoot, string modTools, string runtime, string aetherDb) { var info = new ProcessStartInfo("dotnet") { - Arguments = $"publish tools/AetherDb -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o \"{modTools}\"", + Arguments = $"publish tools/AetherDb -c Release -r {runtime} --self-contained -p:PublishSingleFile=true -o \"{modTools}\"", WorkingDirectory = repoRoot, UseShellExecute = false, RedirectStandardOutput = true, @@ -92,15 +101,16 @@ public static class AetheriaBuild if (process.ExitCode != 0) throw new InvalidOperationException($"dotnet publish exited {process.ExitCode}:\n{text}\n{errors.Result}"); } - if (!File.Exists(Path.Combine(modTools, "AetherDb.exe"))) - throw new InvalidOperationException("dotnet publish produced no ModTools/AetherDb.exe"); + if (!File.Exists(Path.Combine(modTools, aetherDb))) + throw new InvalidOperationException($"dotnet publish produced no ModTools/{aetherDb}"); } - private static string Argument(string name) + private static string Argument(string name, string fallback = null) { var args = Environment.GetCommandLineArgs(); var at = Array.IndexOf(args, name); - if (at < 0 || at + 1 >= args.Length) throw new InvalidOperationException(name + " is required"); + if (at < 0 || at + 1 >= args.Length) + return fallback ?? throw new InvalidOperationException(name + " is required"); return args[at + 1]; } } diff --git a/Assets/Editor/PlayerStaging.cs b/Assets/Editor/PlayerStaging.cs index 4698130f6..6f48daaa0 100644 --- a/Assets/Editor/PlayerStaging.cs +++ b/Assets/Editor/PlayerStaging.cs @@ -2,9 +2,8 @@ using System; using System.IO; // What a player directory holds beside the executable's data: GameData/Aetheria.cc, GameData/Narrative/** and, when -// the repo has one, GameData/Mods/**. Nothing else under GameData ships (run.cc and player.cc are the player's own; -// SoundbanksInfo.json is read by no code). Unity-free so the headless suite can prove the rules; AetheriaBuild is the -// only caller. +// the repo has one, GameData/Mods/**. Nothing else under GameData ships (run.cc and player.cc are the player's own). +// Unity-free so the headless suite can prove the rules; AetheriaBuild is the only caller. internal static class PlayerStaging { public const string Catalog = "Aetheria.cc"; diff --git a/Assets/Materials/Brushes/Gravity Well.mat b/Assets/Materials/Brushes/Gravity Field.mat similarity index 92% rename from Assets/Materials/Brushes/Gravity Well.mat rename to Assets/Materials/Brushes/Gravity Field.mat index e0bd9f79d..d912917c9 100644 --- a/Assets/Materials/Brushes/Gravity Well.mat +++ b/Assets/Materials/Brushes/Gravity Field.mat @@ -7,8 +7,8 @@ Material: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: Gravity Well - m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, type: 3} + m_Name: Gravity Field + m_Shader: {fileID: 4800000, guid: 08d8efa80af84065b54c9bfed784e435, type: 3} m_ShaderKeywords: m_LightmapFlags: 4 m_EnableInstancingVariants: 0 @@ -57,8 +57,6 @@ Material: m_Offset: {x: 0, y: 0} m_Floats: - _BumpScale: 1 - - _Cutoff: 1 - - _Depth: 80 - _DetailNormalMapScale: 1 - _DstBlend: 0 - _GlossMapScale: 1 @@ -68,7 +66,6 @@ Material: - _Mode: 0 - _OcclusionStrength: 1 - _Parallax: 0.02 - - _Power: 16 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 1 diff --git a/Assets/Materials/Brushes/Gravity Well.mat.meta b/Assets/Materials/Brushes/Gravity Field.mat.meta similarity index 100% rename from Assets/Materials/Brushes/Gravity Well.mat.meta rename to Assets/Materials/Brushes/Gravity Field.mat.meta diff --git a/Assets/Materials/Brushes/Gravity Waves.mat b/Assets/Materials/Brushes/Gravity Waves.mat deleted file mode 100644 index a45dfed21..000000000 --- a/Assets/Materials/Brushes/Gravity Waves.mat +++ /dev/null @@ -1,83 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Gravity Waves - m_Shader: {fileID: 4800000, guid: 8a8e593b17063b54ea02f7ea5363b59d, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _BumpScale: 1 - - _Cutoff: 0.5 - - _Depth: 3.5 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _Frequency: 27.43 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _Phase: 0 - - _Power: 8 - - _SinePower: 1.25 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _SrcBlend: 1 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Brushes/Gravity Waves.mat.meta b/Assets/Materials/Brushes/Gravity Waves.mat.meta deleted file mode 100644 index f4e2f41d4..000000000 --- a/Assets/Materials/Brushes/Gravity Waves.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 11fbaa87dcc7a3743a6bd3aa5c86e5ae -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Brushes/Sector Tint.mat b/Assets/Materials/Brushes/Sector Tint.mat index 3e5e00f24..84e252ad3 100644 --- a/Assets/Materials/Brushes/Sector Tint.mat +++ b/Assets/Materials/Brushes/Sector Tint.mat @@ -8,7 +8,7 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: Sector Tint - m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, type: 3} + m_Shader: {fileID: 4800000, guid: 782740c1a3d7455ea75dc87fafdc8432, type: 3} m_ValidKeywords: [] m_InvalidKeywords: [] m_LightmapFlags: 4 @@ -59,8 +59,8 @@ Material: m_Ints: [] m_Floats: - _BumpScale: 1 - - _Cutoff: 1 - _Depth: 0.025 + - _EdgeBrightness: 0.12 - _DetailNormalMapScale: 1 - _DstBlend: 0 - _Frequency: 6.2832 @@ -72,7 +72,6 @@ Material: - _OcclusionStrength: 1 - _Parallax: 0.02 - _Phase: 0 - - _Power: 2 - _SinePower: 1.25 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 diff --git a/Assets/Materials/Brushes/Wormhole Gravity 1.mat b/Assets/Materials/Brushes/Wormhole Gravity 1.mat deleted file mode 100644 index 3ae24d6ee..000000000 --- a/Assets/Materials/Brushes/Wormhole Gravity 1.mat +++ /dev/null @@ -1,80 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Wormhole Gravity 1 - m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _BumpScale: 1 - - _Cutoff: 1 - - _Depth: 54 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _Power: 4 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _SrcBlend: 1 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Brushes/Wormhole Gravity 2.mat b/Assets/Materials/Brushes/Wormhole Gravity 2.mat deleted file mode 100644 index 3ae24d6ee..000000000 --- a/Assets/Materials/Brushes/Wormhole Gravity 2.mat +++ /dev/null @@ -1,80 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Wormhole Gravity 1 - m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _BumpScale: 1 - - _Cutoff: 1 - - _Depth: 54 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _Power: 4 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _SrcBlend: 1 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Brushes/Wormhole Gravity 2.mat.meta b/Assets/Materials/Brushes/Wormhole Gravity 2.mat.meta deleted file mode 100644 index fb6f1270d..000000000 --- a/Assets/Materials/Brushes/Wormhole Gravity 2.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 0ec0689c58b99c64297b3e429e1d9112 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Brushes/Wormhole Gravity.mat b/Assets/Materials/Brushes/Wormhole Gravity.mat deleted file mode 100644 index fb6f52b20..000000000 --- a/Assets/Materials/Brushes/Wormhole Gravity.mat +++ /dev/null @@ -1,82 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 8 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Wormhole Gravity - m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, type: 3} - m_ValidKeywords: [] - m_InvalidKeywords: [] - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Ints: [] - m_Floats: - - _BumpScale: 1 - - _Cutoff: 0.15 - - _Depth: 2000 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _Power: 64 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _SrcBlend: 1 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Brushes/Wormhole Gravity.mat.meta b/Assets/Materials/Brushes/Wormhole Gravity.mat.meta deleted file mode 100644 index 30ec9b01d..000000000 --- a/Assets/Materials/Brushes/Wormhole Gravity.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a9ef17c902709ce41a837c54b809184b -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Brushes/Wormhole Waves.mat b/Assets/Materials/Brushes/Wormhole Waves.mat deleted file mode 100644 index 7902c5c36..000000000 --- a/Assets/Materials/Brushes/Wormhole Waves.mat +++ /dev/null @@ -1,84 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Wormhole Waves - m_Shader: {fileID: 4800000, guid: 8681ac064d59d15458d16cc9b884262c, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _BumpScale: 1 - - _Cutoff: 0.5 - - _Depth: 33.9 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _Frequency: 2 - - _FrequencyDist: 60 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _Power: 2 - - _SinePower: 1.25 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _Speed: 15 - - _SrcBlend: 1 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Brushes/Wormhole Waves.mat.meta b/Assets/Materials/Brushes/Wormhole Waves.mat.meta deleted file mode 100644 index 1e5f35dd3..000000000 --- a/Assets/Materials/Brushes/Wormhole Waves.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ef2f6090d5cc14a4e9ce38def3bed9ac -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Fog Marching 1.mat b/Assets/Materials/Fog Marching 1.mat deleted file mode 100644 index 20290c785..000000000 --- a/Assets/Materials/Fog Marching 1.mat +++ /dev/null @@ -1,73 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Fog Marching 1 - m_Shader: {fileID: 4800000, guid: 7a890b6ceccf53c44bfc54b18b5daa58, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _Displacement: - m_Texture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Patch: - m_Texture: {fileID: 8400000, guid: c618a6b20b3c7394590c59a9fa467e86, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Surface: - m_Texture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _TintTexture: - m_Texture: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _AlphaFloor: 0.01 - - _DepthBlend: 512 - - _DepthCeiling: 4096 - - _FlowSpeed: 2 - - _Gamma: 0.5 - - _GridCeilingBlend: -300 - - _GridCeilingDensity: 0.01 - - _GridCeilingFollow: 0 - - _GridCeilingOffset: 0 - - _GridFillDensity: 0.001 - - _GridFloorBlend: 100 - - _GridFloorDensity: 2 - - _GridFloorOffset: 20 - - _GridPatchBlend: 50 - - _GridPatchDensity: 2 - - _NoiseExponent: 2.25 - - _NoiseFrequency: 0.015 - - _NoiseSpeed: 0 - - _NoiseStrength: 500 - - _SafetyDistance: 20 - - _Scattering: 0 - - _ScatteringDensityExponent: 0.5 - - _ScatteringDistExponent: 0.5 - - _ScatteringMinDist: 500 - - _StepExponent: 2 - - _TintExponent: 0.1 - - _TurbulenceAmplitude: 100 - - _TurbulenceScale: 100 - m_Colors: - - _Tint: {r: 0.0627451, g: 0.0627451, b: 0.0627451, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Fog Marching 1.mat.meta b/Assets/Materials/Fog Marching 1.mat.meta deleted file mode 100644 index 00e039020..000000000 --- a/Assets/Materials/Fog Marching 1.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a72896cb7097bfa46a1a8f4092e47c72 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Fog Marching Main Menu.mat b/Assets/Materials/Fog Marching Main Menu.mat deleted file mode 100644 index b22522a2d..000000000 --- a/Assets/Materials/Fog Marching Main Menu.mat +++ /dev/null @@ -1,99 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Fog Marching Main Menu - m_Shader: {fileID: 4800000, guid: 7a890b6ceccf53c44bfc54b18b5daa58, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _Displacement: - m_Texture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaPatch: - m_Texture: {fileID: 8400000, guid: c618a6b20b3c7394590c59a9fa467e86, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaPatchHeight: - m_Texture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaSurfaceHeight: - m_Texture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaTint: - m_Texture: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Patch: - m_Texture: {fileID: 8400000, guid: c618a6b20b3c7394590c59a9fa467e86, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Surface: - m_Texture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _TintTexture: - m_Texture: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _AlphaFloor: 0 - - _DepthBlend: 512 - - _DepthCeiling: 1024 - - _Exposure: 100 - - _FlowAmplitude: 1 - - _FlowScale: 1 - - _FlowScroll: 1 - - _FlowSpeed: 5 - - _Gamma: 0.3 - - _GridCeilingBlend: -300 - - _GridCeilingDensity: 0.01 - - _GridCeilingFollow: 0 - - _GridCeilingOffset: 0 - - _GridFillDensity: 0.001 - - _GridFloorBlend: 50 - - _GridFloorDensity: 1 - - _GridFloorOffset: 100 - - _GridPatchBlend: 50 - - _GridPatchDensity: 1 - - _NebulaFillDensity: 0.00075 - - _NebulaFloorBlend: 50 - - _NebulaFloorDensity: 1 - - _NebulaFloorOffset: 80 - - _NebulaPatchBlend: 25 - - _NebulaPatchDensity: 0.1 - - _NoiseExponent: 2 - - _NoiseFrequency: 0.02 - - _NoiseSpeed: 0.00001 - - _NoiseStrength: 200 - - _SafetyDistance: 50 - - _Scattering: 0 - - _ScatteringDensityExponent: 8 - - _ScatteringDistExponent: 0.5 - - _ScatteringMinDist: 1 - - _StepExponent: 2 - - _TintExponent: 1.2 - - _TurbulenceAmplitude: 20 - - _TurbulenceScale: 30 - m_Colors: - - _Tint: {r: 0.09411765, g: 0.09411765, b: 0.09411765, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Fog Marching Main Menu.mat.meta b/Assets/Materials/Fog Marching Main Menu.mat.meta deleted file mode 100644 index 6a5cdb35e..000000000 --- a/Assets/Materials/Fog Marching Main Menu.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e21702a5e6e98b7409d6913663ceab7e -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Fog Marching Test.mat b/Assets/Materials/Fog Marching Test.mat deleted file mode 100644 index 91b56761e..000000000 --- a/Assets/Materials/Fog Marching Test.mat +++ /dev/null @@ -1,65 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Fog Marching Test - m_Shader: {fileID: 4800000, guid: 7a890b6ceccf53c44bfc54b18b5daa58, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _Displacement: - m_Texture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Patch: - m_Texture: {fileID: 8400000, guid: c618a6b20b3c7394590c59a9fa467e86, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Surface: - m_Texture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _TintTexture: - m_Texture: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _AlphaFloor: 0 - - _DepthBlend: 512 - - _DepthCeiling: 4096 - - _Gamma: 0.66 - - _GridCeilingBlend: -300 - - _GridCeilingDensity: 0.01 - - _GridCeilingFollow: 0 - - _GridCeilingOffset: 0 - - _GridFillDensity: 0.000025 - - _GridFloorBlend: 100 - - _GridFloorDensity: 4 - - _GridFloorOffset: 0 - - _GridPatchBlend: 5 - - _GridPatchDensity: 0.1 - - _NoiseFrequency: 0.01 - - _NoiseSpeed: 0.02 - - _NoiseStrength: 50 - - _SafetyDistance: 0 - - _StepExponent: 2 - - _TintExponent: 8 - m_Colors: - - _Tint: {r: 2.0078435, g: 2.0078435, b: 2.0078435, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Fog Marching Test.mat.meta b/Assets/Materials/Fog Marching Test.mat.meta deleted file mode 100644 index 9f0e3d586..000000000 --- a/Assets/Materials/Fog Marching Test.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ad63650ae6d6cc542a5aad16694b3c46 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/Fog Marching.mat b/Assets/Materials/Fog Marching.mat deleted file mode 100644 index 52dc13c19..000000000 --- a/Assets/Materials/Fog Marching.mat +++ /dev/null @@ -1,99 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Fog Marching - m_Shader: {fileID: 4800000, guid: 7a890b6ceccf53c44bfc54b18b5daa58, type: 3} - m_ShaderKeywords: - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _Displacement: - m_Texture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaPatch: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaPatchHeight: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaSurfaceHeight: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _NebulaTint: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Patch: - m_Texture: {fileID: 8400000, guid: c618a6b20b3c7394590c59a9fa467e86, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Surface: - m_Texture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _TintTexture: - m_Texture: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _AlphaFloor: 0.01 - - _DepthBlend: 512 - - _DepthCeiling: 4096 - - _Exposure: 10 - - _FlowAmplitude: 512 - - _FlowScale: 1024 - - _FlowScroll: 0 - - _FlowSpeed: 1 - - _Gamma: 0.3 - - _GridCeilingBlend: -300 - - _GridCeilingDensity: 0.01 - - _GridCeilingFollow: 0 - - _GridCeilingOffset: 0 - - _GridFillDensity: 0.0005 - - _GridFloorBlend: 50 - - _GridFloorDensity: 1 - - _GridFloorOffset: 10 - - _GridPatchBlend: 150 - - _GridPatchDensity: 1 - - _NebulaFillDensity: 0.00075 - - _NebulaFloorBlend: 150 - - _NebulaFloorDensity: 1 - - _NebulaFloorOffset: 0 - - _NebulaPatchBlend: 51.47 - - _NebulaPatchDensity: 0.25 - - _NoiseExponent: 3 - - _NoiseFrequency: 0.005 - - _NoiseSpeed: 0 - - _NoiseStrength: -400 - - _SafetyDistance: 200 - - _Scattering: 50 - - _ScatteringDensityExponent: 1 - - _ScatteringDistExponent: 1.5 - - _ScatteringMinDist: 10 - - _StepExponent: 2 - - _TintExponent: 2 - - _TurbulenceAmplitude: 20 - - _TurbulenceScale: 60 - m_Colors: - - _Tint: {r: 0.01, g: 0.01, b: 0.01, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Materials/Fog Marching.mat.meta b/Assets/Materials/Fog Marching.mat.meta deleted file mode 100644 index 04c6076f8..000000000 --- a/Assets/Materials/Fog Marching.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 2ff39f5d101241043ba1dcde19d92f77 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Materials/UI/MapAsteroid.mat b/Assets/Materials/UI/MapAsteroid.mat new file mode 100644 index 000000000..a0dbbdb1f --- /dev/null +++ b/Assets/Materials/UI/MapAsteroid.mat @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: MapAsteroid + m_Shader: {fileID: 4800000, guid: 22e2fc798f044a3aa2f6cd71f1df09a7, type: 3} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 0 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _MainTex: + m_Texture: {fileID: 2800000, guid: 060aefd5a3d5ced4dabd9190a140e76e, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: [] + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Materials/Brushes/Wormhole Gravity 1.mat.meta b/Assets/Materials/UI/MapAsteroid.mat.meta similarity index 79% rename from Assets/Materials/Brushes/Wormhole Gravity 1.mat.meta rename to Assets/Materials/UI/MapAsteroid.mat.meta index 7d40d0332..2c044e9e9 100644 --- a/Assets/Materials/Brushes/Wormhole Gravity 1.mat.meta +++ b/Assets/Materials/UI/MapAsteroid.mat.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 244f3e4c0c030a24b8d7aa944895bdcf +guid: c1012d5466454651b7279be93ef9af88 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 diff --git a/Assets/Prefabs/RPG/Pickups/Dodecahedron.prefab b/Assets/Prefabs/RPG/Pickups/Dodecahedron.prefab index 5d4f837d1..7a65a0864 100644 --- a/Assets/Prefabs/RPG/Pickups/Dodecahedron.prefab +++ b/Assets/Prefabs/RPG/Pickups/Dodecahedron.prefab @@ -189,7 +189,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: RotationSpeed: 1 - GridOffset: 0 LabelFadeDuration: 0.5 LabelPersistDuration: 3 LabelDisplayAngle: 15 diff --git a/Assets/Prefabs/RPG/Pickups/Hexahedron.prefab b/Assets/Prefabs/RPG/Pickups/Hexahedron.prefab index 91f1c2ad3..f9a477cda 100644 --- a/Assets/Prefabs/RPG/Pickups/Hexahedron.prefab +++ b/Assets/Prefabs/RPG/Pickups/Hexahedron.prefab @@ -162,7 +162,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: RotationSpeed: 1 - GridOffset: 0 LabelFadeDuration: 0.5 LabelPersistDuration: 3 LabelDisplayAngle: 15 diff --git a/Assets/Prefabs/RPG/Pickups/Icosahedron.prefab b/Assets/Prefabs/RPG/Pickups/Icosahedron.prefab index 95973ba56..4252e74cc 100644 --- a/Assets/Prefabs/RPG/Pickups/Icosahedron.prefab +++ b/Assets/Prefabs/RPG/Pickups/Icosahedron.prefab @@ -162,7 +162,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: RotationSpeed: 1 - GridOffset: 0 LabelFadeDuration: 0.5 LabelPersistDuration: 3 LabelDisplayAngle: 15 diff --git a/Assets/Prefabs/RPG/Pickups/Octahedron.prefab b/Assets/Prefabs/RPG/Pickups/Octahedron.prefab index 5957a7295..099083195 100644 --- a/Assets/Prefabs/RPG/Pickups/Octahedron.prefab +++ b/Assets/Prefabs/RPG/Pickups/Octahedron.prefab @@ -162,7 +162,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: RotationSpeed: 1 - GridOffset: 0 LabelFadeDuration: 0.5 LabelPersistDuration: 3 LabelDisplayAngle: 15 diff --git a/Assets/Prefabs/RPG/Pickups/Tetrahedron.prefab b/Assets/Prefabs/RPG/Pickups/Tetrahedron.prefab index 8688b6a3b..6dad19967 100644 --- a/Assets/Prefabs/RPG/Pickups/Tetrahedron.prefab +++ b/Assets/Prefabs/RPG/Pickups/Tetrahedron.prefab @@ -162,7 +162,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: RotationSpeed: 1 - GridOffset: 0 LabelFadeDuration: 0.5 LabelPersistDuration: 3 LabelDisplayAngle: 15 diff --git a/Assets/Prefabs/RPG/Planets/Gas Giant.prefab b/Assets/Prefabs/RPG/Planets/Gas Giant.prefab index cb628d804..de87b2a13 100644 --- a/Assets/Prefabs/RPG/Planets/Gas Giant.prefab +++ b/Assets/Prefabs/RPG/Planets/Gas Giant.prefab @@ -30,7 +30,6 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 3973935122748960680} - - {fileID: 3973935121058605868} - {fileID: 3973935121358855036} m_Father: {fileID: 0} m_RootOrder: 0 @@ -48,93 +47,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Body: {fileID: 3973935122748960683} - GravityWell: {fileID: 3973935121058605871} Icon: {fileID: 3973935121358855037} Generator: {fileID: 0} SunMaterial: {fileID: 3973935122748960685} ---- !u!1 &3973935121058605867 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 3973935121058605868} - - component: {fileID: 3973935121058605856} - - component: {fileID: 3973935121058605871} - m_Layer: 9 - m_Name: Gravity Well - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &3973935121058605868 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3973935121058605867} - m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 3973935120991099173} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &3973935121058605856 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3973935121058605867} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &3973935121058605871 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3973935121058605867} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 44a8bbb89acb4e34c9f41a9da5842bb4, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &3973935121358855035 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/RPG/Planets/Planet.prefab b/Assets/Prefabs/RPG/Planets/Planet.prefab index 336b3430b..afffe73c4 100644 --- a/Assets/Prefabs/RPG/Planets/Planet.prefab +++ b/Assets/Prefabs/RPG/Planets/Planet.prefab @@ -83,89 +83,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &8859079004671924238 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 8859079004671924239} - - component: {fileID: 8859079004671924339} - - component: {fileID: 8859079004671924338} - m_Layer: 9 - m_Name: Gravity Well - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &8859079004671924239 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8859079004671924238} - m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 8859079004937162792} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &8859079004671924339 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8859079004671924238} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &8859079004671924338 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8859079004671924238} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 44a8bbb89acb4e34c9f41a9da5842bb4, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &8859079004937162791 GameObject: m_ObjectHideFlags: 0 @@ -197,7 +114,6 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 8859079005270853336} - - {fileID: 8859079004671924239} - {fileID: 8859079003226229097} m_Father: {fileID: 0} m_RootOrder: 0 @@ -215,7 +131,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Body: {fileID: 8859079005270853338} - GravityWell: {fileID: 8859079004671924338} Icon: {fileID: 8859079003226229098} Generator: {fileID: -7153121627769399805} --- !u!114 &-7153121627769399805 diff --git a/Assets/Prefabs/RPG/Planets/Sun.prefab b/Assets/Prefabs/RPG/Planets/Sun.prefab index cf7fb216f..ebe0fa72f 100644 --- a/Assets/Prefabs/RPG/Planets/Sun.prefab +++ b/Assets/Prefabs/RPG/Planets/Sun.prefab @@ -30,7 +30,6 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 7852221720971737233} - - {fileID: 7852221720969441490} - {fileID: 7852221720816817928} - {fileID: 7852221720964551245} m_Father: {fileID: 0} @@ -49,7 +48,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Body: {fileID: 7852221720971737229} - GravityWell: {fileID: 7852221720969441484} Icon: {fileID: 7852221720964551246} Generator: {fileID: 0} SunMaterial: {fileID: 7852221720971737234} @@ -221,89 +219,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &7852221720969441489 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 7852221720969441490} - - component: {fileID: 7852221720969441485} - - component: {fileID: 7852221720969441484} - m_Layer: 9 - m_Name: Gravity Well - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &7852221720969441490 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7852221720969441489} - m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 7852221720480299767} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &7852221720969441485 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7852221720969441489} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &7852221720969441484 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7852221720969441489} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 44a8bbb89acb4e34c9f41a9da5842bb4, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &7852221720971737232 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/RPG/Wormhole 1.prefab b/Assets/Prefabs/RPG/Wormhole 1.prefab index b03d01009..e1ca93be7 100644 --- a/Assets/Prefabs/RPG/Wormhole 1.prefab +++ b/Assets/Prefabs/RPG/Wormhole 1.prefab @@ -1,167 +1,5 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: ---- !u!1 &1826894967093516808 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1030590306481442051} - - component: {fileID: 3216580199816124320} - - component: {fileID: 3211969211988968959} - m_Layer: 9 - m_Name: Gravity Well - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &1030590306481442051 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1826894967093516808} - m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1024, y: 1024, z: 1024} - m_Children: [] - m_Father: {fileID: 463084220523011477} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &3216580199816124320 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1826894967093516808} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &3211969211988968959 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1826894967093516808} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 244f3e4c0c030a24b8d7aa944895bdcf, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3908760799087214860 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 581261949390603689} - - component: {fileID: 987447324816385543} - - component: {fileID: 3959510878930752720} - m_Layer: 9 - m_Name: Gravity Waves - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &581261949390603689 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3908760799087214860} - m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 512, y: 512, z: 512} - m_Children: [] - m_Father: {fileID: 463084220523011477} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &987447324816385543 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3908760799087214860} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &3959510878930752720 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3908760799087214860} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: ef2f6090d5cc14a4e9ce38def3bed9ac, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &4837153162502999620 GameObject: m_ObjectHideFlags: 0 @@ -189,9 +27,6 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - - {fileID: 8222188360457572195} - - {fileID: 1030590306481442051} - - {fileID: 581261949390603689} - {fileID: 8249320701965103923} m_Father: {fileID: 0} m_RootOrder: 0 @@ -277,84 +112,3 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &7647029665738834910 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 8222188360457572195} - - component: {fileID: 2468954688819198096} - - component: {fileID: 8290522694236574882} - m_Layer: 9 - m_Name: Gravity Pit - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &8222188360457572195 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7647029665738834910} - m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 512, y: 512, z: 512} - m_Children: [] - m_Father: {fileID: 463084220523011477} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} ---- !u!33 &2468954688819198096 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7647029665738834910} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &8290522694236574882 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7647029665738834910} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: a9ef17c902709ce41a837c54b809184b, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Prefabs/RTS/Asteroid Belt.prefab b/Assets/Prefabs/RTS/Asteroid Belt.prefab deleted file mode 100644 index 38df4e9e3..000000000 --- a/Assets/Prefabs/RTS/Asteroid Belt.prefab +++ /dev/null @@ -1,111 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &992254348637625916 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1779911639310264026} - - component: {fileID: 349597964696402918} - - component: {fileID: 2844878085845264008} - - component: {fileID: 917803079556930035} - - component: {fileID: 4397878771830533054} - m_Layer: 14 - m_Name: Asteroid Belt - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &1779911639310264026 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 992254348637625916} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &349597964696402918 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 992254348637625916} - m_Mesh: {fileID: 0} ---- !u!23 &2844878085845264008 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 992254348637625916} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: c018d99199afeca448c64b9022ac7270, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 0 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!64 &917803079556930035 -MeshCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 992254348637625916} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - serializedVersion: 4 - m_Convex: 0 - m_CookingOptions: 30 - m_Mesh: {fileID: 0} ---- !u!114 &4397878771830533054 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 992254348637625916} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 52538a3309c36fc4583c964a1c0b705f, type: 3} - m_Name: - m_EditorClassIdentifier: diff --git a/Assets/Prefabs/RTS/Asteroid Belt.prefab.meta b/Assets/Prefabs/RTS/Asteroid Belt.prefab.meta deleted file mode 100644 index f1298ae38..000000000 --- a/Assets/Prefabs/RTS/Asteroid Belt.prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 1f1bfa2175a13b647888e2949e0cb138 -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Prefabs/RTS/Planet.prefab b/Assets/Prefabs/RTS/Planet.prefab index dc8300809..b288f30c5 100644 --- a/Assets/Prefabs/RTS/Planet.prefab +++ b/Assets/Prefabs/RTS/Planet.prefab @@ -223,7 +223,6 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 5321404120083801960} - {fileID: 5321404118931821940} - {fileID: 5321404119494867799} - {fileID: 1093865191529778971} @@ -245,7 +244,7 @@ MonoBehaviour: Label: {fileID: 5321404119494867796} Message: {fileID: 6196070888394201391} Children: [] - GravityMesh: {fileID: 5321404120083801962} + GravityMesh: {fileID: 0} --- !u!1 &5321404118931821943 GameObject: m_ObjectHideFlags: 0 @@ -564,93 +563,3 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_renderer: {fileID: 5321404119494867795} m_maskType: 0 ---- !u!1 &5321404120083801964 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5321404120083801960} - - component: {fileID: 5321404120083801963} - - component: {fileID: 5321404120083801962} - m_Layer: 8 - m_Name: Gravity Well - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &5321404120083801960 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5321404120083801964} - serializedVersion: 2 - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 10, y: 10, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 5321404118521994135} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &5321404120083801963 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5321404120083801964} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &5321404120083801962 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5321404120083801964} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_ForceMeshLod: -1 - m_MeshLodSelectionBias: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 44a8bbb89acb4e34c9f41a9da5842bb4, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 0 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_GlobalIlluminationMeshLod: 0 - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_MaskInteraction: 0 - m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Resources/Geonames.meta b/Assets/Resources/Geonames.meta deleted file mode 100644 index 64c8c9a19..000000000 --- a/Assets/Resources/Geonames.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 0f3e518a4c9bcc2469abe6cebdcb3691 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Australia.csv b/Assets/Resources/Geonames/Australia.csv deleted file mode 100644 index 05587efaa..000000000 --- a/Assets/Resources/Geonames/Australia.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01bb478f4433dc42d5533e5030ef103ad163deca6bc40fb412025acd12e35c73 -size 442795 diff --git a/Assets/Resources/Geonames/Australia.csv.meta b/Assets/Resources/Geonames/Australia.csv.meta deleted file mode 100644 index 33d8c7dbe..000000000 --- a/Assets/Resources/Geonames/Australia.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 8ba25d5d0ecee31408c7ce7460b4e015 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Congo.csv b/Assets/Resources/Geonames/Congo.csv deleted file mode 100644 index 5d0f0b66e..000000000 --- a/Assets/Resources/Geonames/Congo.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c84ff44083e4f0d9d9314e7b7095ed4ce2955905f26e7926faa09ed222655729 -size 506722 diff --git a/Assets/Resources/Geonames/Congo.csv.meta b/Assets/Resources/Geonames/Congo.csv.meta deleted file mode 100644 index 6147d0a96..000000000 --- a/Assets/Resources/Geonames/Congo.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 1e43b383bf860524986a6947496776d0 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Germany.csv b/Assets/Resources/Geonames/Germany.csv deleted file mode 100644 index beb35d1d0..000000000 --- a/Assets/Resources/Geonames/Germany.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:73e4e7231d448879d0a6d8524b3200be75fc8f5011a70cfd670b0897d07eca10 -size 1284208 diff --git a/Assets/Resources/Geonames/Germany.csv.meta b/Assets/Resources/Geonames/Germany.csv.meta deleted file mode 100644 index 244e41eda..000000000 --- a/Assets/Resources/Geonames/Germany.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: ed470b8eb6cc68c4485ddc05d5a17ca1 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/India.csv b/Assets/Resources/Geonames/India.csv deleted file mode 100644 index 6b4eab5c6..000000000 --- a/Assets/Resources/Geonames/India.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:96c66414822c43a5451ce1910c1e545dedb224b0f3e79bc54f945dd484afc6a2 -size 3229324 diff --git a/Assets/Resources/Geonames/India.csv.meta b/Assets/Resources/Geonames/India.csv.meta deleted file mode 100644 index 3f8c176b4..000000000 --- a/Assets/Resources/Geonames/India.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: ca62f6d649360184aaa975c2716dfb27 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Iran.csv b/Assets/Resources/Geonames/Iran.csv deleted file mode 100644 index 8d2754642..000000000 --- a/Assets/Resources/Geonames/Iran.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b70fbbe6b4832eb1a3f35844bd87c0eb479fc77b6e59d6600b64cbbe7cc1bf16 -size 1073119 diff --git a/Assets/Resources/Geonames/Iran.csv.meta b/Assets/Resources/Geonames/Iran.csv.meta deleted file mode 100644 index 507664280..000000000 --- a/Assets/Resources/Geonames/Iran.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: e2d60c7158f6a01418d78e46c5845729 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Japan.csv b/Assets/Resources/Geonames/Japan.csv deleted file mode 100644 index d106ec364..000000000 --- a/Assets/Resources/Geonames/Japan.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d58dc0227cc6e3b7622726c155031467b6d7314623c7e3a33aad8ce1462b2c5b -size 1031132 diff --git a/Assets/Resources/Geonames/Japan.csv.meta b/Assets/Resources/Geonames/Japan.csv.meta deleted file mode 100644 index f46c1022d..000000000 --- a/Assets/Resources/Geonames/Japan.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 5dd450a6f5ec92d49871674b33509135 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Mexico.csv b/Assets/Resources/Geonames/Mexico.csv deleted file mode 100644 index 517228e8f..000000000 --- a/Assets/Resources/Geonames/Mexico.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9a654b8451a08a0c769712f4fb1d96c3c661457a88bb57ec4288eab36eab7ec1 -size 633432 diff --git a/Assets/Resources/Geonames/Mexico.csv.meta b/Assets/Resources/Geonames/Mexico.csv.meta deleted file mode 100644 index 2b0283e78..000000000 --- a/Assets/Resources/Geonames/Mexico.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: b75e17ea31f6a62488628bf9605bd48c -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Russia.csv b/Assets/Resources/Geonames/Russia.csv deleted file mode 100644 index 6344ff571..000000000 --- a/Assets/Resources/Geonames/Russia.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f4ed581eef681e5357a14efe4e6a1fc52370def7ada6565f4b953e410fa7f39e -size 2375319 diff --git a/Assets/Resources/Geonames/Russia.csv.meta b/Assets/Resources/Geonames/Russia.csv.meta deleted file mode 100644 index 7794294e9..000000000 --- a/Assets/Resources/Geonames/Russia.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 5c1aee30ccf3a1e44bddeb4d550edbf0 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Swahili.csv b/Assets/Resources/Geonames/Swahili.csv deleted file mode 100644 index 678065eed..000000000 --- a/Assets/Resources/Geonames/Swahili.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb0f44e7948c637a2b0f9e0ca098b2f4689d065fe6484723acf0bfa34a26d0a5 -size 501002 diff --git a/Assets/Resources/Geonames/Swahili.csv.meta b/Assets/Resources/Geonames/Swahili.csv.meta deleted file mode 100644 index 94bfbb4f8..000000000 --- a/Assets/Resources/Geonames/Swahili.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 63e21276398c8b0449e01d97a088add1 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/Thailand.csv b/Assets/Resources/Geonames/Thailand.csv deleted file mode 100644 index 400b8f1a9..000000000 --- a/Assets/Resources/Geonames/Thailand.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:888c929f0e29728369448e6981bb8a043c49610f4c4390a2cb549bf6b494d3dc -size 143084 diff --git a/Assets/Resources/Geonames/Thailand.csv.meta b/Assets/Resources/Geonames/Thailand.csv.meta deleted file mode 100644 index fd5bd346a..000000000 --- a/Assets/Resources/Geonames/Thailand.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 2923f8bd3a2f0fb4084f64138e6d793d -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/USA.csv b/Assets/Resources/Geonames/USA.csv deleted file mode 100644 index 05bd91140..000000000 --- a/Assets/Resources/Geonames/USA.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:314a30df2033795f04738589c660991e4310f1bfe0578c71bda2a9a72d8a617f -size 954928 diff --git a/Assets/Resources/Geonames/USA.csv.meta b/Assets/Resources/Geonames/USA.csv.meta deleted file mode 100644 index b59cdf567..000000000 --- a/Assets/Resources/Geonames/USA.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: a06c0e26f73d51f489303deadad77030 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/bad-words.txt b/Assets/Resources/Geonames/bad-words.txt deleted file mode 100644 index 5013fea9d..000000000 --- a/Assets/Resources/Geonames/bad-words.txt +++ /dev/null @@ -1,1384 +0,0 @@ - -abbo -abo -abortion -abuse -addict -addicts -adult -africa -african -alla -allah -alligatorbait -amateur -american -anal -analannie -analsex -angie -angry -anus -arab -arabs -areola -argie -aroused -arse -arsehole -asian -ass -assassin -assassinate -assassination -assault -assbagger -assblaster -assclown -asscowboy -asses -assfuck -assfucker -asshat -asshole -assholes -asshore -assjockey -asskiss -asskisser -assklown -asslick -asslicker -asslover -assman -assmonkey -assmunch -assmuncher -asspacker -asspirate -asspuppies -assranger -asswhore -asswipe -athletesfoot -attack -australian -babe -babies -backdoor -backdoorman -backseat -badfuck -balllicker -balls -ballsack -banging -baptist -barelylegal -barf -barface -barfface -bast -bastard -bazongas -bazooms -beaner -beast -beastality -beastial -beastiality -beatoff -beat-off -beatyourmeat -beaver -bestial -bestiality -bi -biatch -bible -bicurious -bigass -bigbastard -bigbutt -bigger -bisexual -bi-sexual -bitch -bitcher -bitches -bitchez -bitchin -bitching -bitchslap -bitchy -biteme -black -blackman -blackout -blacks -blind -blow -blowjob -boang -bogan -bohunk -bollick -bollock -bomb -bombers -bombing -bombs -bomd -bondage -boner -bong -boob -boobies -boobs -booby -boody -boom -boong -boonga -boonie -booty -bootycall -bountybar -bra -brea5t -breast -breastjob -breastlover -breastman -brothel -bugger -buggered -buggery -bullcrap -bulldike -bulldyke -bullshit -bumblefuck -bumfuck -bunga -bunghole -buried -burn -butchbabes -butchdike -butchdyke -butt -buttbang -butt-bang -buttface -buttfuck -butt-fuck -buttfucker -butt-fucker -buttfuckers -butt-fuckers -butthead -buttman -buttmunch -buttmuncher -buttpirate -buttplug -buttstain -byatch -cacker -cameljockey -cameltoe -canadian -cancer -carpetmuncher -carruth -catholic -catholics -cemetery -chav -cherrypopper -chickslick -children's -chin -chinaman -chinamen -chinese -chink -chinky -choad -chode -christ -christian -church -cigarette -cigs -clamdigger -clamdiver -clit -clitoris -clogwog -cocaine -cock -cockblock -cockblocker -cockcowboy -cockfight -cockhead -cockknob -cocklicker -cocklover -cocknob -cockqueen -cockrider -cocksman -cocksmith -cocksmoker -cocksucer -cocksuck -cocksucked -cocksucker -cocksucking -cocktail -cocktease -cocky -cohee -coitus -color -colored -coloured -commie -communist -condom -conservative -conspiracy -coolie -cooly -coon -coondog -copulate -cornhole -corruption -cra5h -crabs -crack -crackpipe -crackwhore -crack-whore -crap -crapola -crapper -crappy -crash -creamy -crime -crimes -criminal -criminals -crotch -crotchjockey -crotchmonkey -crotchrot -cum -cumbubble -cumfest -cumjockey -cumm -cummer -cumming -cumquat -cumqueen -cumshot -cunilingus -cunillingus -cunn -cunnilingus -cunntt -cunt -cunteyed -cuntfuck -cuntfucker -cuntlick -cuntlicker -cuntlicking -cuntsucker -cybersex -cyberslimer -dago -dahmer -dammit -damn -damnation -damnit -darkie -darky -datnigga -dead -deapthroat -death -deepthroat -defecate -dego -demon -deposit -desire -destroy -deth -devil -devilworshipper -dick -dickbrain -dickforbrains -dickhead -dickless -dicklick -dicklicker -dickman -dickwad -dickweed -diddle -die -died -dies -dike -dildo -dingleberry -dink -dipshit -dipstick -dirty -disease -diseases -disturbed -dive -dix -dixiedike -dixiedyke -doggiestyle -doggystyle -dong -doodoo -doo-doo -doom -dope -dragqueen -dragqween -dripdick -drug -drunk -drunken -dumb -dumbass -dumbbitch -dumbfuck -dyefly -dyke -easyslut -eatballs -eatme -eatpussy -ecstacy -ejaculate -ejaculated -ejaculating -ejaculation -enema -enemy -erect -erection -ero -escort -ethiopian -ethnic -european -evl -excrement -execute -executed -execution -executioner -explosion -facefucker -faeces -fag -fagging -faggot -fagot -failed -failure -fairies -fairy -faith -fannyfucker -fart -farted -farting -farty -fastfuck -fat -fatah -fatass -fatfuck -fatfucker -fatso -fckcum -fear -feces -felatio -felch -felcher -felching -fellatio -feltch -feltcher -feltching -fetish -fight -filipina -filipino -fingerfood -fingerfuck -fingerfucked -fingerfucker -fingerfuckers -fingerfucking -fire -firing -fister -fistfuck -fistfucked -fistfucker -fistfucking -fisting -flange -flasher -flatulence -floo -flydie -flydye -fok -fondle -footaction -footfuck -footfucker -footlicker -footstar -fore -foreskin -forni -fornicate -foursome -fourtwenty -fraud -freakfuck -freakyfucker -freefuck -fu -fubar -fuc -fucck -fuck -fucka -fuckable -fuckbag -fuckbuddy -fucked -fuckedup -fucker -fuckers -fuckface -fuckfest -fuckfreak -fuckfriend -fuckhead -fuckher -fuckin -fuckina -fucking -fuckingbitch -fuckinnuts -fuckinright -fuckit -fuckknob -fuckme -fuckmehard -fuckmonkey -fuckoff -fuckpig -fucks -fucktard -fuckwhore -fuckyou -fudgepacker -fugly -fuk -fuks -funeral -funfuck -fungus -fuuck -gangbang -gangbanged -gangbanger -gangsta -gatorbait -gay -gaymuthafuckinwhore -gaysex -geez -geezer -geni -genital -german -getiton -gin -ginzo -gipp -girls -givehead -glazeddonut -gob -god -godammit -goddamit -goddammit -goddamn -goddamned -goddamnes -goddamnit -goddamnmuthafucker -goldenshower -gonorrehea -gonzagas -gook -gotohell -goy -goyim -greaseball -gringo -groe -gross -grostulation -gubba -gummer -gun -gyp -gypo -gypp -gyppie -gyppo -gyppy -hamas -handjob -hapa -harder -hardon -harem -headfuck -headlights -hebe -heeb -hell -henhouse -heroin -herpes -heterosexual -hijack -hijacker -hijacking -hillbillies -hindoo -hiscock -hitler -hitlerism -hitlerist -hiv -ho -hobo -hodgie -hoes -hole -holestuffer -homicide -homo -homobangers -homosexual -honger -honk -honkers -honkey -honky -hook -hooker -hookers -hooters -hore -hork -horn -horney -horniest -horny -horseshit -hosejob -hoser -hostage -hotdamn -hotpussy -hottotrot -hummer -husky -hussy -hustler -hymen -hymie -iblowu -idiot -ikey -illegal -incest -insest -intercourse -interracial -intheass -inthebuff -israel -israeli -israel's -italiano -itch -jackass -jackoff -jackshit -jacktheripper -jade -jap -japanese -japcrap -jebus -jeez -jerkoff -jesus -jesuschrist -jew -jewish -jiga -jigaboo -jigg -jigga -jiggabo -jigger -jiggy -jihad -jijjiboo -jimfish -jism -jiz -jizim -jizjuice -jizm -jizz -jizzim -jizzum -joint -juggalo -jugs -junglebunny -kaffer -kaffir -kaffre -kafir -kanake -kid -kigger -kike -kill -killed -killer -killing -kills -kink -kinky -kissass -kkk -knife -knockers -kock -kondum -koon -kotex -krap -krappy -kraut -kum -kumbubble -kumbullbe -kummer -kumming -kumquat -kums -kunilingus -kunnilingus -kunt -ky -kyke -lactate -laid -lapdance -latin -lesbain -lesbayn -lesbian -lesbin -lesbo -lez -lezbe -lezbefriends -lezbo -lezz -lezzo -liberal -libido -licker -lickme -lies -limey -limpdick -limy -lingerie -liquor -livesex -loadedgun -lolita -looser -loser -lotion -lovebone -lovegoo -lovegun -lovejuice -lovemuscle -lovepistol -loverocket -lowlife -lsd -lubejob -lucifer -luckycammeltoe -lugan -lynch -macaca -mad -mafia -magicwand -mams -manhater -manpaste -marijuana -mastabate -mastabater -masterbate -masterblaster -mastrabator -masturbate -masturbating -mattressprincess -meatbeatter -meatrack -meth -mexican -mgger -mggor -mickeyfinn -mideast -milf -minority -mockey -mockie -mocky -mofo -moky -moles -molest -molestation -molester -molestor -moneyshot -mooncricket -mormon -moron -moslem -mosshead -mothafuck -mothafucka -mothafuckaz -mothafucked -mothafucker -mothafuckin -mothafucking -mothafuckings -motherfuck -motherfucked -motherfucker -motherfuckin -motherfucking -motherfuckings -motherlovebone -muff -muffdive -muffdiver -muffindiver -mufflikcer -mulatto -muncher -munt -murder -murderer -muslim -naked -narcotic -nasty -nastybitch -nastyho -nastyslut -nastywhore -nazi -necro -negro -negroes -negroid -negro's -nig -niger -nigerian -nigerians -nigg -nigga -niggah -niggaracci -niggard -niggarded -niggarding -niggardliness -niggardliness's -niggardly -niggards -niggard's -niggaz -nigger -niggerhead -niggerhole -niggers -nigger's -niggle -niggled -niggles -niggling -nigglings -niggor -niggur -niglet -nignog -nigr -nigra -nigre -nip -nipple -nipplering -nittit -nlgger -nlggor -nofuckingway -nook -nookey -nookie -noonan -nooner -nude -nudger -nuke -nutfucker -nymph -ontherag -oral -orga -orgasim -orgasm -orgies -orgy -osama -paki -palesimian -palestinian -pansies -pansy -panti -panties -payo -pearlnecklace -peck -pecker -peckerwood -pee -peehole -pee-pee -peepshow -peepshpw -pendy -penetration -peni5 -penile -penis -penises -penthouse -period -perv -phonesex -phuk -phuked -phuking -phukked -phukking -phungky -phuq -pi55 -picaninny -piccaninny -pickaninny -piker -pikey -piky -pimp -pimped -pimper -pimpjuic -pimpjuice -pimpsimp -pindick -piss -pissed -pisser -pisses -pisshead -pissin -pissing -pissoff -pistol -pixie -pixy -playboy -playgirl -pocha -pocho -pocketpool -pohm -polack -pom -pommie -pommy -poo -poon -poontang -poop -pooper -pooperscooper -pooping -poorwhitetrash -popimp -porchmonkey -porn -pornflick -pornking -porno -pornography -pornprincess -pot -poverty -premature -pric -prick -prickhead -primetime -propaganda -pros -prostitute -protestant -pu55i -pu55y -pube -pubic -pubiclice -pud -pudboy -pudd -puddboy -puke -puntang -purinapricness -puss -pussie -pussies -pussy -pussycat -pussyeater -pussyfucker -pussylicker -pussylips -pussylover -pussypounder -pusy -quashie -queef -queer -quickie -quim -ra8s -rabbi -racial -racist -radical -radicals -raghead -randy -rape -raped -raper -rapist -rearend -rearentry -rectum -redlight -redneck -reefer -reestie -refugee -reject -remains -rentafuck -republican -rere -retard -retarded -ribbed -rigger -rimjob -rimming -roach -robber -roundeye -rump -russki -russkie -sadis -sadom -samckdaddy -sandm -sandnigger -satan -scag -scallywag -scat -schlong -screw -screwyou -scrotum -scum -semen -seppo -servant -sex -sexed -sexfarm -sexhound -sexhouse -sexing -sexkitten -sexpot -sexslave -sextogo -sextoy -sextoys -sexual -sexually -sexwhore -sexy -sexymoma -sexy-slim -shag -shaggin -shagging -shat -shav -shawtypimp -sheeney -shhit -shinola -shit -shitcan -shitdick -shite -shiteater -shited -shitface -shitfaced -shitfit -shitforbrains -shitfuck -shitfucker -shitfull -shithapens -shithappens -shithead -shithouse -shiting -shitlist -shitola -shitoutofluck -shits -shitstain -shitted -shitter -shitting -shitty -shoot -shooting -shortfuck -showtime -sick -sissy -sixsixsix -sixtynine -sixtyniner -skank -skankbitch -skankfuck -skankwhore -skanky -skankybitch -skankywhore -skinflute -skum -skumbag -slant -slanteye -slapper -slaughter -slav -slave -slavedriver -sleezebag -sleezeball -slideitin -slime -slimeball -slimebucket -slopehead -slopey -slopy -slut -sluts -slutt -slutting -slutty -slutwear -slutwhore -smack -smackthemonkey -smut -snatch -snatchpatch -snigger -sniggered -sniggering -sniggers -snigger's -sniper -snot -snowback -snownigger -sob -sodom -sodomise -sodomite -sodomize -sodomy -sonofabitch -sonofbitch -sooty -sos -soviet -spaghettibender -spaghettinigger -spank -spankthemonkey -sperm -spermacide -spermbag -spermhearder -spermherder -spic -spick -spig -spigotty -spik -spit -spitter -splittail -spooge -spreadeagle -spunk -spunky -squaw -stagg -stiffy -strapon -stringer -stripclub -stroke -stroking -stupid -stupidfuck -stupidfucker -suck -suckdick -sucker -suckme -suckmyass -suckmydick -suckmytit -suckoff -suicide -swallow -swallower -swalow -swastika -sweetness -syphilis -taboo -taff -tampon -tang -tantra -tarbaby -tard -teat -terror -terrorist -teste -testicle -testicles -thicklips -thirdeye -thirdleg -threesome -threeway -timbernigger -tinkle -tit -titbitnipply -titfuck -titfucker -titfuckin -titjob -titlicker -titlover -tits -tittie -titties -titty -tnt -toilet -tongethruster -tongue -tonguethrust -tonguetramp -tortur -torture -tosser -towelhead -trailertrash -tramp -trannie -tranny -transexual -transsexual -transvestite -triplex -trisexual -trojan -trots -tuckahoe -tunneloflove -turd -turnon -twat -twink -twinkie -twobitwhore -uck -uk -unfuckable -upskirt -uptheass -upthebutt -urinary -urinate -urine -usama -uterus -vagina -vaginal -vatican -vibr -vibrater -vibrator -vietcong -violence -virgin -virginbreaker -vomit -vulva -wab -wank -wanker -wanking -waysted -weapon -weenie -weewee -welcher -welfare -wetb -wetback -wetspot -whacker -whash -whigger -whiskey -whiskeydick -whiskydick -whit -whitenigger -whites -whitetrash -whitey -whiz -whop -whore -whorefucker -whorehouse -wigger -willie -williewanker -willy -wn -wog -women's -wop -wtf -wuss -wuzzie -xtc -xxx -yankee -yellowman -zigabo -zipperhead diff --git a/Assets/Resources/Geonames/bad-words.txt.meta b/Assets/Resources/Geonames/bad-words.txt.meta deleted file mode 100644 index ff605e0c5..000000000 --- a/Assets/Resources/Geonames/bad-words.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 790ef350f8658b04480e0645919a21fa -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Geonames/pleiades.csv b/Assets/Resources/Geonames/pleiades.csv deleted file mode 100644 index 581d44ff7..000000000 --- a/Assets/Resources/Geonames/pleiades.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45b0dbc07d492958b26de3695427076d270bdd0525ab45c415be72560ebccb5f -size 400944 diff --git a/Assets/Resources/Geonames/pleiades.csv.meta b/Assets/Resources/Geonames/pleiades.csv.meta deleted file mode 100644 index dc0edb8f1..000000000 --- a/Assets/Resources/Geonames/pleiades.csv.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: b532694a5ec3cc6458d42aa2835537b5 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Settings.asset b/Assets/Resources/Settings.asset index 64c4333d7..46bee7272 100644 --- a/Assets/Resources/Settings.asset +++ b/Assets/Resources/Settings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:319895214aaeea5d4dfe034c46edca055f62c6b4a9692788e60ce8a5827c4b78 -size 12634 +oid sha256:7f09c54dfdb87dffd76fa92365d62c53ce333457b89ccf8ead09093ffffedc42 +size 12525 diff --git a/Assets/Scenes/ARPG.unity b/Assets/Scenes/ARPG.unity index 57c793e09..4d5b15473 100644 --- a/Assets/Scenes/ARPG.unity +++ b/Assets/Scenes/ARPG.unity @@ -409,13 +409,13 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 23420356} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: -5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1037490739} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!23 &23420359 MeshRenderer: m_ObjectHideFlags: 0 @@ -8037,7 +8037,7 @@ Camera: m_Depth: 0 m_CullingMask: serializedVersion: 2 - m_Bits: 5632 + m_Bits: 5120 m_RenderingPath: 1 m_TargetTexture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} m_TargetDisplay: 0 @@ -16322,6 +16322,7 @@ MonoBehaviour: Settings: {fileID: 11400000, guid: cd2344e2001a37544aab0960c164d4ff, type: 2} ZoneRoot: {fileID: 2095699057} SectorBrushes: {fileID: 1037490739} + AmbientLight: {fileID: 23420357} SceneCamera: {fileID: 887677622} FogCameras: - {fileID: 559840917} @@ -16332,70 +16333,98 @@ MonoBehaviour: - {fileID: 488792362} - {fileID: 970624071} - {fileID: 2095741464} - FogMaterial: {fileID: 2100000, guid: 2ff39f5d101241043ba1dcde19d92f77, type: 2} - FogFarFadeFraction: 0.125 FarPlaneDistanceMultiplier: 1 AsteroidMeshes: - Mesh: {fileID: 4300004, guid: bdb4c719a85f3f2419f6c573dd1daf15, type: 3} Material: {fileID: 2100000, guid: ce975d2d2ccd0914b9260b7d66319aaa, type: 2} + Radius: 1.5022775 - Mesh: {fileID: 4300004, guid: 19fa9aa3b5fee244fb007d58c351be01, type: 3} Material: {fileID: 2100000, guid: 443981befe15f604aba2b04eef2f0de2, type: 2} + Radius: 1.0907339 - Mesh: {fileID: 4300004, guid: 693c11c21d8f22240ae2b6c576bf923a, type: 3} Material: {fileID: 2100000, guid: 818a00140c1abb047be1d65d1c20b52a, type: 2} + Radius: 1.345119 - Mesh: {fileID: 4300004, guid: 73f7fb37d1925f246919650feda3df71, type: 3} Material: {fileID: 2100000, guid: 60bb3870b22a23b458c2bc94e4403bda, type: 2} + Radius: 0.9581426 - Mesh: {fileID: 4300004, guid: 13503d5cc77421a4384053d90e12610f, type: 3} Material: {fileID: 2100000, guid: 3175592343a31774b872b32ae89a7ee8, type: 2} + Radius: 1.3620772 - Mesh: {fileID: 4300004, guid: 7c93e75f8f4cde14d82932c65389f566, type: 3} Material: {fileID: 2100000, guid: edee875c32d24154688acf645e314864, type: 2} + Radius: 1.1282945 - Mesh: {fileID: 4300004, guid: b2e60056527750e4c87bd4f6de85ec32, type: 3} Material: {fileID: 2100000, guid: c3d0e5db17690724b89cff9a71876aa6, type: 2} + Radius: 1.5490558 - Mesh: {fileID: 4300004, guid: 87ef785989ec82b498488e651b61b2c6, type: 3} Material: {fileID: 2100000, guid: 5686a9131ca8350448768971c9e21da0, type: 2} + Radius: 1.4786077 - Mesh: {fileID: 4300004, guid: a0f93e74d454f5046908a26ecbbc31cf, type: 3} Material: {fileID: 2100000, guid: 85e9ac4c62af0fb4a94bc35ac5e24254, type: 2} + Radius: 1.1443666 - Mesh: {fileID: 4300004, guid: 4a9e0d7ffd8f8e94abf7a800653c0b4e, type: 3} Material: {fileID: 2100000, guid: 27b1a299ede684d499133b9a0dc85bda, type: 2} + Radius: 1.1114912 - Mesh: {fileID: 4300004, guid: 232b4a71860923049890d661dee4aff7, type: 3} Material: {fileID: 2100000, guid: d9e9ebabc89054f4495754372a7b4d8f, type: 2} + Radius: 1.5410137 - Mesh: {fileID: 4300004, guid: 335c54d8095aa3f44bab46ca5fa5bd4c, type: 3} Material: {fileID: 2100000, guid: 71cd479bb26cd7140ba9d3753f49ffbc, type: 2} + Radius: 1.3792597 - Mesh: {fileID: 4300004, guid: 8942a135422bd014bb3451fe02d656f2, type: 3} Material: {fileID: 2100000, guid: 0f7035043b158484995ff86e9371b141, type: 2} + Radius: 1.2433527 - Mesh: {fileID: 4300004, guid: 47e8c832aa7d46e4ba06961fddf3cc06, type: 3} Material: {fileID: 2100000, guid: d5788bb4b0d2d93429d9c6462034e470, type: 2} + Radius: 1.3664026 - Mesh: {fileID: 4300004, guid: 7c1227bb235e0f74aa9a6cdb9e7ccac5, type: 3} Material: {fileID: 2100000, guid: 060b5e360701d5146adc3d1c65c7a3df, type: 2} + Radius: 1.2458941 - Mesh: {fileID: 4300004, guid: 77a272b796d88344eacf56a79372b45e, type: 3} Material: {fileID: 2100000, guid: 3cd15eb9c358aa547bcd223ee3c1edde, type: 2} + Radius: 2.157539 - Mesh: {fileID: 4300004, guid: bafc829a0712b174d9485c5bcbc1b529, type: 3} Material: {fileID: 2100000, guid: b58fd84189173114d8708fb50b43f2d7, type: 2} + Radius: 1.5399581 - Mesh: {fileID: 4300004, guid: 47fa308b6b417034ea47809cb08bccfb, type: 3} Material: {fileID: 2100000, guid: f56b0896907ac9e41b6fd9dca05f1fa2, type: 2} + Radius: 1.8855069 - Mesh: {fileID: 4300004, guid: fb03a3e40b409db44a810641e0a6c493, type: 3} Material: {fileID: 2100000, guid: 78859c2284122944493cc283967036db, type: 2} + Radius: 1.2214345 - Mesh: {fileID: 4300004, guid: de7739fee205f4a4f9ae290126a36b61, type: 3} Material: {fileID: 2100000, guid: 89b312038af5203469d447f7ff01105f, type: 2} + Radius: 1.8183402 - Mesh: {fileID: 4300004, guid: a966f9aba651bf34da1bb6e3e7f26301, type: 3} Material: {fileID: 2100000, guid: 3326bc3e2b8ada34c9bfd44cf8cf1a1c, type: 2} + Radius: 1.5686527 - Mesh: {fileID: 4300004, guid: 0d55ec2b68453b7418abcd63378eed69, type: 3} Material: {fileID: 2100000, guid: 12b1f515279281a42b2bd53cdb174488, type: 2} + Radius: 1.9112675 - Mesh: {fileID: 4300004, guid: e5dc38d7ffa6ed242831255d93657aaa, type: 3} Material: {fileID: 2100000, guid: 5a06a4f6d5df32b4dae76c13939d3ce9, type: 2} + Radius: 1.9507759 - Mesh: {fileID: 4300004, guid: 9f36793bd68d3d84d8116485bf933992, type: 3} Material: {fileID: 2100000, guid: decc37f956dfc244b956de2d6f5375c2, type: 2} + Radius: 1.5778105 - Mesh: {fileID: 4300004, guid: 214c1534cdd5b3a4a80927e9d879290f, type: 3} Material: {fileID: 2100000, guid: a20b424628f81de4e8cf080a503b66af, type: 2} + Radius: 1.4580269 - Mesh: {fileID: 4300004, guid: 7db51bba443e10f4f9a597a296ceb0c6, type: 3} Material: {fileID: 2100000, guid: f96d19f3fe978b044972d1360d2e7707, type: 2} + Radius: 2.430305 - Mesh: {fileID: 4300004, guid: 6116ec4d9786d04408c3b26d0c525407, type: 3} Material: {fileID: 2100000, guid: 1788de6db766a4444a09dc0c80e1bcb2, type: 2} + Radius: 1.7872484 - Mesh: {fileID: 4300004, guid: 3d6e611c9ebead04b9d6d7d490977ddc, type: 3} Material: {fileID: 2100000, guid: 0fab294fd86e3b84394a967642904d55, type: 2} + Radius: 1.9273407 - Mesh: {fileID: 4300004, guid: 76b1f709ca820234bbf6c03507333b7e, type: 3} Material: {fileID: 2100000, guid: 63a8a57976371814689d7fb222e635e9, type: 2} + Radius: 1.70707 - Mesh: {fileID: 4300004, guid: d3c9f05eabff9e24ca9521e0b8dffd50, type: 3} Material: {fileID: 2100000, guid: 9be65d82397829b46894243c2d91a06d, type: 2} + Radius: 1.6336656 AsteroidSpritesheetWidth: 4 AsteroidSpritesheetHeight: 4 LODHandler: {fileID: 1009503736} @@ -16416,13 +16445,13 @@ MonoBehaviour: MapGravityMaterials: - {fileID: 2100000, guid: 841e4091d19548347bd59115047b4e49, type: 2} - {fileID: 2100000, guid: a92fc6d958edb9f46b6e58134f609cc0, type: 2} + GravityField: {fileID: 2100000, guid: 44a8bbb89acb4e34c9f41a9da5842bb4, type: 2} Tour: 0 TourSwitchTime: 10 TourFollowDistance: 100 TourHeightOffset: 50 TourFollowOffsetDegrees: 55 - AsteroidBeltUI: {fileID: 349597964696402918, guid: 1f1bfa2175a13b647888e2949e0cb138, - type: 3} + MapAsteroidMaterial: {fileID: 2100000, guid: c1012d5466454651b7279be93ef9af88, type: 2} Planet: {fileID: 8287217960984982033, guid: 88ace6073e3db1a46bd3aaf4f971acb9, type: 3} GasGiant: {fileID: 3250623180916197132, guid: 85753b4a7c762274385af384abed7a04, type: 3} @@ -16462,7 +16491,6 @@ MonoBehaviour: NebulaTint: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} Heightmap: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} ParticleColors: {fileID: 2800000, guid: 56db63da398b2d04394ddbccedce943c, type: 3} - GravityCamera: {fileID: 1537342003} --- !u!114 &1009503736 MonoBehaviour: m_ObjectHideFlags: 0 @@ -16863,7 +16891,6 @@ Transform: m_LocalScale: {x: 3000, y: 3000, z: 3000} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 23420357} - {fileID: 1108843909} - {fileID: 1647230682} - {fileID: 1254851255} @@ -23473,7 +23500,7 @@ Camera: m_Depth: 0 m_CullingMask: serializedVersion: 2 - m_Bits: 1536 + m_Bits: 512 m_RenderingPath: 1 m_TargetTexture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} m_TargetDisplay: 0 @@ -38394,6 +38421,7 @@ SceneRoots: - {fileID: 906767442} - {fileID: 1009503730} - {fileID: 1037490739} + - {fileID: 23420357} - {fileID: 2095699057} - {fileID: 134113271} - {fileID: 666227432} diff --git a/Assets/Scenes/Main Menu.unity b/Assets/Scenes/Main Menu.unity index 2d80e45b7..4fafe8336 100644 --- a/Assets/Scenes/Main Menu.unity +++ b/Assets/Scenes/Main Menu.unity @@ -892,7 +892,6 @@ MonoBehaviour: NebulaTint: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2} Heightmap: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2} ParticleColors: {fileID: 2800000, guid: 56db63da398b2d04394ddbccedce943c, type: 3} - GravityCamera: {fileID: 2103364771} --- !u!114 &750375536 MonoBehaviour: m_ObjectHideFlags: 0 @@ -982,7 +981,7 @@ Camera: m_Depth: 0 m_CullingMask: serializedVersion: 2 - m_Bits: 5632 + m_Bits: 5120 m_RenderingPath: 1 m_TargetTexture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} m_TargetDisplay: 0 @@ -1076,16 +1075,6 @@ PrefabInstance: propertyPath: m_Materials.Array.data[0] value: objectReference: {fileID: 2100000, guid: b9f52544f98f47146b486fb6e27179bb, type: 2} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.x - value: 512 - objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.y - value: 512 - objectReference: {fileID: 0} - target: {fileID: 7852221720971737233, guid: 046dde0f4600bce41ad33c476f99b309, type: 3} propertyPath: m_LocalScale.x diff --git a/Assets/Scenes/TestScene.unity b/Assets/Scenes/TestScene.unity index ffc3f3905..45c73ee85 100644 --- a/Assets/Scenes/TestScene.unity +++ b/Assets/Scenes/TestScene.unity @@ -911,16 +911,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.x - value: 20 - objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.y - value: 20 - objectReference: {fileID: 0} - target: {fileID: 7852221720971737233, guid: 046dde0f4600bce41ad33c476f99b309, type: 3} propertyPath: m_LocalPosition.y @@ -1768,16 +1758,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.x - value: 20 - objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.y - value: 20 - objectReference: {fileID: 0} - target: {fileID: 7852221720971737233, guid: 046dde0f4600bce41ad33c476f99b309, type: 3} propertyPath: m_LocalPosition.y @@ -3789,7 +3769,7 @@ Camera: m_Depth: 0 m_CullingMask: serializedVersion: 2 - m_Bits: 5632 + m_Bits: 5120 m_RenderingPath: 1 m_TargetTexture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2} m_TargetDisplay: 0 @@ -5277,16 +5257,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.x - value: 20 - objectReference: {fileID: 0} - - target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309, - type: 3} - propertyPath: m_LocalScale.y - value: 20 - objectReference: {fileID: 0} - target: {fileID: 7852221720971737233, guid: 046dde0f4600bce41ad33c476f99b309, type: 3} propertyPath: m_LocalPosition.y diff --git a/Assets/Scripts/Editor/AmbientEdgeVerify.cs b/Assets/Scripts/Editor/AmbientEdgeVerify.cs new file mode 100644 index 000000000..4a56b6ea9 --- /dev/null +++ b/Assets/Scripts/Editor/AmbientEdgeVerify.cs @@ -0,0 +1,244 @@ +using System; +using System.IO; +using GameCult.Caching; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Experimental.Rendering; +using UnityEngine.Rendering; + +namespace Aetheria.EditorTools +{ + /// + /// Batchmode check for campaign aetheria-gravity, cut ambient-follows-populated-area: the ambient tint falls softly to + /// dark and never hits an edge (ruling dark-beyond-populated-area). It opens ARPG.unity, takes the Ambient Fog Light the + /// scene wires into ZoneRenderer, sizes it for a generated zone with ZoneRenderer.FitAmbientLight (the call LoadZone + /// makes, not a copy), renders it through an orthographic camera that culls its layer into a target built from the descriptor of the production + /// Fog Tint render texture (B10G11R11_UFloatPack32, the format the Tint cameras write), and checks the + /// radial profile against exp(-(r/P)^2 ln(1/_EdgeBrightness)) times the centre, to within what that format stores (a + /// 5-bit mantissa) wherever the gaussian is at least a few ZoneRenderer.AmbientFloor (P is Zone.PopulatedRadius), that it + /// is lit at Pack.Radius, that the quad's rim and every lit texel beside a dark one are + /// at most two floors, and that no two lit texels are more than half a decade apart. + /// + /// Renders, so it needs a graphics device: run WITHOUT -nographics. + /// Unity -batchmode -projectPath <repo> -executeMethod Aetheria.EditorTools.AmbientEdgeVerify.Run -logFile <path> + /// Do not pass -quit: Run exits the Editor itself, 0 on success and 1 on any failure. + /// + public static class AmbientEdgeVerify + { + const string Tag = "[AmbientEdgeVerify]"; + const int Size = 512; + const int AmbientLayer = 13; + const string FogTintPath = "Assets/Resources/Fog Tint.renderTexture"; + // A float with a 5-bit mantissa (Fog Tint's blue) steps by 2^-5 of its value, and the target truncates to a step. + const float FormatRelativeError = 1f / 32f; + // The profile is held to the gaussian where it is at least this many floors; below that the format's tail is measured, not held. + const float FaithfulFloors = 8f; + // The rim and every lit texel beside a dark one hold at most this many floors, 2^-14 (the smallest normal of the format's + // floats): the one step to dark is the format's last value. Measured on the Fog Tint format it is 1.5 floors at most. + const float StepToDarkFloors = 2f; + static bool _ok = true; + + static void OnLog(string message, string stack, LogType type) + { + if ((type == LogType.Error || type == LogType.Exception || type == LogType.Assert) && !message.StartsWith(Tag)) + Fail("logged-error", message.Split('\n')[0]); + } + + static void Fail(string check, string detail) + { + Debug.LogError($"{Tag} FAIL {check}: {detail}"); + _ok = false; + } + + public static void Run() + { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null) + { + Debug.LogError($"{Tag} FAIL: no graphics device in this session; rerun batchmode without -nographics."); + EditorApplication.Exit(1); + return; + } + Application.logMessageReceived += OnLog; + var work = Path.Combine(Path.GetTempPath(), "ambient-edge-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(Path.Combine(work, "probe")); + try + { + var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset") + ?? throw new InvalidOperationException("No Settings.asset."); + // Generated zones have the populated radius at about .78 of the pack radius (p50 over 1500 zones): this one's + // pack radius is set so, which puts Pack.Radius where it sits in play, inside the quad's reach. + var probe = GravityFieldParityVerify.BuildZone(settings, Path.Combine(work, "probe"), out _); + var zone = GravityFieldParityVerify.BuildZone(settings, work, out _, probe.PopulatedRadius / .78f); + EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Single); + try + { + var renderer = UnityEngine.Object.FindFirstObjectByType(FindObjectsInactive.Include); + if (renderer == null || renderer.AmbientLight == null) throw new InvalidOperationException("ARPG.unity wires no ZoneRenderer.AmbientLight."); + Check(renderer, zone); + } + finally + { + EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + } + Debug.Log(_ok ? $"{Tag} PASS" : $"{Tag} FAIL: see errors above"); + } + catch (Exception e) + { + Debug.LogError($"{Tag} FAIL exception: {e}"); + _ok = false; + } + finally + { + Application.logMessageReceived -= OnLog; + try { Directory.Delete(work, true); } catch { } + } + EditorApplication.Exit(_ok ? 0 : 1); + } + + // A bare ambient quad for the verifies that run LoadZone on a renderer built in code: the scene's material, no scene. + public static Transform NewAmbientLight() + { + var quad = new GameObject("Ambient Fog Light") { layer = AmbientLayer }; + quad.AddComponent().sharedMesh = Resources.GetBuiltinResource("Quad.fbx"); + quad.AddComponent().sharedMaterial = AssetDatabase.LoadAssetAtPath("Assets/Materials/Brushes/Sector Tint.mat"); + return quad.transform; + } + + static void Check(ZoneRenderer renderer, Zone zone) + { + var production = AssetDatabase.LoadAssetAtPath(FogTintPath); + if (production == null || production.graphicsFormat != GraphicsFormat.B10G11R11_UFloatPack32) + Fail("format", $"Fog Tint is {(production == null ? "missing" : production.graphicsFormat.ToString())}, not the B10G11R11_UFloatPack32 that AmbientFloor and FormatRelativeError describe"); + var light = renderer.AmbientLight; + if (light.parent != null) Fail("wiring", $"the ambient quad sits under {light.parent.name}, whose scale is the pack radius's"); + if (light.gameObject.layer != AmbientLayer) Fail("wiring", $"the ambient quad is on layer {light.gameObject.layer}, not {AmbientLayer}"); + + ZoneRenderer.FitAmbientLight(light, zone); + var meshRenderer = light.GetComponent(); + var material = meshRenderer.sharedMaterial; + var edge = material.GetFloat("_EdgeBrightness"); + var tint = material.GetColor("_Color").linear; + var centre = material.GetFloat("_Depth") * Mathf.Max(tint.r, Mathf.Max(tint.g, tint.b)); + var populated = zone.PopulatedRadius; + var half = light.localScale.x / 2f; + Debug.Log($"{Tag} populated radius {populated:F1} u, pack radius {zone.Pack.Radius:F1} u, quad half-extent {half:F1} u, edge brightness {edge:G3}, centre {centre:G4}"); + if (!(populated > 0f) || !(half > populated)) { Fail("sizing", $"the quad half-extent {half:F1} u does not reach beyond the populated radius {populated:F1} u"); return; } + + Render(light, half * 1.05f, out var value, out var world); + var floor = ZoneRenderer.AmbientFloor; + Debug.Log($"{Tag} format floor {floor:E2} = {floor / centre:E2} of the centre; tail (drawn / floor): " + + string.Join(" ", System.Linq.Enumerable.Select(new[] { 1.5f, 1.75f, 1.9f, 2f, 2.05f, 2.1f, 2.15f, 2.2f }, + k => $"{k}P:{Sample(value, world, new Vector2(k * populated, 0f)) / floor:G3}"))); + var logFall = Mathf.Log(1f / edge); + foreach (var k in new[] { 0f, .5f, 1f, 1.5f, 2f }) + { + var drawn = Sample(value, world, new Vector2(k * populated, 0f)); + Debug.Log($"{Tag} r = {k} P: drawn {drawn:G5}, expected {centre * Mathf.Exp(-k * k * logFall):G5}"); + } + var worstProfile = 0f; + var compared = 0; + var rim = 0f; + for (var i = 0; i < value.Length; i++) + { + if (float.IsNaN(value[i])) { Fail("texels", $"texel {i}: NaN"); return; } + var r = world[i].magnitude; + var expected = centre * Mathf.Exp(-(r / populated) * (r / populated) * logFall); + if (r <= half && expected >= FaithfulFloors * floor) + { + var error = Mathf.Abs(value[i] - expected) / (FormatRelativeError * expected); + worstProfile = Mathf.Max(worstProfile, error); + compared++; + if (error > 1f) { Fail("profile", $"at r = {r / populated:F3} P: expected {expected:G5}, drawn {value[i]:G5}, off by {error:F2} times the format's step"); return; } + } + // The rim: the texels in the quad's outermost ring (within two texels of its edge). + var edgeDistance = half - Mathf.Max(Mathf.Abs(world[i].x), Mathf.Abs(world[i].y)); + if (edgeDistance >= 0f && edgeDistance < 2f * (half * 1.05f * 2f / Size)) rim = Mathf.Max(rim, value[i]); + } + Debug.Log($"{Tag} profile: {compared} texels held to the gaussian, worst error {worstProfile:F3} of the format's step; rim {rim / centre:E2} of the centre"); + if (compared < Size * Size / 16) Fail("profile", $"only {compared} texels were held to the gaussian; the check is vacuous"); + if (rim > floor * StepToDarkFloors) Fail("rim", $"the quad's rim is {rim / floor:G3} times the format floor"); + + var atPack = Sample(value, world, new Vector2(zone.Pack.Radius, 0f)); + Debug.Log($"{Tag} at Pack.Radius: {atPack / centre:E2} of the centre"); + if (!(atPack > 0f)) Fail("pack-radius", "the ambient is zero at Pack.Radius"); + + var worstJump = 0f; + for (var y = 0; y < Size; y++) + for (var x = 0; x < Size; x++) + { + var here = value[y * Size + x]; + if (here <= 0f) continue; + foreach (var next in new[] { x + 1 < Size ? value[y * Size + x + 1] : 0f, y + 1 < Size ? value[(y + 1) * Size + x] : 0f }) + { + if (next <= 0f) { if (here > floor * StepToDarkFloors) Fail("last-step", $"a lit texel of {here / floor:G3} floors sits beside a dark one"); continue; } + worstJump = Mathf.Max(worstJump, Mathf.Abs(Mathf.Log10(here / next))); + } + } + Debug.Log($"{Tag} largest jump between neighbouring lit texels: {worstJump:F3} decades (limit 0.5)"); + if (worstJump > .5f) Fail("jump", $"neighbouring texels differ by {worstJump:F2} decades"); + var lit = 0; + foreach (var v in value) if (v > 0f) lit++; + if (lit < Size * Size / 2) Fail("jump", $"only {lit} texels are lit; the check is vacuous"); + } + + static float Sample(float[] value, Vector2[] world, Vector2 at) + { + var best = 0; + for (var i = 1; i < world.Length; i++) + if ((world[i] - at).sqrMagnitude < (world[best] - at).sqrMagnitude) best = i; + return value[best]; + } + + // Renders the ambient layer through a downward orthographic camera centred on the origin, as the Tint cameras see it, + // and returns the brightest channel of every texel with the world xz of its centre, which is asked of the camera. + static void Render(Transform light, float half, out float[] value, out Vector2[] world) + { + var production = AssetDatabase.LoadAssetAtPath(FogTintPath) ?? throw new InvalidOperationException("No Fog Tint render texture."); + var rt = new RenderTexture(production.descriptor) { filterMode = FilterMode.Point }; + var cameraObject = new GameObject("Ambient Edge Camera"); + var read = new Texture2D(Size, Size, TextureFormat.RGBAFloat, false); + var previous = RenderTexture.active; + try + { + var camera = cameraObject.AddComponent(); + camera.orthographic = true; + camera.orthographicSize = half; + camera.aspect = 1f; + camera.nearClipPlane = .3f; + camera.farClipPlane = 1000f; + camera.cullingMask = 1 << light.gameObject.layer; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = Color.black; + camera.allowHDR = false; + camera.allowMSAA = false; + camera.targetTexture = rt; + cameraObject.transform.SetPositionAndRotation(new Vector3(0f, 100f, 0f), Quaternion.Euler(90f, 0f, 0f)); + camera.Render(); + + RenderTexture.active = rt; + read.ReadPixels(new Rect(0, 0, Size, Size), 0, 0); + read.Apply(); + var pixels = read.GetPixelData(0); + value = new float[Size * Size]; + for (var i = 0; i < value.Length; i++) + value[i] = Mathf.Max(pixels[4 * i], Mathf.Max(pixels[4 * i + 1], pixels[4 * i + 2])); + world = new Vector2[Size * Size]; + for (var y = 0; y < Size; y++) + for (var x = 0; x < Size; x++) + { + var p = camera.ViewportToWorldPoint(new Vector3((x + .5f) / Size, (y + .5f) / Size, 100f)); + world[y * Size + x] = new Vector2(p.x, p.z); + } + } + finally + { + RenderTexture.active = previous; + UnityEngine.Object.DestroyImmediate(cameraObject); + UnityEngine.Object.DestroyImmediate(read); + rt.Release(); + UnityEngine.Object.DestroyImmediate(rt); + } + } + } +} diff --git a/Assets/Scripts/Editor/AmbientEdgeVerify.cs.meta b/Assets/Scripts/Editor/AmbientEdgeVerify.cs.meta new file mode 100644 index 000000000..0962854de --- /dev/null +++ b/Assets/Scripts/Editor/AmbientEdgeVerify.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 63b03af05dfdae44c89e21ca7ebec892 \ No newline at end of file diff --git a/Assets/Scripts/Editor/BeltFloaterLayerVerify.cs b/Assets/Scripts/Editor/BeltFloaterLayerVerify.cs new file mode 100644 index 000000000..500ae1cea --- /dev/null +++ b/Assets/Scripts/Editor/BeltFloaterLayerVerify.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using GameCult.Caching; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using float2 = CultMath.float2; +using float4 = CultMath.float4; + +// End-to-end proof of the belt draw (rulings asteroid-tops-at-layer, rock-meshes-by-size), measured where it fails: two belts are loaded +// into a ZoneRenderer carrying ARPG.unity's real AsteroidMeshes (meshes, stored radii) and Settings.asset, over a zone whose wells make the +// field differ rock to rock, and ZoneRenderer.DrawBelts runs as Update runs it. Every instanced matrix it hands to the GPU is read back with the +// mesh it is drawn with (ZoneRenderer.DrawInstanced), and for each drawn rock the verify measures, from the mesh's real vertices: +// - the farthest vertex from the rock's centre after the matrix (the reach, a bound on the rock's top under any rotation) is the rock's +// gameplay radius, and centre y + reach is the floater layer over the field at the rock's xz and the drawn time; +// - the rock is drawn exactly once, on exactly one matrix of a real pose (no spare instance in a batch); +// - within a belt a bigger rock never takes a smaller mesh (by the mesh's own farthest vertex), and the belt keeps min(AsteroidMeshCount, meshes) meshes. +// The arguments the renderer hands the draw list (radius field, belt, batch size, kept count) are not inspected: their consequence is. +// +// Reached: ZoneRenderer.LoadPlanet for each belt, DrawBelts. Not reached: LoadZone's other planets (their prefabs), the GPU draw itself. +// Needs a GPU device for MapPresenter's belt buffer: run without -nographics. +// +// Unity -batchmode -projectPath -executeMethod Aetheria.EditorTools.BeltFloaterLayerVerify.Run -logFile +// +// Do not pass -quit: Run enters play mode (a domain reload), the check runs there, and the Editor exits itself, 0 on success and 1 on any failure. +namespace Aetheria.EditorTools +{ + [InitializeOnLoad] + public static class BeltFloaterLayerVerify + { + private const string Tag = "[BeltFloaterLayerVerify]"; + private const string Flag = "BeltFloaterLayerVerify.running"; + private const string Wiring = "BeltFloaterLayerVerify.wiring"; + private const double Budget = 300; // real seconds + private const float Lead = .008f, Layer = -1.5f, Tolerance = .02f; + private static IEnumerator _check; + private static double _started; + private static string _work; + + static BeltFloaterLayerVerify() + { + if (SessionState.GetBool(Flag, false)) EditorApplication.playModeStateChanged += OnPlayMode; + } + + public static void Run() + { + try + { + SessionState.SetString(Wiring, ReadWiring()); + SessionState.SetBool(Flag, true); + EditorApplication.EnterPlaymode(); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + private static void OnPlayMode(PlayModeStateChange change) + { + if (change != PlayModeStateChange.EnteredPlayMode) return; + _work = Path.Combine(Path.GetTempPath(), $"belt-floater-{Guid.NewGuid():N}"); + Directory.CreateDirectory(_work); + _started = Time.realtimeSinceStartupAsDouble; + _check = Check(); + EditorApplication.update += Step; + } + + private static void Step() + { + try + { + if (Time.realtimeSinceStartupAsDouble - _started > Budget) throw new InvalidOperationException($"The check did not finish in {Budget} seconds."); + if (_check.MoveNext()) return; + Finish(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + Finish(1); + } + } + + private static void Finish(int code) + { + EditorApplication.update -= Step; + SessionState.SetBool(Flag, false); + try { if (Directory.Exists(_work)) Directory.Delete(_work, true); } catch (Exception) { /* a temp directory */ } + EditorApplication.Exit(code); + } + + private static void Require(bool condition, string what) + { + if (!condition) throw new InvalidOperationException(what); + } + + // A belt of rocks of every size, its sizes in the given order, round an orbit about the planet. + private static Asteroid[] Rocks(int count, Func size, float start) => Enumerable.Range(0, count).Select(i => new Asteroid + { + Distance = start + i * 9.7f, Phase = Mathf.Repeat(i * .6180339887f, 1f), Size = size(i), RotationSpeed = .1f + i * .013f + }).ToArray(); + + private static IEnumerator Check() + { + var game = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset") ?? throw new InvalidOperationException("No Settings.asset."); + var settings = game.GameplaySettings; + settings.FloaterLayerHeight = Layer; // in memory, never saved back to the asset: a draw that drops the layer fails + + var meshes = SessionState.GetString(Wiring, "").Split('|').Select(entry => entry.Split('~')).Select(p => new InstancedMesh + { + Mesh = AssetDatabase.LoadAllAssetsAtPath(p[0]).OfType().First(m => m.name == p[1]), + Material = p[2] == "" ? null : AssetDatabase.LoadAssetAtPath(p[2]), + Radius = float.Parse(p[3], System.Globalization.CultureInfo.InvariantCulture) + }).ToArray(); + Require(meshes.Length > game.AsteroidMeshCount, $"ARPG.unity has {meshes.Length} rock meshes for a kept count of {game.AsteroidMeshCount}: nothing to prune."); + + var cache = AetheriaStores.Open(Path.Combine(_work, "belts.cc"), Path.Combine(_work, "belts-run.cc"), catalogWritable: true); + cache.Upsert(new VerseGrammar { Revision = 1 }); + cache.FlushAsync().Wait(); + var items = new ItemManager(1u, cache, new ProvenanceLedger(), settings, _ => { }); + + CultRecordKey Orbit(CultRecordKey parent, float distance, float phase, float2 fixedPosition = default) + { + var data = new OrbitData { Distance = distance, Phase = phase, FixedPosition = fixedPosition }; + if (parent.IsSet()) data.Parent = new CultRecordRef(parent); + return cache.Upsert(data).Key; + } + + // A heavy sun and a planet on a moving rail, the belts round the planet: the field under a rock differs from rock to rock. + var sunOrbit = Orbit(default, 0f, 0f, new float2(700f, -300f)); + var planetOrbit = Orbit(sunOrbit, 600f, .3f); + var beltOrbitA = Orbit(planetOrbit, 150f, .1f); + var beltOrbitB = Orbit(planetOrbit, 300f, .6f); + var beltA = cache.Upsert(new AsteroidBeltData { Orbit = new CultRecordRef(beltOrbitA), Asteroids = Rocks(24, i => (i * 7 % 24) / 23f, 40f) }).Key; + var beltB = cache.Upsert(new AsteroidBeltData { Orbit = new CultRecordRef(beltOrbitB), Asteroids = Rocks(24, i => (i * 5 % 24) / 23f, 60f) }).Key; + var pack = new ZonePack { Radius = 5000f, Mass = 10000f }; + foreach (var orbit in new[] { sunOrbit, planetOrbit, beltOrbitA, beltOrbitB }) pack.Orbits.Add(new CultRecordRef(orbit)); + pack.Planets.Add(new CultRecordRef(cache.Upsert(new PlanetData { Name = "Belt Sun", Orbit = new CultRecordRef(sunOrbit), Mass = 400f }).Key)); + pack.Planets.Add(new CultRecordRef(cache.Upsert(new PlanetData { Name = "Belt Planet", Orbit = new CultRecordRef(planetOrbit), Mass = 400f }).Key)); + pack.Planets.Add(new CultRecordRef(beltA)); + pack.Planets.Add(new CultRecordRef(beltB)); + var curves = new PlanetSettings + { + BodyRadius = new ExponentialCurve(), + WellStrength = new ExponentialCurve { Multiplier = 1f, Exponent = 1f }, + RocheLimit = new ExponentialCurve { Constant = 10f }, + AsteroidSize = new ExponentialLerp { Minimum = 2f, Maximum = 9f, Exponent = 1.7f }, + AsteroidHitpoints = new ExponentialLerp { Minimum = 10f, Maximum = 300f, Exponent = 2f }, + AsteroidRespawnTime = new ExponentialLerp { Minimum = 5f, Maximum = 60f, Exponent = 1f } + }; + var zone = new Zone(items, curves, pack, null, null); + zone.Update(.5f); + + // The renderer, configured as the scene configures it for belts: Awake builds the map, LoadPlanet reads Zone, Settings and AsteroidMeshes. + var rendererObject = new GameObject("Belt Zone Renderer"); + rendererObject.SetActive(false); + var renderer = rendererObject.AddComponent(); + renderer.Settings = game; + renderer.AsteroidMeshes = meshes; + renderer.ItemManager = items; + renderer.LODHandler = new GameObject("Belt LOD").AddComponent(); + const BindingFlags Any = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + typeof(ZoneRenderer).GetProperty("Zone").GetSetMethod(true).Invoke(renderer, new object[] { zone }); + typeof(ZoneRenderer).GetMethod("Awake", Any).Invoke(renderer, null); + var load = typeof(ZoneRenderer).GetMethod("LoadPlanet", Any); + foreach (var belt in new[] { beltA, beltB }) load.Invoke(renderer, new object[] { belt, zone.Planets[belt] }); + + var planetAt = DrawAhead.Orbit(zone, planetOrbit, Lead); + var camera = new GameObject("Belt Camera").AddComponent(); + camera.transform.position = new Vector3(planetAt.x, 4000f, planetAt.y); + camera.transform.rotation = Quaternion.Euler(90, 0, 0); + camera.farClipPlane = 20000f; + renderer.MainCamera = camera; + yield return null; + + ActionGameManager.Clock.Reset(); + ActionGameManager.Clock.Advance(Lead, _ => throw new InvalidOperationException("a step ran")); + var lead = ActionGameManager.Clock.Lead; + Require(Mathf.Abs(lead - Lead) < 1e-6f, $"The clock's lead is {lead}, not {Lead}."); + + var drawn = new List<(Mesh mesh, Matrix4x4[] matrices)>(); + renderer.DrawInstanced = (mesh, _, matrices) => drawn.Add((mesh, (Matrix4x4[]) matrices.Clone())); + renderer.DrawBelts(lead); + + // The poses the sim defines at the drawn time: every rock of both belts. + var rocks = new List<(CultRecordKey belt, int index, float4 pose)>(); + foreach (var belt in new[] { beltA, beltB }) + { + var poses = new float4[((AsteroidBeltData) zone.Planets[belt]).Asteroids.Length]; + zone.EvaluateBelt(belt, poses, DrawAhead.Time(zone, lead)); + for (var i = 0; i < poses.Length; i++) rocks.Add((belt, i, poses[i])); + } + Require(drawn.Count > 0, "DrawBelts drew nothing: the belts are out of the camera or have no rocks."); + + // The field differs between rocks, so a draw that drops it or samples it at the wrong place fails. + var layers = rocks.Select(r => DrawAhead.FloaterHeight(zone, settings, r.pose.xy, lead)).ToArray(); + Require(layers.Max() - layers.Min() > 1f, $"The fixture's field is flat under the rocks: layer heights span {layers.Max() - layers.Min()}."); + + var seen = new Dictionary<(CultRecordKey, int), float>(); // rock -> its mesh's own farthest vertex + var vertices = new Dictionary(); + foreach (var (mesh, matrices) in drawn) + { + if (!vertices.TryGetValue(mesh, out var v)) vertices[mesh] = v = mesh.vertices; + var own = Mathf.Sqrt(v.Max(x => x.sqrMagnitude)); + foreach (var matrix in matrices) + { + var centre = (Vector3) matrix.GetColumn(3); + var at = rocks.Where(r => Mathf.Abs(r.pose.x - centre.x) < .01f && Mathf.Abs(r.pose.y - centre.z) < .01f).ToArray(); + Require(at.Length == 1, $"An instance of {mesh.name} sits at {centre.x},{centre.z}, where {at.Length} rocks are (a spare or misplaced instance)."); + var rock = at[0]; + Require(seen.TryAdd((rock.belt, rock.index), own), $"Rock {rock.index} of {rock.belt} is drawn twice."); + var reach = v.Max(x => (matrix.MultiplyPoint3x4(x) - centre).magnitude); + Require(Mathf.Abs(reach - rock.pose.w) < Tolerance * Mathf.Max(1f, rock.pose.w), + $"Rock {rock.index} of {rock.belt} on {mesh.name}: its farthest vertex is {reach} from its centre, its gameplay radius {rock.pose.w}."); + var top = centre.y + reach; + var want = DrawAhead.FloaterHeight(zone, settings, rock.pose.xy, lead); + Require(Mathf.Abs(top - want) < Tolerance, + $"Rock {rock.index} of {rock.belt} on {mesh.name}: its top is {top}, the floater layer over the field there {want}."); + } + } + Require(seen.Count == rocks.Count, $"{seen.Count} of {rocks.Count} rocks were drawn."); + + foreach (var belt in new[] { beltA, beltB }) + { + var mine = rocks.Where(r => r.belt.Equals(belt)).ToArray(); + var radii = mine.Select(r => seen[(belt, r.index)]).Distinct().OrderBy(x => x).ToArray(); + Require(radii.Length >= 2, $"Belt {belt} is drawn on {radii.Length} distinct mesh radii: the size match is not exercised."); + var kept = drawn.Where(d => d.matrices.Any(m => mine.Any(r => Mathf.Abs(r.pose.x - m.m03) < .01f && Mathf.Abs(r.pose.y - m.m23) < .01f))).Select(d => d.mesh).Distinct().Count(); + Require(kept == Mathf.Min(game.AsteroidMeshCount, meshes.Length), $"Belt {belt} is drawn on {kept} meshes, not the kept count {game.AsteroidMeshCount}."); + var size = mine.ToDictionary(r => r.index, r => zone.AsteroidBelts[belt].UndamagedSize(r.index, zone.Settings)); + foreach (var a in mine) + foreach (var b in mine) + if (size[a.index] < size[b.index]) + Require(seen[(belt, a.index)] <= seen[(belt, b.index)], + $"Belt {belt}: rock {a.index} (size {size[a.index]}) is smaller than rock {b.index} ({size[b.index]}) and takes a bigger mesh ({seen[(belt, a.index)]} against {seen[(belt, b.index)]})."); + var biggest = mine.OrderByDescending(r => size[r.index]).First(); + Require(Mathf.Approximately(seen[(belt, biggest.index)], radii[^1]), $"Belt {belt}: its biggest rock is not on its biggest mesh."); + } + ActionGameManager.Clock.Reset(); + Debug.Log($"{Tag} ok: {rocks.Count} rocks of 2 belts drawn at the floater layer, big rocks on big meshes ({drawn.Count} batches)"); + } + + // The AsteroidMeshes ARPG.unity's ZoneRenderer names, read before the play session (a scene cannot be opened inside one) and carried across the + // domain reload as asset paths: mesh path, mesh name, material path, stored radius. + private static string ReadWiring() + { + var scene = EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Additive); + try + { + var found = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None) + .Where(r => r.gameObject.scene == scene).ToArray(); + Require(found.Length == 1, $"ARPG.unity has {found.Length} ZoneRenderers."); + var meshes = found[0].AsteroidMeshes ?? throw new InvalidOperationException("ARPG.unity's ZoneRenderer has no AsteroidMeshes."); + return string.Join("|", meshes.Select(m => string.Join("~", AssetDatabase.GetAssetPath(m.Mesh), m.Mesh.name, + m.Material == null ? "" : AssetDatabase.GetAssetPath(m.Material), m.Radius.ToString("R", System.Globalization.CultureInfo.InvariantCulture)))); + } + finally { EditorSceneManager.CloseScene(scene, true); } + } + } +} diff --git a/Assets/Scripts/Editor/BeltFloaterLayerVerify.cs.meta b/Assets/Scripts/Editor/BeltFloaterLayerVerify.cs.meta new file mode 100644 index 000000000..a383593c7 --- /dev/null +++ b/Assets/Scripts/Editor/BeltFloaterLayerVerify.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7d25a93db1972324fa5460b2415c1e4a \ No newline at end of file diff --git a/Assets/Scripts/Editor/CapabilityPresentationSmoke.cs b/Assets/Scripts/Editor/CapabilityPresentationSmoke.cs index 75d97c63f..135163e24 100644 --- a/Assets/Scripts/Editor/CapabilityPresentationSmoke.cs +++ b/Assets/Scripts/Editor/CapabilityPresentationSmoke.cs @@ -62,7 +62,7 @@ public static class CapabilityPresentationSmoke cache.FlushAsync().Wait(); var ledger = new ProvenanceLedger(); for (var i = 1; i <= 8; i++) ledger.Lots[i] = new Lot { Origin = new Attributed(), Quality = 1 }; - var items = new ItemManager(cache, ledger, settings, _ => { }); + var items = new ItemManager(1u, cache, ledger, settings, _ => { }); var zone = new Zone(items, new PlanetSettings(), new ZonePack(), new GalaxyZone { Name = "Smoke", Owner = null }, null); var hullRef = items.ItemData.RefOf(items.ItemData.GetByName("Smoke Hull")); var ship = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000, Lot = 1 }, new EntitySettings()); @@ -85,6 +85,11 @@ public static class CapabilityPresentationSmoke ?? throw new InvalidOperationException("No Tractor Beam prefab."); // An entity's instance as the game builds it: the beam is a child before SetEntity on both ship paths, so the // binder finds it as the game does. + // The scene's map: an entity registers its icon with the ZoneRenderer's presenter, so each instance carries a quad. + var mapRenderer = new GameObject("Smoke Zone Renderer").AddComponent(); + mapRenderer.Settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset"); + mapRenderer.MapGravityMaterials = new Material[0]; + Awaken(mapRenderer); (GameObject Object, EntityInstance Instance, TractorBeam Beam) Bound(string name, Entity entity) { var go = new GameObject(name); @@ -98,7 +103,11 @@ public static class CapabilityPresentationSmoke var tractor = ((GameObject)PrefabUtility.InstantiatePrefab(beamPrefab, go.transform)).GetComponent(); tractor.Power = .8f; tractor.Direction = Vector3.forward; - bound.SetEntity(null, entity); + var icon = GameObject.CreatePrimitive(PrimitiveType.Quad); + icon.transform.SetParent(go.transform); + bound.MapIcon = icon.GetComponent(); + bound.MapIcon.sharedMaterial = new Material(Shader.Find("Unlit/Color")); + bound.SetEntity(mapRenderer, entity); return (go, bound, tractor); } var (instanceObject, instance, beam) = Bound("Smoke Entity Instance", ship); diff --git a/Assets/Scripts/Editor/CultCacheDrawers.cs b/Assets/Scripts/Editor/CultCacheDrawers.cs index c446212db..27d21afec 100644 --- a/Assets/Scripts/Editor/CultCacheDrawers.cs +++ b/Assets/Scripts/Editor/CultCacheDrawers.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System; using System.Linq; using System.Reflection; @@ -87,14 +88,13 @@ public sealed class InspectableTemperatureDrawer : ICultInspectorDrawer } } -// A size and a toggle per cell. When the owning item has a schematic texture, the texture lies under the grid and the -// height follows its aspect; a hull's hardpoint cells are tinted. Both read the owning record through +// A size and a toggle per cell. The underlay (the item's schematic texture, or a hull's line art stretched to the Shape's +// Width x Height) is drawn by HullUnderlay; the height follows a texture's aspect. A hull's hardpoint cells are tinted. +// Both read the owning record through // inspector.Record, which is a read-only copy: nothing here writes to it. [CultInspectorDrawer(typeof(InspectableSchematicShapeAttribute))] public sealed class InspectableSchematicShapeDrawer : ICultInspectorDrawer { - private static Material _schematicMaterial; - public object Draw(CultInspector inspector, string label, Type type, object value, MemberInfo member) { if (type != typeof(Shape)) return inspector.DrawDefault(label, type, value, member); @@ -122,18 +122,13 @@ public sealed class InspectableSchematicShapeDrawer : ICultInspectorDrawer { using (var grid = new EditorGUILayout.VerticalScope()) { - if (schematic != null && Event.current.type == EventType.Repaint) - { - _schematicMaterial ??= new Material(Shader.Find("Legacy Shaders/Particles/Additive")); - EditorGUI.DrawPreviewTexture(grid.rect, schematic, _schematicMaterial); - } + HullUnderlay.Draw(grid.rect, item, inspector.Records); for (var y = shape.Height - 1; y >= 0; y--) using (new EditorGUILayout.HorizontalScope()) for (var x = 0; x < shape.Width; x++) { - var hardpoint = hull?.Hardpoints.FirstOrDefault(hp => hp.Shape.Coordinates - .Any(v => v.x + hp.Position.x == x && v.y + hp.Position.y == y)); + var hardpoint = hull?.Hardpoints.FirstOrDefault(hp => HullFootprint.Cells(hp).Contains(int2(x, y))); var previous = GUI.backgroundColor; if (hardpoint != null) GUI.backgroundColor = hardpoint.TintColor.ToColor(); shape[int2(x, y)] = GUILayout.Toggle(shape[int2(x, y)], GUIContent.none, GUILayout.Width(16)); @@ -145,3 +140,93 @@ public sealed class InspectableSchematicShapeDrawer : ICultInspectorDrawer return shape; } } + +// The underlay under a hull's cell grids, drawn on Repaint only. A shipped hull's schematic texture is stretched over +// the grid rect; a mod hull's line art (its ShipAuthoring, found in the open cache at hull.Visual) is placed by +// SchematicUnderlay.Fit in cell units and mapped to the rect here, the one site that maps cells to a grid's screen +// rect. The hull Shape grid and the footprint painter both draw through it. No lookup found: no line underlay. +public static class HullUnderlay +{ + private static Material _schematicMaterial; + + public static void Draw(Rect grid, EquippableItemData item, IReadOnlyList records) + { + if (item == null || Event.current.type != EventType.Repaint) return; + var texture = string.IsNullOrEmpty(item.Schematic) ? null : AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(item.Schematic)); + if (texture != null) + { + _schematicMaterial ??= new Material(Shader.Find("Legacy Shaders/Particles/Additive")); + EditorGUI.DrawPreviewTexture(grid, texture, _schematicMaterial); + } + + if (!(item is HullData hull) || hull.Shape == null || hull.Shape.Width == 0 || hull.Shape.Height == 0) return; + var key = CultInspectorModel.RecordKey(hull.Visual); + var authoring = records?.FirstOrDefault(record => record.Key.Value == key && record.Document is ShipAuthoring)?.Document as ShipAuthoring; + if (authoring == null) return; + + var lines = authoring.SchematicLines; + var fit = SchematicUnderlay.Fit(lines, hull.Shape); + for (var i = 0; i < fit.Length; i++) + { + var rgba = lines[i].Color; + var color = rgba == null ? Color.white : new Color(rgba[0], rgba[1], rgba[2], rgba[3]); + var points = fit[i].Select(p => new Vector3( + grid.x + p.x / hull.Shape.Width * grid.width, + grid.yMax - p.y / hull.Shape.Height * grid.height)).ToList(); + if (lines[i].Cyclic && points.Count > 2) points.Add(points[0]); + using (new Handles.DrawingScope(color)) Handles.DrawAAPolyLine(2f, points.ToArray()); + } + } +} + +// A hardpoint's schematic footprint over its hull's cells: the hull's occupied cells shaded, this hardpoint's cells lit +// in its tint, other hardpoints' cells tinted and disabled. A click edits Position and Shape together through +// HullFootprint.Toggle, which owns the rule so a Thing host reads the same one. The footprint is authored for gameplay, +// never derived from the model. Read-only on inspector.Record; only the HardpointData it is given changes. +[CultInspectorDrawer(typeof(HardpointData))] +public sealed class HardpointFootprintDrawer : ICultInspectorDrawer +{ + public object Draw(CultInspector inspector, string label, Type type, object value, MemberInfo member) + { + var hardpoint = value as HardpointData; + if (type != typeof(HardpointData) || hardpoint == null) return inspector.DrawDefault(label, type, value, member); + var hull = inspector.Record as HullData; + var own = hull?.Hardpoints.FirstOrDefault(hp => hp.Type == hardpoint.Type && hp.Position.Equals(hardpoint.Position) + && HullFootprint.Cells(hp).SequenceEqual(HullFootprint.Cells(hardpoint))); + var drawn = (HardpointData) inspector.DrawDefault(label, type, value, member); + if (hull?.Shape == null) return drawn; + + var others = hull.Hardpoints.Where(hp => hp != own).ToArray(); + EditorGUILayout.LabelField("Footprint"); + using (new EditorGUILayout.HorizontalScope()) + { + using (var grid = new EditorGUILayout.VerticalScope()) + { + HullUnderlay.Draw(grid.rect, hull, inspector.Records); + for (var y = hull.Shape.Height - 1; y >= 0; y--) + using (new EditorGUILayout.HorizontalScope()) + for (var x = 0; x < hull.Shape.Width; x++) + { + var cell = int2(x, y); + var other = others.FirstOrDefault(hp => HullFootprint.Cells(hp).Contains(cell)); + var on = HullFootprint.Cells(drawn).Contains(cell); + var previous = GUI.backgroundColor; + GUI.backgroundColor = on ? drawn.TintColor.ToColor() : other != null ? other.TintColor.ToColor() + : hull.Shape[cell] ? Color.gray : previous; + EditorGUI.BeginChangeCheck(); + using (new EditorGUI.DisabledScope(other != null && !on)) + GUILayout.Toggle(on || other != null, GUIContent.none, GUILayout.Width(16)); + if (EditorGUI.EndChangeCheck() && HullFootprint.Toggle(drawn.Shape, drawn.Position, cell, out var shape, out var position)) + { + drawn.Shape = shape; + drawn.Position = position; + GUI.changed = true; + } + GUI.backgroundColor = previous; + } + } + GUILayout.FlexibleSpace(); + } + return drawn; + } +} diff --git a/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs b/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs new file mode 100644 index 000000000..69109a622 --- /dev/null +++ b/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections; +using System.IO; +using System.Linq; +using GameCult.Caching; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using float2 = CultMath.float2; +using float3 = CultMath.float3; + +// Batch proof that the presenters read the drawn pose through the clock: DrawnPoseTests pins the pose maths headlessly, +// and this is what pins that each presenter passes ActionGameManager.Clock.Lead into it. A zone with a sun and a planet on a moving rail +// is built and the pickup and mine prefabs ARPG.unity's ZoneRenderer names are bound to it as the renderer binds them; a loose Projectile is launched on the +// zone. The zone is stepped once, then the clock is set to leads 0, 0.008 and 0.015 s with no step, and each presenter's +// own Update places its transform. A body is drawn at its last step plus its step velocity times the lead, spelled here +// from the body's fields, so a presenter that reads lead 0 (or half the lead) sits on the step and fails. +// +// Reached: ItemPickup, MineInstance, Projectile.Draw. Not reached: ZoneRenderer.Update (Lower needs a ComputeBuffer, a +// GPU device) and the ActionGameManager markers (they need the ARPG scene running); a Lower split so its record fill is +// pure, or a run with a GPU and ARPG.unity in play mode, would reach them. +// +// Unity -batchmode -nographics -projectPath -executeMethod Aetheria.EditorTools.DrawnPoseWiringVerify.Run -logFile +// +// Do not pass -quit: Run enters play mode (a domain reload), the check runs there, and the Editor exits itself, 0 on +// success and 1 on any failure. +namespace Aetheria.EditorTools +{ + [InitializeOnLoad] + public static class DrawnPoseWiringVerify + { + private const string Tag = "[DrawnPoseWiringVerify]"; + private const string Flag = "DrawnPoseWiringVerify.running"; + private const string Wiring = "DrawnPoseWiringVerify.wiring"; + private const double Budget = 300; // real seconds + private const float RoundSpeed = 100f, Tolerance = 1e-3f; + private static IEnumerator _check; + private static double _started; + private static string _work; + + static DrawnPoseWiringVerify() + { + if (SessionState.GetBool(Flag, false)) EditorApplication.playModeStateChanged += OnPlayMode; + } + + public static void Run() + { + try + { + SessionState.SetString(Wiring, ReadWiring()); + SessionState.SetBool(Flag, true); + EditorApplication.EnterPlaymode(); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + private static void OnPlayMode(PlayModeStateChange change) + { + if (change != PlayModeStateChange.EnteredPlayMode) return; + _work = Path.Combine(Path.GetTempPath(), $"drawn-pose-wiring-{Guid.NewGuid():N}"); + Directory.CreateDirectory(_work); + _started = Time.realtimeSinceStartupAsDouble; + _check = Check(); + EditorApplication.update += Step; + } + + private static void Step() + { + try + { + if (Time.realtimeSinceStartupAsDouble - _started > Budget) throw new InvalidOperationException($"The check did not finish in {Budget} seconds."); + if (_check.MoveNext()) return; + Finish(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + Finish(1); + } + } + + private static void Finish(int code) + { + EditorApplication.update -= Step; + SessionState.SetBool(Flag, false); + try { if (Directory.Exists(_work)) Directory.Delete(_work, true); } catch (Exception) { /* a temp directory */ } + EditorApplication.Exit(code); + } + + private static void Require(bool condition, string what) + { + if (!condition) throw new InvalidOperationException(what); + } + + private static IEnumerator Check() + { + var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset")?.GameplaySettings + ?? throw new InvalidOperationException("No Settings.asset."); + settings.FloaterLayerHeight = -1.5f; // nonzero, so a presenter that drops the floater layer fails + + var wired = SessionState.GetString(Wiring, "").Split('|').Select(entry => entry.Split('=')).ToDictionary(pair => pair[0], pair => pair[1]); + var pickupPrefab = AssetDatabase.LoadAssetAtPath(wired["SimpleCommodityPickup"])?.GetComponent(); + var minePrefab = AssetDatabase.LoadAssetAtPath(wired["MinePrefab"])?.GetComponent(); + Require(pickupPrefab != null, "ZoneRenderer.SimpleCommodityPickup is not an ItemPickup in ARPG.unity."); + Require(minePrefab != null, "ZoneRenderer.MinePrefab is not a MineInstance in ARPG.unity."); + + var oneCell = new Shape(1, 1); + oneCell[new CultMath.int2(0, 0)] = true; + var cache = AetheriaStores.Open(Path.Combine(_work, "wiring.cc"), Path.Combine(_work, "wiring-run.cc"), catalogWritable: true); + cache.Upsert(new VerseGrammar { Revision = 1 }); + cache.Upsert(new SimpleCommodityData { Name = "Wiring Ore", Shape = oneCell, MaxStack = 100 }); + cache.FlushAsync().Wait(); + var ledger = new ProvenanceLedger(); + var items = new ItemManager(1u, cache, ledger, settings, _ => { }); + + // A sun and a planet on a moving rail round it, both near the bodies below: the field under a floater changes with the + // drawn time, so a presenter that samples it at the step's time, drops it, or reads lead 0 for the height draws a different y. + var sunOrbit = cache.Upsert(new OrbitData { FixedPosition = new float2(-55, -.5f) }).Key; + var planetOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef(sunOrbit), Distance = 40f, Phase = 0f }).Key; + var pack = new ZonePack { Radius = 5000f, Mass = 10000f }; + pack.Orbits.Add(new CultRecordRef(sunOrbit)); + pack.Orbits.Add(new CultRecordRef(planetOrbit)); + pack.Planets.Add(new CultRecordRef(cache.Upsert(new PlanetData { Name = "Wiring Sun", Orbit = new CultRecordRef(sunOrbit), Mass = 400f }).Key)); + pack.Planets.Add(new CultRecordRef(cache.Upsert(new PlanetData { Name = "Wiring Planet", Orbit = new CultRecordRef(planetOrbit), Mass = 400f }).Key)); + var curves = new PlanetSettings + { + BodyRadius = new ExponentialCurve(), + WellStrength = new ExponentialCurve { Multiplier = 1f, Exponent = 1f }, + RocheLimit = new ExponentialCurve { Constant = 10f } + }; + var zone = new Zone(items, curves, pack, null, null); + + var rootObject = new GameObject("Wiring Zone Root"); + + var ore = new SimpleCommodity { Data = items.ItemData.RefOf(items.ItemData.GetByName("Wiring Ore")), Quantity = 7 }; + var floating = zone.Release(ore, new float2(-17, 4), new float2(10, 0)); + var mine = new Mine + { + Body = new KinematicBody { Position = new float2(-20, 8), Velocity = new float2(0, 14) }, + ArmingDelay = 1e6f, FuseDelay = 1f, Lifetime = 1e6f, BlastRadius = 1f + }; + zone.Lay(mine); + + // Bound the way ZoneRenderer.LoadFloatingItem and LoadMine bind them: the prefab ARPG.unity names, Bind(zone, body, settings). + // (A ZoneRenderer would load this zone's sun and planet too, and that needs the planet prefab, the map and a GPU.) + var pickup = UnityEngine.Object.Instantiate(pickupPrefab, rootObject.transform); + pickup.Bind(zone, floating, settings); + var mineInstance = UnityEngine.Object.Instantiate(minePrefab, rootObject.transform); + mineInstance.Bind(zone, mine, settings); + + // A round on its frozen line, no target and no outcome known: drawn at FireOrigin + direction * speed * (T - FireTime). + var shot = new PendingShot + { + ShotId = 1, FireOrigin = new float3(0, 0, 0), FireTime = 0f, TravelDirection = new float2(1, 0), Speed = RoundSpeed, + MaxRange = 1e6f, FireRange = 1e6f, CommitTime = 1e5f, ArrivalTime = 2e5f + }; + var projectile = new GameObject("Wiring Round").AddComponent(); + projectile.Launch(null, zone, shot, new Vector3(0, 0, 0)); + yield return null; + + zone.Update(.5f); + + + var pickupVelocity = floating.Body.TotalVelocity; + var mineVelocity = mine.Body.TotalVelocity; + Require(Mathf.Abs(pickupVelocity.x) > 1f && Mathf.Abs(mineVelocity.y) > 1f, "The fixture's bodies stopped: a lead would not move them."); + + // The field under each floater really is a function of the drawn time here, by more than the tolerance at the largest lead. + const float LargestLead = .015f; + foreach (var at in new[] { floating.Body.Position + pickupVelocity * LargestLead, mine.Body.Position + mineVelocity * LargestLead }) + Require(Mathf.Abs(DrawAhead.Height(zone, at, LargestLead) - zone.GetHeight(at, zone.ExactTime)) > 5 * Tolerance, + $"The fixture's field is flat under a floater: height {DrawAhead.Height(zone, at, LargestLead)} at the drawn time, {zone.GetHeight(at, zone.ExactTime)} at the step, planet at {zone.OrbitPositionAt(planetOrbit, zone.ExactTime)}, floater at {at}."); + + Vector2 baseline = default; + foreach (var leadSeconds in new[] { 0f, .008f, .015f }) + { + ActionGameManager.Clock.Reset(); + if (leadSeconds > 0) ActionGameManager.Clock.Advance(leadSeconds, _ => throw new InvalidOperationException("a step ran")); + var lead = ActionGameManager.Clock.Lead; + Require(Mathf.Abs(lead - leadSeconds) < 1e-6f, $"The clock's lead is {lead}, not {leadSeconds}."); + yield return null; + + var seen = pickup.transform.position; + var want = floating.Body.Position + pickupVelocity * lead; + Require(Mathf.Abs(seen.x - want.x) < Tolerance && Mathf.Abs(seen.z - want.y) < Tolerance, + $"ItemPickup at lead {lead}: drawn x/z {seen.x},{seen.z}, the step plus velocity times lead {want.x},{want.y}."); + + Require(Mathf.Abs(seen.y - DrawAhead.FloaterHeight(zone, settings, new float2(want.x, want.y), lead)) < Tolerance, + $"ItemPickup y {seen.y} is not on the floater layer."); + + seen = mineInstance.transform.position; + want = mine.Body.Position + mineVelocity * lead; + Require(Mathf.Abs(seen.x - want.x) < Tolerance && Mathf.Abs(seen.z - want.y) < Tolerance, + $"MineInstance at lead {lead}: drawn x/z {seen.x},{seen.z}, the step plus velocity times lead {want.x},{want.y}."); + Require(Mathf.Abs(seen.y - DrawAhead.FloaterHeight(zone, settings, new float2(want.x, want.y), lead)) < Tolerance, + $"MineInstance y {seen.y} is not on the floater layer."); + + // The round's line is a function of the drawn time: it moves RoundSpeed * lead along its direction between leads. + seen = projectile.transform.position; + if (leadSeconds == 0f) baseline = new Vector2(seen.x, seen.z); + var moved = new Vector2(seen.x, seen.z) - baseline; + Require(Mathf.Abs(moved.x - RoundSpeed * lead) < Tolerance && Mathf.Abs(moved.y) < Tolerance, + $"Projectile at lead {lead}: drawn {moved.x},{moved.y} past its lead-0 pose, expected {RoundSpeed * lead},0."); + } + ActionGameManager.Clock.Reset(); + Debug.Log($"{Tag} ok: ItemPickup, MineInstance and Projectile draw at the clock's lead (0, 0.008, 0.015 s)"); + } + + // What ARPG.unity's ZoneRenderer names in the pickup and mine fields, read from the saved scene before the play session + // (a scene cannot be opened inside one) and carried across the domain reload as asset paths. + private static string ReadWiring() + { + var scene = EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Additive); + try + { + var found = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None) + .Where(r => r.gameObject.scene == scene).ToArray(); + Require(found.Length == 1, $"ARPG.unity has {found.Length} ZoneRenderers."); + var wired = found[0]; + return string.Join("|", new (string, UnityEngine.Object)[] + { + ("SimpleCommodityPickup", wired.SimpleCommodityPickup), ("MinePrefab", wired.MinePrefab) + }.Select(f => f.Item1 + "=" + (f.Item2 == null ? "" : AssetDatabase.GetAssetPath(f.Item2)))); + } + finally { EditorSceneManager.CloseScene(scene, true); } + } + } +} diff --git a/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs.meta b/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs.meta new file mode 100644 index 000000000..534bebdb6 --- /dev/null +++ b/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c2270cf68a4af384eb715c8b5a55f5e8 \ No newline at end of file diff --git a/Assets/Scripts/Editor/FloatingItemsDrawnSmoke.cs b/Assets/Scripts/Editor/FloatingItemsDrawnSmoke.cs index 50f6fcb94..742e8aa9d 100644 --- a/Assets/Scripts/Editor/FloatingItemsDrawnSmoke.cs +++ b/Assets/Scripts/Editor/FloatingItemsDrawnSmoke.cs @@ -24,7 +24,8 @@ public static class FloatingItemsDrawnSmoke private const string Flag = "FloatingItemsDrawnSmoke.running"; private const string Wiring = "FloatingItemsDrawnSmoke.wiring"; private static IEnumerator _check; - private static int _frames; + private const double Budget = 300; // real seconds: batch frames can be far shorter than a game second + private static double _started; private static string _work; static FloatingItemsDrawnSmoke() @@ -52,6 +53,7 @@ public static class FloatingItemsDrawnSmoke if (change != PlayModeStateChange.EnteredPlayMode) return; _work = Path.Combine(Path.GetTempPath(), $"floating-items-smoke-{Guid.NewGuid():N}"); Directory.CreateDirectory(_work); + _started = Time.realtimeSinceStartupAsDouble; _check = Check(); EditorApplication.update += Step; } @@ -60,7 +62,7 @@ public static class FloatingItemsDrawnSmoke { try { - if (++_frames > 3000) throw new InvalidOperationException("The check did not finish in 3000 frames."); + if (Time.realtimeSinceStartupAsDouble - _started > Budget) throw new InvalidOperationException($"The check did not finish in {Budget} seconds."); if (_check.MoveNext()) return; Finish(0); } @@ -88,6 +90,8 @@ public static class FloatingItemsDrawnSmoke { var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset")?.GameplaySettings ?? throw new InvalidOperationException("No Settings.asset."); + // A floater rides its own layer: a nonzero one (never saved back to the asset), so a presenter that drops it fails. + settings.FloaterLayerHeight = -1.5f; // The wiring: the scene's ZoneRenderer names an ItemPickup prefab in each of the four fields, and none of those // prefabs keeps a script that no longer exists. @@ -120,7 +124,7 @@ public static class FloatingItemsDrawnSmoke cache.FlushAsync().Wait(); var ledger = new ProvenanceLedger(); for (var i = 1; i <= 8; i++) ledger.Lots[i] = new Lot { Origin = new Attributed(), Quality = 1 }; - var items = new ItemManager(cache, ledger, settings, _ => { }); + var items = new ItemManager(1u, cache, ledger, settings, _ => { }); var zone = new Zone(items, new PlanetSettings(), new ZonePack(), null, null); EquippableItem Make(string name, int lot) => new EquippableItem { @@ -145,6 +149,7 @@ public static class FloatingItemsDrawnSmoke typeof(ZoneRenderer).GetField(name).SetValue(renderer, pickup); renderer.ItemManager = items; renderer.SectorBrushes = new GameObject("Smoke Brushes").transform; + renderer.AmbientLight = Aetheria.EditorTools.AmbientEdgeVerify.NewAmbientLight(); renderer.SlimeGravityCamera = new GameObject("Smoke Gravity Camera").AddComponent(); var slimeObject = new GameObject("Smoke Slime"); slimeObject.SetActive(false); // the real Slime needs its compute shader; LoadZone only sets its radius @@ -155,8 +160,9 @@ public static class FloatingItemsDrawnSmoke ItemPickup[] Pickups() => rootObject.GetComponentsInChildren(true).Where(p => p != null).ToArray(); void At(ItemPickup pickup, FloatingItem floating, string what) { - var at = floating.Body.Position; - var want = new Vector3(at.x, zone.GetHeight(at), at.y); + var lead = ActionGameManager.Clock.Lead; + var at = DrawAhead.Position(floating.Body, lead); + var want = new Vector3(at.x, DrawAhead.FloaterHeight(zone, settings, at, lead), at.y); Require(Vector3.Distance(pickup.transform.position, want) < 1e-3f, $"{what}: the pickup is at {pickup.transform.position}, the body at {want}."); } @@ -185,10 +191,13 @@ public static class FloatingItemsDrawnSmoke var earlyPickup = Pickups().Single(p => p.Item == early.Item); earlyPickup.LabelPersistDuration = 1; earlyPickup.LabelFadeDuration = .2f; - for (var i = 0; i < 1200 && earlyPickup.ScanLabel.color.a > .05f; i++) yield return null; + // Game time, not frames: batch frames can be far shorter than a game second, so a frame count can run out before the fade. + var labelUntil = Time.time + earlyPickup.LabelPersistDuration + earlyPickup.LabelFadeDuration + 1f; + while (Time.time < labelUntil && earlyPickup.ScanLabel.color.a > .05f) yield return null; Require(earlyPickup.ScanLabel.color.a <= .05f, "label: an unviewed, unheld label did not go dark."); Require(zone.TryHold(taker, early.Id), "label: the zone refused to hold the item."); - for (var i = 0; i < 1200 && earlyPickup.ScanLabel.color.a < .95f; i++) yield return null; + labelUntil = Time.time + earlyPickup.LabelPersistDuration + earlyPickup.LabelFadeDuration + 1f; + while (Time.time < labelUntil && earlyPickup.ScanLabel.color.a < .95f) yield return null; Require(earlyPickup.ScanLabel.color.a >= .95f, "label: a held item's label is not lit."); // The test scene's stand-in sim and tendril ride the zone's own held-pose curve: its pose at each point of a grab diff --git a/Assets/Scripts/Editor/FlowFieldParityVerify.cs b/Assets/Scripts/Editor/FlowFieldParityVerify.cs index 90979f7e8..2fa3e5fda 100644 --- a/Assets/Scripts/Editor/FlowFieldParityVerify.cs +++ b/Assets/Scripts/Editor/FlowFieldParityVerify.cs @@ -106,7 +106,7 @@ namespace Aetheria.EditorTools var cache = AetheriaStores.Open(Path.Combine(work, Guid.NewGuid().ToString("N") + ".cc"), catalogWritable: true); cache.Upsert(new VerseGrammar { Revision = 1 }); cache.FlushAsync().Wait(); - var items = new ItemManager(cache, new ProvenanceLedger(), settings.GameplaySettings, _ => { }); + var items = new ItemManager(1u, cache, new ProvenanceLedger(), settings.GameplaySettings, _ => { }); return new Zone(items, new PlanetSettings { Flow = flow }, new ZonePack { Time = time }, new GalaxyZone { Name = "Parity", Owner = null }, null); } diff --git a/Assets/Scripts/Editor/FlowFieldSceneVerify.cs b/Assets/Scripts/Editor/FlowFieldSceneVerify.cs index 80d9a4e31..d39874f33 100644 --- a/Assets/Scripts/Editor/FlowFieldSceneVerify.cs +++ b/Assets/Scripts/Editor/FlowFieldSceneVerify.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Reflection; @@ -140,6 +140,9 @@ namespace Aetheria.EditorTools Call(volume, "Update"); if (Differing(flowing, Frame()) == 0) Fail("scene-stardust-run", "the particle buffer did not move while the zone ran"); if (Shader.GetGlobalFloat("_NebulaNoiseTime") == noiseTime) Fail("scene-noise-clock", "_NebulaNoiseTime did not move while the zone ran"); + + CheckStardustUncullable(dust, bufferField); + CheckStardustUnderFog(dust, bufferField); } finally { @@ -147,6 +150,214 @@ namespace Aetheria.EditorTools } } + // The event Stardust itself attaches its draw at, read from the class so the verifier cannot agree with a copy of it. + static CameraEvent DrawEvent() => + (CameraEvent) typeof(Stardust).GetField("DrawEvent", BindingFlags.NonPublic | BindingFlags.Static).GetRawConstantValue(); + + // The dust is a command buffer on the target camera, which no culling sees: with every particle 10000 below the + // camera (far past any box a hand could inflate round it) a render still shows dust, and shows none when the + // same particles have no size. + static void CheckStardustUncullable(Stardust dust, FieldInfo bufferField) + { + var camera = dust.TargetCamera; + if (camera == null) { Fail("scene-stardust-uncullable", "Stardust has no TargetCamera"); return; } + var drawEvent = DrawEvent(); + var attached = camera.GetCommandBuffers(drawEvent).Count(buffer => buffer.name == "Stardust"); + if (attached != 1) Fail("scene-stardust-uncullable", $"{attached} Stardust command buffers at {drawEvent} on the target camera, expected 1"); + foreach (var other in Enum.GetValues(typeof(CameraEvent)).Cast().Where(other => other != drawEvent)) + { + var stray = camera.GetCommandBuffers(other).Count(buffer => buffer.name == "Stardust"); + if (stray != 0) Fail("scene-stardust-uncullable", $"{stray} Stardust command buffers at {other}, expected 0"); + } + + var particles = (ComputeBuffer) bufferField.GetValue(dust); + float[] Layout(float size, Vector3 centre, float below) => DustLayout(particles.count, size, centre, below); + + var transform = camera.transform; + var (position, rotation) = (transform.position, transform.rotation); + var (far, flags, background, mask, target) = (camera.farClipPlane, camera.clearFlags, camera.backgroundColor, camera.cullingMask, camera.targetTexture); + var texture = RenderTexture.GetTemporary(64, 64, 24, RenderTextureFormat.ARGB32); + var readback = new Texture2D(64, 64, TextureFormat.RGBA32, false); + var previous = RenderTexture.active; + // Image effects on the camera would write their own frame over the dust; only the dust draw is under test. + var effects = camera.GetComponents().Where(behaviour => behaviour.enabled).ToArray(); + Color32[] Render(float size, Vector3 centre, float below) + { + Call(dust, "Update"); // on a bounded draw this queues the draw for the camera; here it dispatches the compute + particles.SetData(Layout(size, centre, below)); + camera.Render(); + RenderTexture.active = texture; + readback.ReadPixels(new Rect(0, 0, 64, 64), 0, 0); + readback.Apply(); + return readback.GetPixels32(); + } + try + { + foreach (var effect in effects) effect.enabled = false; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = Color.black; + camera.cullingMask = 1; + camera.targetTexture = texture; + // control: the camera where the scene put it, dust 100 below, inside any box a hand-set extent would give + transform.SetPositionAndRotation(position, Quaternion.Euler(90f, 0f, 0f)); + camera.farClipPlane = 1000f; + var nearWith = Render(5f, position, 100f); var nearWithout = Render(0f, position, 100f); + var nearDiff = 0; for (var i = 0; i < nearWith.Length; i++) if (!nearWith[i].Equals(nearWithout[i])) nearDiff++; + Debug.Log($"{Tag} stardust near-control differing pixels={nearDiff}"); + if (nearDiff == 0) Fail("scene-stardust-uncullable", "control: dust 100 below the camera drew no pixels"); + transform.SetPositionAndRotation(new Vector3(0f, -40000f, 0f), Quaternion.Euler(90f, 0f, 0f)); + camera.farClipPlane = 30000f; + var withDust = Render(400f, transform.position, 10000f); // the material scales a quad by distance^.33, so at this range it needs this size to cover pixels + var without = Render(0f, transform.position, 10000f); + var differing = 0; + for (var i = 0; i < withDust.Length; i++) + if (!withDust[i].Equals(without[i])) differing++; + if (differing == 0) Fail("scene-stardust-uncullable", "the dust 10000 below the camera drew no pixels"); + } + finally + { + RenderTexture.active = previous; + foreach (var effect in effects) effect.enabled = true; + camera.targetTexture = target; + camera.cullingMask = mask; + camera.backgroundColor = background; + camera.clearFlags = flags; + camera.farClipPlane = far; + transform.SetPositionAndRotation(position, rotation); + RenderTexture.ReleaseTemporary(texture); + UnityEngine.Object.DestroyImmediate(readback); + } + } + + // The nebula is an [ImageEffectOpaque] that fogs the whole ray to a depth the dust never writes (B49): dust drawn + // before it is attenuated by the fog, dust drawn after it sits on top at full strength. The dust's contribution to a + // pixel (render with dust minus render without) is read with the cloud renderer off and on, in an HDR target. + static void CheckStardustUnderFog(Stardust dust, FieldInfo bufferField) + { + const int Size = 128; + var camera = dust.TargetCamera; + var cloud = camera == null ? null : camera.GetComponent(); + if (cloud == null) { Fail("scene-stardust-under-fog", "the target camera has no VolumeCloudRenderer"); return; } + var particles = (ComputeBuffer) bufferField.GetValue(dust); + + var transform = camera.transform; + var (position, rotation) = (transform.position, transform.rotation); + var (far, flags, background, mask, target, hdr) = (camera.farClipPlane, camera.clearFlags, camera.backgroundColor, camera.cullingMask, camera.targetTexture, camera.allowHDR); + var texture = RenderTexture.GetTemporary(Size, Size, 24, RenderTextureFormat.ARGBHalf); + var readback = new Texture2D(Size, Size, TextureFormat.RGBAHalf, false); + var previous = RenderTexture.active; + var effects = camera.GetComponents().Where(behaviour => behaviour.enabled).ToArray(); + var fields = new System.Collections.Generic.List(); + GameObject floor = null; + float[] Render(bool fog, float size) + { + cloud.enabled = fog; + for (var frame = 0; frame < 2; frame++) // the cloud's first frame forces ULTRA and fills its history; the second is the settled one + { + Call(dust, "Update"); + particles.SetData(DustLayout(particles.count, size, position, 100f)); + camera.Render(); + } + RenderTexture.active = texture; + readback.ReadPixels(new Rect(0, 0, Size, Size), 0, 0); + readback.Apply(); + return readback.GetPixels().Select(pixel => pixel.r * .2126f + pixel.g * .7152f + pixel.b * .0722f).ToArray(); + } + try + { + foreach (var effect in effects) effect.enabled = false; + camera.allowHDR = true; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = Color.black; + camera.cullingMask = 1; + camera.targetTexture = texture; + camera.farClipPlane = 1000f; + transform.SetPositionAndRotation(position, Quaternion.Euler(90f, 0f, 0f)); + // The fog only measures a partial density where depth is written: a pixel with no depth is fogged whole (CloudShader sets + // density 1 past the far plane). A floor under the dust gives the march a finite ray. + floor = GameObject.CreatePrimitive(PrimitiveType.Plane); + floor.transform.position = position + Vector3.down * 200f; + floor.transform.localScale = Vector3.one * 100f; + UnityEngine.Object.DestroyImmediate(floor.GetComponent()); + + // The scene's own nebula fields are textures the game renders; here a uniform fill of white fog stands in for them. + foreach (var (name, format) in new[] { ("_NebulaSurfaceHeight", TextureFormat.RFloat), ("_NebulaPatchHeight", TextureFormat.RHalf), ("_NebulaPatch", TextureFormat.RHalf) }) + Shader.SetGlobalTexture(name, Uniform(format, Color.black, fields)); + Shader.SetGlobalTexture("_NebulaTint", Uniform(TextureFormat.RGBA32, Color.white, fields)); + Shader.DisableKeyword("NOISE_SLOPE"); + Shader.SetGlobalFloat("_NebulaFillDensity", .01f); + Shader.SetGlobalFloat("_NebulaFillDistance", 120f); + Shader.SetGlobalFloat("_NebulaFillExponent", 0f); + Shader.SetGlobalFloat("_NebulaFillOffset", 0f); + Shader.SetGlobalFloat("_NebulaPatchDensity", 0f); + Shader.SetGlobalFloat("_NebulaLuminance", .01f); // a fog dimmer than the dust: dust drawn over it would otherwise read as a negative contribution + Shader.SetGlobalFloat("_ExtinctionCoefficient", .25f); // with the fill density, about 40% of the light to the floor is fogged away + Shader.SetGlobalFloat("_SafetyDistance", -1e6f); // below every sample: only the uniform fill, none of the zone's noise + Shader.SetGlobalFloat("_TintLodExponent", 0f); + + var clearBare = Render(false, 0f); + var clearDust = Render(false, 5f); + var fogBare = Render(true, 0f); + var fogDust = Render(true, 5f); + + var fogShown = 0f; + var (clearSum, fogSum, drawn) = (0f, 0f, 0); + for (var i = 0; i < clearBare.Length; i++) + { + fogShown += Mathf.Abs(fogBare[i] - clearBare[i]) / clearBare.Length; + if (clearDust[i] == clearBare[i]) continue; + drawn++; clearSum += clearDust[i] - clearBare[i]; fogSum += fogDust[i] - fogBare[i]; + } + var (clearMean, fogMean) = (drawn == 0 ? 0f : clearSum / drawn, drawn == 0 ? 0f : fogSum / drawn); + Debug.Log($"{Tag} stardust under fog: fog control mean |difference|={fogShown}, dust pixels={drawn}, dust contribution cloud off={clearMean}, cloud on={fogMean}, ratio={(clearMean == 0f ? 0f : fogMean / clearMean)}"); + if (drawn == 0 || clearMean <= 0f) Fail("scene-stardust-under-fog", "control: the dust drew no pixels with the cloud renderer off"); + else if (fogShown < 1e-3f) Fail("scene-stardust-under-fog", "control: the cloud renderer changed no pixels, so no fog is in view"); + else if (fogMean > .9f * clearMean) Fail("scene-stardust-under-fog", "the dust is not attenuated by the fog in front of it: it was drawn after the nebula composites"); + else if (fogMean < .1f * clearMean) Fail("scene-stardust-under-fog", "the dust is erased, not dimmed, by a partial fog"); + } + finally + { + RenderTexture.active = previous; + foreach (var effect in effects) effect.enabled = true; + camera.allowHDR = hdr; + camera.targetTexture = target; + camera.cullingMask = mask; + camera.backgroundColor = background; + camera.clearFlags = flags; + camera.farClipPlane = far; + transform.SetPositionAndRotation(position, rotation); + RenderTexture.ReleaseTemporary(texture); + UnityEngine.Object.DestroyImmediate(readback); + foreach (var field in fields) UnityEngine.Object.DestroyImmediate(field); + if (floor != null) UnityEngine.Object.DestroyImmediate(floor); + } + } + + static Texture2D Uniform(TextureFormat format, Color colour, System.Collections.Generic.List owner) + { + var texture = new Texture2D(1, 1, format, false) { wrapMode = TextureWrapMode.Clamp }; + texture.SetPixel(0, 0, colour); + texture.Apply(); + owner.Add(texture); + return texture; + } + + // A square patch of particles 4 apart, `below` under `centre`. + static float[] DustLayout(int count, float size, Vector3 centre, float below) + { + var side = Mathf.RoundToInt(Mathf.Sqrt(count)); + var data = new float[count * 7]; + for (var i = 0; i < count; i++) + { + data[i * 7 + 0] = centre.x + ((i % side) - side / 2f) * 4f; + data[i * 7 + 1] = centre.y - below; + data[i * 7 + 2] = centre.z + ((i / side) - side / 2f) * 4f; + data[i * 7 + 3] = data[i * 7 + 4] = data[i * 7 + 5] = 1f; + data[i * 7 + 6] = size; + } + return data; + } + // CloudShader's pass 0 compiles with the flow keywords (it must #include_with_pragmas the shared include). static void CheckCloudKeywords() { diff --git a/Assets/Scripts/Editor/GravityFieldParityVerify.cs b/Assets/Scripts/Editor/GravityFieldParityVerify.cs new file mode 100644 index 000000000..d996d3227 --- /dev/null +++ b/Assets/Scripts/Editor/GravityFieldParityVerify.cs @@ -0,0 +1,428 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using GameCult.Caching; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Rendering; +using CultMath; + +namespace Aetheria.EditorTools +{ + /// + /// Batchmode check for campaign aetheria-gravity, cut grid-lowering: what the Gravity Field pass draws is the sim's + /// height. It builds a zone from Settings.asset's PlanetSettings with three bodies, runs the real lowering + /// (GravityLowering.Lower and CreateField), renders the field through an orthographic camera that culls layer 9 into + /// a float target, and compares every texel with -Zone.GetHeight at the texel centre's world position. It then checks + /// that the texture's gradient points where Zone.GetForce does, and that ARPG.unity and the body prefabs are wired + /// the way the cut leaves them (the gravity cameras cull layer 9 alone, nothing but the run-time field sits on it). + /// + /// Renders, so it needs a graphics device: run WITHOUT -nographics (as FlowFieldParityVerify). + /// Unity -batchmode -projectPath <repo> -executeMethod Aetheria.EditorTools.GravityFieldParityVerify.Run -logFile <path> + /// Do not pass -quit: Run exits the Editor itself, 0 on success and 1 on any failure. + /// + public static class GravityFieldParityVerify + { + const string Tag = "[GravityFieldParityVerify]"; + const int Size = 256; + // Measured on this tree (the "max error" line this run prints): the largest |texel + GetHeight| over the windows of the three + // frames is 0.0103 u, in the moved-again frame's sun window (float32 on both sides, the GPU's log and the closed form's + // single-precision floor of 1.5e-5 per unit strength against the CPU's, the quad's interpolated world position). The + // tolerance is 2.9x that. A band whose terms were drawn from the wrong edge (reference, inner or outer) moves the + // drawn height by whole units. + const float Tolerance = 0.03f; + // The worst angle between the texture's central difference and GetForce (B28: 1.0 degrees median zone, 2.9 worst). + const float GradientDegrees = 3f; + // Below this force (u/s^2) the drawn height differs between neighbouring texels by less than float32 resolves. + const float FlatForce = 1e-3f; + static float _maxError, _maxAngle, _maxTau; + static bool _ok = true; + + static void OnLog(string message, string stack, LogType type) + { + if ((type == LogType.Error || type == LogType.Exception || type == LogType.Assert) && !message.StartsWith(Tag)) + Fail("logged-error", message.Split('\n')[0]); + } + + static void Fail(string check, string detail) + { + Debug.LogError($"{Tag} FAIL {check}: {detail}"); + _ok = false; + } + + public static void Run() + { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null) + { + Debug.LogError($"{Tag} FAIL: no graphics device in this session; rerun batchmode without -nographics."); + EditorApplication.Exit(1); + return; + } + + Application.logMessageReceived += OnLog; + var work = Path.Combine(Path.GetTempPath(), "gravity-parity-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(work); + ComputeBuffer wells = null; + GameObject field = null; + try + { + var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset") + ?? throw new InvalidOperationException("No Settings.asset."); + var material = AssetDatabase.LoadAssetAtPath("Assets/Materials/Brushes/Gravity Field.mat") + ?? throw new InvalidOperationException("No Gravity Field.mat."); + + var zone = BuildZone(settings, work, out var orbits, multiRoot: true); + GravityLowering.Lower(zone, 0f, ref wells); + field = GravityLowering.CreateField(material); + + // Three frames through the one buffer, as ZoneRenderer.Update runs it: the bodies as built, then two moves (an + // orbit's Position is what Zone.GetHeight reads). A move keeps the tree's geometry, since a rail never leaves + // its parent's distance and the walk skips a subtree outside its reach: the sun is the root, the planet is + // its Distance from the sun, the planetoid its Distance from the planet. A lowering that mirrors an axis or + // stops refreshing the upload agrees with the sim in the first frame only where it is symmetric. + CheckFrame("built", zone, orbits); + orbits[1].Position = Around(orbits[0], orbits[1], .31f); + orbits[2].Position = Around(orbits[1], orbits[2], .77f); + orbits[5].Position = Around(orbits[1], orbits[5], .41f); + GravityLowering.Lower(zone, 0f, ref wells); + CheckFrame("moved", zone, orbits); + orbits[0].Position = new float2(210f, -95f); + orbits[1].Position = Around(orbits[0], orbits[1], .52f); + orbits[2].Position = Around(orbits[1], orbits[2], .05f); + orbits[5].Position = Around(orbits[1], orbits[5], .93f); + GravityLowering.Lower(zone, 0f, ref wells); + CheckFrame("moved-again", zone, orbits); + if (_maxTau <= 1f) Fail("series-branch", $"the largest tau over the drawn bounded terms is {_maxTau:G4}; no window draws the series branch"); + Debug.Log($"{Tag} largest drawn tau {_maxTau:G4}"); + Debug.Log($"{Tag} max error {_maxError:G4} u (tolerance {Tolerance:G4}); max gradient angle {_maxAngle:F2} degrees (limit {GradientDegrees:F0})"); + + CheckWiring(settings); + Debug.Log(_ok ? $"{Tag} PASS" : $"{Tag} FAIL: see errors above"); + } + catch (Exception e) + { + Debug.LogError($"{Tag} FAIL exception: {e}"); + _ok = false; + } + finally + { + Application.logMessageReceived -= OnLog; + if (field != null) UnityEngine.Object.DestroyImmediate(field); + wells?.Release(); + try { Directory.Delete(work, true); } catch { } + } + EditorApplication.Exit(_ok ? 0 : 1); + } + + static float Core(Zone zone, float mass) => zone.Settings.RocheLimit.Evaluate(mass); + + // Where an orbit lies when it stands `turns` round its parent at its Distance. + static float2 Around(Orbit parent, Orbit child, float turns) => parent.Position + child.Data.Distance * OrbitData.Evaluate(turns); + + static Vector2 At(Orbit orbit) => new Vector2(orbit.Position.x, orbit.Position.y); + + // Orbits 0-2 are the sun, the planet and the planetoid, each window centred where the body is now; orbit 3 is the + // body of no strength, orbit 4 the second sun and orbit 5 the planet's moon inside its Roche limit (whose band has + // tau above 1: the series branch of the shader). The zone window holds all of them; the far window is centred on the + // zone node, so its band (nothing inside A, the zone's mass beyond Beta A) is drawn. A window draws a term when + // the term's disc reaches it; the largest tau over the drawn terms must exceed 1 or the series branch went unchecked. + static void CheckFrame(string frame, Zone zone, Orbit[] orbits) + { + CheckTexels(frame + "-planetoid", zone, At(orbits[2]), 4f * Core(zone, 29.2f)); + CheckTexels(frame + "-planet", zone, At(orbits[1]), 4f * Core(zone, 273.9f)); + CheckTexels(frame + "-sun", zone, At(orbits[0]), 4f * Core(zone, 14152f)); + CheckTexels(frame + "-zone", zone, Vector2.zero, 1.05f * zone.Pack.Radius); + var centre = new Vector2(zone.Wells.Centre.x, zone.Wells.Centre.y); + var span = 0f; + foreach (var root in new[] { orbits[0], orbits[4] }) + span = Math.Max(span, (At(root) - centre).magnitude + zone.Wells.Reach(zone.Orbits.First(o => o.Value == root).Key)); + // The wells have their own windows: out here a texel is 40 u wide, so only the ring from 0.9 A outward, where the + // zone node's band rises and the zone becomes one body, is measured. + CheckTexels(frame + "-far", zone, centre, 1.6f * span, .9f * span); + CheckGradient(zone, orbits.Select(At).ToArray()); + } + + // A sun (14152 at (-130, 85)), its planet (273.9, placed at (610, -240) at the pack's time), the planet's planetoid + // (29.2, placed at (670, -270)) and a body of mass 0, whose well has no strength (a legal body; its curves give + // c = 0 and R = 0), at (1700, 300): a family, so the band terms are drawn (the planet's and the sun's cusps and + // fades), off both axes and asymmetric, so the zone's wells sit where the verify says before any Update and a + // mirrored or swapped axis cannot agree with the sim. The sun and the zero-mass body are fixed roots. + internal static Zone BuildZone(GameSettings settings, string work, out Orbit[] orbits, float radius = 4182f, bool multiRoot = false) + { + var cache = AetheriaStores.Open(Path.Combine(work, "catalog.cc"), Path.Combine(work, "run.cc"), catalogWritable: true); + cache.Upsert(new VerseGrammar { Revision = 1 }); + cache.FlushAsync().Wait(); + var items = new ItemManager(1u, cache, new ProvenanceLedger(), settings.GameplaySettings, _ => { }); + var positions = new List { new Vector2(-130f, 85f), new Vector2(610f, -240f), new Vector2(670f, -270f), new Vector2(1700f, 300f) }; + var masses = new List { 14152f, 273.9f, 29.2f, 0f }; + var parents = new List { -1, 0, 1, -1 }; + if (multiRoot) + { + // A second sun (a second subzone, so the zone node exists) and a moon of the planet 0.6 of its Roche limit away. + positions.Add(new Vector2(-2400f, -1900f)); + masses.Add(3000f); + parents.Add(-1); + positions.Add(positions[1] + new Vector2(-.6f * settings.PlanetSettings.RocheLimit.Evaluate(273.9f), 0f)); + masses.Add(1f); + parents.Add(1); + } + var keys = new CultRecordKey[positions.Count]; + var pack = new ZonePack { Radius = radius }; + for (var i = 0; i < positions.Count; i++) + { + var data = new OrbitData { FixedPosition = new float2(positions[i].x, positions[i].y) }; + if (parents[i] >= 0) + { + var offset = positions[i] - positions[parents[i]]; + data.Parent = new CultRecordRef(keys[parents[i]]); + data.Distance = offset.magnitude; + data.Phase = Mathf.Atan2(offset.y, offset.x) / (2f * Mathf.PI); + } + var orbit = keys[i] = cache.Upsert(data).Key; + var body = cache.Upsert(new PlanetData + { Name = "Body " + i, Orbit = new CultRecordRef(orbit), Mass = masses[i] }).Key; + pack.Orbits.Add(new CultRecordRef(orbit)); + pack.Planets.Add(new CultRecordRef(body)); + } + var zone = new Zone(items, settings.PlanetSettings, pack, new GalaxyZone { Name = "Gravity Parity", Owner = null }, null); + orbits = keys.Select(k => zone.Orbits[k]).ToArray(); + return zone; + } + + // Renders the field through a downward orthographic camera that culls layer 9 only, centred on `centre` with + // half-size `half`, and returns the depth of every texel with the world xz of the texel's centre, which is asked + // of the camera rather than assumed. + static void Render(Vector2 centre, float half, out float[] depth, out Vector2[] world) + { + var rt = new RenderTexture(Size, Size, 0, RenderTextureFormat.RFloat) { filterMode = FilterMode.Point }; + var cameraObject = new GameObject("Gravity Parity Camera"); + var read = new Texture2D(Size, Size, TextureFormat.RFloat, false); + var previous = RenderTexture.active; + try + { + var camera = cameraObject.AddComponent(); + camera.orthographic = true; + camera.orthographicSize = half; + camera.aspect = 1f; + camera.nearClipPlane = .3f; + camera.farClipPlane = 1000f; + camera.cullingMask = 1 << GravityLowering.FieldLayer; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = Color.black; + camera.allowHDR = false; + camera.allowMSAA = false; + camera.targetTexture = rt; + cameraObject.transform.SetPositionAndRotation(new Vector3(centre.x, 100f, centre.y), Quaternion.Euler(90f, 0f, 0f)); + camera.Render(); + + RenderTexture.active = rt; + read.ReadPixels(new Rect(0, 0, Size, Size), 0, 0); + read.Apply(); + depth = read.GetPixelData(0).ToArray(); + world = new Vector2[Size * Size]; + for (var y = 0; y < Size; y++) + for (var x = 0; x < Size; x++) + { + var p = camera.ViewportToWorldPoint(new Vector3((x + .5f) / Size, (y + .5f) / Size, 100f)); + world[y * Size + x] = new Vector2(p.x, p.z); + } + } + finally + { + RenderTexture.active = previous; + UnityEngine.Object.DestroyImmediate(cameraObject); + UnityEngine.Object.DestroyImmediate(read); + rt.Release(); + UnityEngine.Object.DestroyImmediate(rt); + } + } + + // The largest tau = (a^2 + eps^2) / (b^2 - a^2) over the bounded terms whose disc reaches the window's square. + static void NoteTau(Zone zone, Vector2 centre, float half) + { + foreach (var term in zone.Wells.Terms) + { + if (float.IsPositiveInfinity(term.Outer)) continue; + var at = term.Node.IsSet() ? zone.Orbits[term.Node].Position : zone.Wells.Centre; + if (Math.Abs(at.x - centre.x) > half + term.Outer || Math.Abs(at.y - centre.y) > half + term.Outer) continue; + _maxTau = Math.Max(_maxTau, (term.Inner * term.Inner + term.Softening * term.Softening) / (term.Outer * term.Outer - term.Inner * term.Inner)); + } + } + + static void CheckTexels(string window, Zone zone, Vector2 centre, float half, float ringFrom = 0f) + { + Render(centre, half, out var depth, out var world); + NoteTau(zone, centre, half); + var worst = 0f; + var live = 0; + var measured = 0; + for (var i = 0; i < depth.Length; i++) + { + if ((world[i] - centre).magnitude < ringFrom) continue; + measured++; + var expected = -zone.GetHeight(new float2(world[i].x, world[i].y)); + if (float.IsNaN(depth[i])) { Fail($"{window}-texels", $"texel {i}: NaN"); return; } + var error = Math.Abs(depth[i] - expected); + worst = Math.Max(worst, error); + if (Math.Abs(expected) > 1f) live++; + if (error > Tolerance) + { + Fail($"{window}-texels", $"texel {i} at ({world[i].x:F2}, {world[i].y:F2}): expected {expected:G6}, drawn {depth[i]:G6}, error {error:G4} > {Tolerance:G4}"); + return; + } + } + _maxError = Math.Max(_maxError, worst); + // A window that draws nothing agrees with a zone that is flat: the check must be live. + if (live < measured / 2) Fail($"{window}-texels", $"only {live} of {measured} texels are deeper than 1 u; the check is vacuous"); + Debug.Log($"{Tag} {window}: {depth.Length} texels, max error {worst:G4} u"); + } + + // The texture's central difference (over two 4 u texels, mid-zone) points where GetForce does: depth = -h, so its + // gradient is the force over the exaggeration. This is what the slope readers (the shader's slopeFlow, the nebula + // surface normal, the drawn grid) take from the texture. + static void CheckGradient(Zone zone, Vector2[] bodies) + { + Render(new Vector2(1500f, 0f), 512f, out var depth, out var world); + var compared = 0; + for (var y = 1; y < Size - 1; y++) + for (var x = 1; x < Size - 1; x++) + { + var here = world[y * Size + x]; + if (bodies.Any(b => (b - here).magnitude < 16f)) continue; + // The world step of each texel axis is read from the camera, so a flipped or rotated target is still measured. + var au = world[y * Size + x + 1] - world[y * Size + x - 1]; + var av = world[(y + 1) * Size + x] - world[(y - 1) * Size + x]; + var du = depth[y * Size + x + 1] - depth[y * Size + x - 1]; + var dv = depth[(y + 1) * Size + x] - depth[(y - 1) * Size + x]; + var det = au.x * av.y - au.y * av.x; + var g = new Vector2((du * av.y - dv * au.y) / det, (dv * au.x - du * av.x) / det); + var f = zone.GetForce(new float2(here.x, here.y)); + if (CultMath.math.length(f) < FlatForce) continue; // between the subzones the ground is flat: no direction to compare + var angle = Vector2.Angle(g, new Vector2(f.x, f.y)); + if (float.IsNaN(angle) || angle > GradientDegrees) + { + Fail("gradient", $"at ({here.x:F1}, {here.y:F1}) the drawn slope points {angle:F2} degrees from the force (limit {GradientDegrees:F0})"); + return; + } + _maxAngle = Math.Max(_maxAngle, angle); + compared++; + } + if (compared < Size * Size / 2) Fail("gradient", $"only {compared} texels were compared"); + } + + // Main Menu.unity has no ZoneRenderer, so no Gravity Field and no lowered potential: its gravity cameras draw the + // menu's own brush objects (Boundary on layer 9, the Waves on layer 10) and are presentation by spec (follow-up + // menu-grid-brushes), not a reader of the potential. This pins that: a ZoneRenderer in the menu would make its + // cameras readers of the field, and they would have to cull layer 9 alone like ARPG's. + static void CheckTestSceneWiring() + { + EditorSceneManager.OpenScene("Assets/Scenes/TestScene.unity", OpenSceneMode.Single); + try + { + if (CheckPatchCameras("TestScene.unity") < 1) Fail("wiring", "no Fog Patch Displacement camera in TestScene.unity"); + } + finally + { + EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + } + } + + static void CheckMenuWiring() + { + var scene = EditorSceneManager.OpenScene("Assets/Scenes/Main Menu.unity", OpenSceneMode.Single); + try + { + if (UnityEngine.Object.FindFirstObjectByType(FindObjectsInactive.Include) != null) + Fail("wiring", "Main Menu.unity has a ZoneRenderer: its gravity cameras now read the Gravity Field and must cull layer 9 alone"); + var seen = 0; + foreach (var camera in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) + { + if (camera.targetTexture == null || camera.targetTexture.name != "Fog Surface Displacement") continue; + seen++; + const int menuBrushLayers = (1 << 9) | (1 << 10); + if (camera.cullingMask != menuBrushLayers) + Fail("wiring", $"Main Menu camera {camera.name} culls mask {camera.cullingMask}, not the menu brush layers 9 and 10"); + } + if (seen != 1) Fail("wiring", $"{seen} Fog Surface Displacement cameras in Main Menu.unity, expected 1"); + if (CheckPatchCameras("Main Menu.unity") < 1) Fail("wiring", "no Fog Patch Displacement camera in Main Menu.unity"); + } + finally + { + EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + } + } + + // Every scene's Fog Patch Displacement camera: cloudDensity adds the R32 gravity height to the patch texture, so + // a patch camera that culls the gravity layer counts the field twice. Returns how many it found in the open scene. + static int CheckPatchCameras(string sceneName) + { + var found = 0; + foreach (var camera in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) + { + if (camera.targetTexture == null || camera.targetTexture.name != "Fog Patch Displacement") continue; + found++; + if ((camera.cullingMask & 1 << GravityLowering.FieldLayer) != 0) + Fail("wiring", $"camera {camera.name} (target Fog Patch Displacement) in {sceneName} culls layer {GravityLowering.FieldLayer}, the gravity layer"); + } + return found; + } + + static void CheckWiring(GameSettings settings) + { + var scene = EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Single); + try + { + var gravityTargets = new[] { "Fog Surface Displacement", "Slime Gravity", "MinimapGravity" }; + var seen = 0; + foreach (var camera in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) + { + if (camera.targetTexture == null || !gravityTargets.Contains(camera.targetTexture.name)) continue; + seen++; + if (camera.cullingMask != 1 << GravityLowering.FieldLayer) + Fail("wiring", $"camera {camera.name} (target {camera.targetTexture.name}) culls mask {camera.cullingMask}, not layer {GravityLowering.FieldLayer} alone"); + } + if (seen < gravityTargets.Length) Fail("wiring", $"only {seen} gravity cameras found in ARPG.unity"); + + // The patch texture is half-float: it holds the patch layers only, the nebula reads gravity from the R32 surface. + if (CheckPatchCameras("ARPG.unity") != 1) Fail("wiring", "ARPG.unity does not have exactly 1 Fog Patch Displacement camera"); + + // The map screen's gravity camera has no target until MapRenderer gives it one at run time. + var map = UnityEngine.Object.FindFirstObjectByType(FindObjectsInactive.Include); + if (map == null || map.GravityCamera == null) Fail("wiring", "no MapRenderer.GravityCamera in ARPG.unity"); + else if (map.GravityCamera.cullingMask != 1 << GravityLowering.FieldLayer) + Fail("wiring", $"MapRenderer.GravityCamera culls mask {map.GravityCamera.cullingMask}, not layer {GravityLowering.FieldLayer} alone"); + + var renderer = UnityEngine.Object.FindFirstObjectByType(FindObjectsInactive.Include); + if (renderer == null) Fail("wiring", "no ZoneRenderer in ARPG.unity"); + else if (renderer.GravityField == null || renderer.GravityField.shader.name != "Brushes/Gravity Field") + Fail("wiring", "ZoneRenderer.GravityField is not a Brushes/Gravity Field material"); + + foreach (var r in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) + if (r.gameObject.layer == GravityLowering.FieldLayer) + Fail("wiring", $"renderer {r.name} in ARPG.unity sits on layer {GravityLowering.FieldLayer}"); + } + finally + { + EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + } + + CheckMenuWiring(); + CheckTestSceneWiring(); + + foreach (var path in new[] + { + "Assets/Prefabs/RPG/Planets/Planet.prefab", "Assets/Prefabs/RPG/Planets/Gas Giant.prefab", + "Assets/Prefabs/RPG/Planets/Sun.prefab", "Assets/Prefabs/RPG/Wormhole 1.prefab" + }) + { + var prefab = AssetDatabase.LoadAssetAtPath(path); + if (prefab == null) { Fail("wiring", $"no prefab at {path}"); continue; } + foreach (var r in prefab.GetComponentsInChildren(true)) + if (r.gameObject.layer == GravityLowering.FieldLayer) + Fail("wiring", $"renderer {r.name} in {path} sits on layer {GravityLowering.FieldLayer}"); + } + } + } +} diff --git a/Assets/Scripts/Editor/NameTools.cs.meta b/Assets/Scripts/Editor/GravityFieldParityVerify.cs.meta similarity index 83% rename from Assets/Scripts/Editor/NameTools.cs.meta rename to Assets/Scripts/Editor/GravityFieldParityVerify.cs.meta index e0022b308..50646bcbb 100644 --- a/Assets/Scripts/Editor/NameTools.cs.meta +++ b/Assets/Scripts/Editor/GravityFieldParityVerify.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 88b5f578fe63a904b997331630e61416 +guid: 1ceb2bdab0804463b1998471d47e82fa MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Scripts/Editor/MapBeltPerf.cs b/Assets/Scripts/Editor/MapBeltPerf.cs new file mode 100644 index 000000000..ea3eac5c0 --- /dev/null +++ b/Assets/Scripts/Editor/MapBeltPerf.cs @@ -0,0 +1,116 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using GameCult.Caching; +using UnityEditor; +using CultMath; +using UnityEngine; +using float4 = CultMath.float4; +using Debug = UnityEngine.Debug; + +// The map's belt cost per frame, CPU milliseconds and managed allocation, for belts of 1000, 2250 (the generated +// maximum, Settings.asset) and 4000 asteroids: the base revision's AsteroidBeltUI.Update (MapBeltPerfBase, a verbatim copy) +// and MapPresenter's belt upload and draw, each as the median and interquartile range of 21 runs of 300 frames after +// a warm-up, in this one Unity job. CPU time only: GPU cost is not covered. Needs a GPU device. +// +// Unity -batchmode -projectPath -executeMethod MapBeltPerf.Run -logFile +public static class MapBeltPerf +{ + private const int Warmup = 60, Frames = 300, RunCount = 21; + + public static void Run() + { + var work = Path.Combine(Path.GetTempPath(), $"map-belt-perf-{Guid.NewGuid():N}"); + try + { + Directory.CreateDirectory(work); + foreach (var count in new[] { 1000, 2250, 4000 }) Measure(work, count); + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + private static void Measure(string work, int count) + { + var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset"); + var cache = AetheriaStores.Open(Path.Combine(work, $"c{count}.cc"), Path.Combine(work, $"r{count}.cc"), catalogWritable: true); + cache.Upsert(new VerseGrammar { Revision = 1 }); + var sunOrbit = cache.Upsert(new OrbitData()).Key; + var sunKey = cache.Upsert(new PlanetData { Name = "Sun", Orbit = new CultRecordRef(sunOrbit), Mass = 400f }).Key; + var beltOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef(sunOrbit), Distance = 900f, Phase = .42f }).Key; + var asteroids = Enumerable.Range(0, count).Select(i => new Asteroid + { Distance = 120f + i * .01f, Phase = CultMath.math.frac(i * .618f), Size = CultMath.math.frac(i * .37f + .1f), RotationSpeed = .15f + i * 1e-4f }).ToArray(); + var beltKey = cache.Upsert(new AsteroidBeltData { Orbit = new CultRecordRef(beltOrbit), Asteroids = asteroids }).Key; + var pack = new ZonePack + { + Orbits = { new CultRecordRef(sunOrbit), new CultRecordRef(beltOrbit) }, + Planets = { new CultRecordRef(sunKey), new CultRecordRef(beltKey) }, + Radius = 5000f, Mass = 10000f, Time = 0 + }; + var items = new ItemManager(1u, cache, new ProvenanceLedger(), settings.GameplaySettings, _ => { }); + var zone = new Zone(items, new PlanetSettings + { + BodyRadius = new ExponentialCurve(), WellStrength = new ExponentialCurve { Multiplier = 1f, Exponent = 1f }, + RocheLimit = new ExponentialCurve { Constant = 2f }, + AsteroidSize = new ExponentialLerp { Minimum = 2f, Maximum = 9f, Exponent = 1.7f }, + AsteroidHitpoints = new ExponentialLerp { Minimum = 10f, Maximum = 300f, Exponent = 2f }, + AsteroidRespawnTime = new ExponentialLerp { Minimum = 5f, Maximum = 60f, Exponent = 1f } + }, pack, new GalaxyZone { Name = "Perf", Owner = null }, null); + + var transforms = new float4[count]; + var presenter = new MapPresenter(new Material[0], AssetDatabase.LoadAssetAtPath("Assets/Materials/UI/MapAsteroid.mat"), 4, 4, 3f); + presenter.RegisterBelt(beltKey, transforms); + var baseBelt = new MapBeltPerfBase(zone, zone.AsteroidBelts[beltKey], 4, 4, 3f); + + Report("base", count, Runs(zone, beltKey, transforms, () => baseBelt.Update(transforms, 0f))); + Report("head", count, Runs(zone, beltKey, transforms, () => presenter.Draw(zone, 0))); + presenter.Clear(); + } + + // Each run is Frames frames after Warmup frames; the figure is one run's CPU ms per frame and managed bytes per frame. + private static (double ms, long bytes)[] Runs(Zone zone, CultRecordKey beltKey, float4[] transforms, Action frame) + { + var results = new (double, long)[RunCount]; + var stopwatch = new Stopwatch(); + var time = 0.0; + for (var run = -1; run < RunCount; run++) + { + stopwatch.Reset(); + long allocated = 0; + for (var i = 0; i < Warmup + Frames; i++) + { + zone.EvaluateBelt(beltKey, transforms, time += 0.016); + var bytes = GC.GetAllocatedBytesForCurrentThread(); + if (i >= Warmup) stopwatch.Start(); + frame(); + if (i >= Warmup) stopwatch.Stop(); + if (i >= Warmup) allocated += GC.GetAllocatedBytesForCurrentThread() - bytes; + } + if (run >= 0) results[run] = (stopwatch.Elapsed.TotalMilliseconds / Frames, allocated / Frames); + } + return results; + } + + private static double Quantile(double[] sorted, double q) + { + var at = q * (sorted.Length - 1); + var low = (int)Math.Floor(at); + var high = Math.Min(low + 1, sorted.Length - 1); + return sorted[low] + (sorted[high] - sorted[low]) * (at - low); + } + + private static void Report(string revision, int count, (double ms, long bytes)[] runs) + { + var ms = runs.Select(r => r.ms).OrderBy(v => v).ToArray(); + var bytes = runs.Select(r => (double)r.bytes).OrderBy(v => v).ToArray(); + var units = Process.GetProcessesByName("Unity").Length; + Debug.Log($"[MapBeltPerf] revision={revision} asteroids={count} runs={runs.Length} frames_per_run={Frames} " + + $"cpu_ms_median={Quantile(ms, .5):F4} iqr={Quantile(ms, .25):F4}-{Quantile(ms, .75):F4} " + + $"gc_bytes_median={Quantile(bytes, .5):F0} unity_processes={units} cpus={Environment.ProcessorCount}"); + } +} diff --git a/Assets/Scripts/Editor/MapBeltPerf.cs.meta b/Assets/Scripts/Editor/MapBeltPerf.cs.meta new file mode 100644 index 000000000..5a06251b4 --- /dev/null +++ b/Assets/Scripts/Editor/MapBeltPerf.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2bf3d3dcd7e3dd940add2640db93e3fe \ No newline at end of file diff --git a/Assets/Scripts/Editor/MapBeltPerfBase.cs b/Assets/Scripts/Editor/MapBeltPerfBase.cs new file mode 100644 index 000000000..6470b9c9a --- /dev/null +++ b/Assets/Scripts/Editor/MapBeltPerfBase.cs @@ -0,0 +1,88 @@ +using GameCult.Caching; +using CultMath; +using UnityEngine; +using static CultMath.math; +using float2 = CultMath.float2; +using float4 = CultMath.float4; +using Random = UnityEngine.Random; + +// The base revision's per-frame belt map path, copied from ZoneRenderer.cs at da6fe033 (class AsteroidBeltUI) so that +// MapBeltPerf can measure it beside MapPresenter in one job. Only the lead it read from ActionGameManager.Clock is +// fixed at zero. Not used by the game. +public class MapBeltPerfBase +{ + private Vector3[] _vertices; + private Vector3[] _normals; + private Vector2[] _uvs; + private int[] _indices; + private CultRecordKey _orbitParent; + private Mesh _mesh; + private float _size; + private Zone _zone; + private AsteroidBelt _belt; + private float _scale; + + public MapBeltPerfBase(Zone zone, AsteroidBelt belt, int spritesheetWidth, int spritesheetHeight, float scale) + { + _belt = belt; + _zone = zone; + var orbit = zone.Orbits[belt.Data.Orbit.Key]; + _orbitParent = orbit.Data.Parent.Key; + _vertices = new Vector3[_belt.Data.Asteroids.Length * 4]; + _normals = new Vector3[_belt.Data.Asteroids.Length * 4]; + _uvs = new Vector2[_belt.Data.Asteroids.Length * 4]; + _indices = new int[_belt.Data.Asteroids.Length * 6]; + _scale = scale; + + var maxDist = 0f; + var spriteSize = float2(1f / spritesheetWidth, 1f / spritesheetHeight); + // vertex order: bottom left, top left, top right, bottom right + for (var i = 0; i < belt.Data.Asteroids.Length; i++) + { + if (belt.Data.Asteroids[i].Distance > maxDist) + maxDist = belt.Data.Asteroids[i].Distance; + var spriteX = Random.Range(0, spritesheetWidth); + var spriteY = Random.Range(0, spritesheetHeight); + + _uvs[i * 4] = new Vector2(spriteX * spriteSize.x, spriteY * spriteSize.y); + _uvs[i * 4 + 1] = new Vector2(spriteX * spriteSize.x, spriteY * spriteSize.y + spriteSize.y); + _uvs[i * 4 + 2] = new Vector2(spriteX * spriteSize.x + spriteSize.x, spriteY * spriteSize.y + spriteSize.y); + _uvs[i * 4 + 3] = new Vector2(spriteX * spriteSize.x + spriteSize.x, spriteY * spriteSize.y); + + _indices[i * 6] = i * 4; + _indices[i * 6 + 1] = i * 4 + 1; + _indices[i * 6 + 2] = i * 4 + 3; + _indices[i * 6 + 3] = i * 4 + 3; + _indices[i * 6 + 4] = i * 4 + 1; + _indices[i * 6 + 5] = i * 4 + 2; + } + + for (var i = 0; i < _normals.Length; i++) _normals[i] = -Vector3.forward; + + _mesh = new Mesh(); + _mesh.vertices = _vertices; + _mesh.uv = _uvs; + _mesh.triangles = _indices; + _mesh.normals = _normals; + _mesh.bounds = new Bounds(Vector3.zero, Vector3.one * maxDist); + _size = maxDist; + } + + public void Update(float4[] transforms, float height) + { + var parentPosition = DrawAhead.Orbit(_zone, _orbitParent, 0f); + for (var i = 0; i < transforms.Length; i++) + { + var transform = transforms[i]; + var rotation = Quaternion.Euler(90, transform.z, 0); + var position = new Vector3(transform.x, height, transform.y); + _vertices[i * 4] = rotation * new Vector3(-transform.w * _scale, -transform.w * _scale, 0) + position; + _vertices[i * 4 + 1] = rotation * new Vector3(-transform.w * _scale, transform.w * _scale, 0) + position; + _vertices[i * 4 + 2] = rotation * new Vector3(transform.w * _scale, transform.w * _scale, 0) + position; + _vertices[i * 4 + 3] = rotation * new Vector3(transform.w * _scale, -transform.w * _scale, 0) + position; + } + + _mesh.bounds = new Bounds(new Vector3(parentPosition.x, 0, parentPosition.y), Vector3.one * (_size * 2)); + _mesh.vertices = _vertices; + } +} diff --git a/Assets/Scripts/Editor/MapBeltPerfBase.cs.meta b/Assets/Scripts/Editor/MapBeltPerfBase.cs.meta new file mode 100644 index 000000000..662341fb1 --- /dev/null +++ b/Assets/Scripts/Editor/MapBeltPerfBase.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c3e6b631c94f2384c87cf1906907af9f \ No newline at end of file diff --git a/Assets/Scripts/Editor/MapPresenterCapture.cs b/Assets/Scripts/Editor/MapPresenterCapture.cs new file mode 100644 index 000000000..1e0a727f0 --- /dev/null +++ b/Assets/Scripts/Editor/MapPresenterCapture.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; + +// Proof that a planet icon and a ping tint reach the pixels of the real map cameras when the planet's well is deeper +// than 1000 below the camera. The ARPG scene's own "Map Overlays" and "Map Tint" cameras (position, orthographic size, +// clip planes and culling mask as authored) render into a render texture on a solid black background. For each, in +// order: a control render with nothing drawn; a render with the thing placed 1100 below the camera (a plain quad, and the +// PingEffect prefab), which must equal the control because the far plane clips it; then MapPresenter's draw of the sun +// (well deeper than 2000) or the PingEffect prefab placed where EntityInstance places it, which must differ from the +// control at its pixel. A capture that draws nothing fails the last check, and one that cannot tell drawn from clipped +// fails the middle one. +// +// Unity -batchmode -projectPath -executeMethod MapPresenterCapture.Run -logFile +// +// Needs a GPU device; do not pass -nographics or -quit: Run exits the Editor itself, 0 on success and 1 on any failure. +public static class MapPresenterCapture +{ + private const int Size = 64; + + public static void Run() + { + var work = Path.Combine(Path.GetTempPath(), $"map-presenter-capture-{Guid.NewGuid():N}"); + try + { + Directory.CreateDirectory(work); + Check(work); + Debug.Log("[MapPresenterCapture] ok"); + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + private static void Require(bool condition, string what) + { + if (!condition) throw new InvalidOperationException(what); + } + + private static Camera SceneCamera(string name) + { + foreach (var camera in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) + if (camera.name == name) return camera; + throw new InvalidOperationException($"No camera named {name} in the scene."); + } + + private static Color Render(Camera camera, RenderTexture target, Vector3 at) + { + camera.targetTexture = target; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = Color.black; + camera.Render(); + var viewport = camera.WorldToViewportPoint(new Vector3(at.x, 0, at.z)); + var previous = RenderTexture.active; + RenderTexture.active = target; + var read = new Texture2D(Size, Size, TextureFormat.RGBA32, false); + read.ReadPixels(new Rect(0, 0, Size, Size), 0, 0); + RenderTexture.active = previous; + var pixel = read.GetPixel(Mathf.Clamp((int)(viewport.x * Size), 0, Size - 1), Mathf.Clamp((int)(viewport.y * Size), 0, Size - 1)); + UnityEngine.Object.DestroyImmediate(read); + return pixel; + } + + private static float Distance(Color a, Color b) => Mathf.Abs(a.r - b.r) + Mathf.Abs(a.g - b.g) + Mathf.Abs(a.b - b.b); + + private static void Check(string work) + { + EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Single); + var f = MapPresenterSmoke.Build(work, flatIcons: true); + f.Map.SetMinimapScale(300f); + var target = new RenderTexture(Size, Size, 24, RenderTextureFormat.ARGB32); + var layer = LayerMask.NameToLayer("Minimap"); + + // Planet: the sun's icon through the map overlays camera. + var overlays = SceneCamera("Map Overlays"); + Require((overlays.cullingMask & (1 << layer)) != 0, "The Map Overlays camera does not see the Minimap layer."); + var well = f.Zone.GetHeight(DrawAhead.Orbit(f.Zone, f.Zone.PlanetInstances[f.SunKey].BodyData.Orbit.Key, 0), 0); + Require(well < -1000f, $"The fixture sun's well is {well}, not deeper than 1000."); + Require(f.Map.TryDrawn(f.SunKey, out var sun), "The sun registered no icon."); + var sunAt = sun.GetPosition(); + // Control: the pixel with nothing queued. Then a quad 1100 below the camera, deeper than the far plane, which must + // leave the pixel as it was. Then the presenter's draw, which must change it. Draws queue for the cameras that + // render after them, so the order is empty, deep, drawn. + var empty = Render(overlays, target, sunAt); + var quad = GameObject.CreatePrimitive(PrimitiveType.Quad); + var deepMesh = quad.GetComponent().sharedMesh; + var deepMaterial = new Material(Shader.Find("Unlit/Color")) { color = Color.white }; + UnityEngine.Object.DestroyImmediate(quad); + Graphics.RenderMesh(new RenderParams(deepMaterial) { layer = layer }, deepMesh, 0, + Matrix4x4.TRS(new Vector3(sunAt.x, -1100f, sunAt.z), Quaternion.Euler(90, 0, 0), Vector3.one * 300f)); + var deep = Render(overlays, target, sunAt); + Require(Distance(deep, empty) < .01f, $"A mesh 1100 below the camera drew: {deep} against {empty}."); + f.Map.Draw(f.Zone, 0); + var drawn = Render(overlays, target, sunAt); + Require(Distance(drawn, empty) > .3f, $"The sun's icon is not in the pixels: drawn {drawn}, empty {empty}."); + + // Ping tint: the PingEffect prefab where EntityInstance places it, through the map tint camera. + var tint = SceneCamera("Map Tint"); + var ping = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath("Assets/Content/Prefabs/PingEffect.prefab")); + ping.transform.localScale = Vector3.one * 300f; + var pingAt = new Vector3(sunAt.x, MapPresenter.PlaneY, sunAt.z); + ping.transform.position = pingAt; + ping.SetActive(false); + var untinted = Render(tint, target, pingAt); + ping.SetActive(true); + ping.transform.position = new Vector3(sunAt.x, -1100f, sunAt.z); + var deepPing = Render(tint, target, pingAt); + Require(Distance(deepPing, untinted) < .01f, $"A ping 1100 below the camera drew: {deepPing} against {untinted}."); + ping.transform.position = pingAt; + var tinted = Render(tint, target, pingAt); + Require(Distance(tinted, untinted) > .02f, $"The ping tint is not in the pixels: with {tinted}, without {untinted}."); + Debug.Log($"[MapPresenterCapture] well={well} sun drawn={drawn} empty={empty} deep={deep}; ping tinted={tinted} untinted={untinted} deep={deepPing}"); + } +} diff --git a/Assets/Scripts/Editor/MapPresenterCapture.cs.meta b/Assets/Scripts/Editor/MapPresenterCapture.cs.meta new file mode 100644 index 000000000..53caa5ee1 --- /dev/null +++ b/Assets/Scripts/Editor/MapPresenterCapture.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cfa012aaae63b5044bdfd1b6b44ca9da \ No newline at end of file diff --git a/Assets/Scripts/Editor/MapPresenterSmoke.cs b/Assets/Scripts/Editor/MapPresenterSmoke.cs new file mode 100644 index 000000000..cccc4883c --- /dev/null +++ b/Assets/Scripts/Editor/MapPresenterSmoke.cs @@ -0,0 +1,233 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using GameCult.Caching; +using UnityEditor; +using UnityEngine; +using CultMath; +using CultMath.UnityBridge; +using static CultMath.math; +using float2 = CultMath.float2; +using float3 = CultMath.float3; +using float4 = CultMath.float4; + +// Batch proof of MapPresenter's contract on a real Zone, without a scene: a sun with a very deep well, a belt, and ships +// registered through EntityInstance.SetEntity as the game does. Planet icons draw at the map plane inside both map +// cameras' clip range however deep the well, a rolled and pitched hull's icon turns by yaw alone, every kind registers one +// draw and leaves no icon child behind, a belt is one instanced draw of every asteroid, and an entity's fade reaches its +// icon the frame it is set. Not reached: prefab, mod ship, station and turret instances (they register through the same +// call), and the rendered pixels (a GPU capture). +// +// Unity -batchmode -projectPath -executeMethod MapPresenterSmoke.Run -logFile +// +// Do not pass -quit: Run exits the Editor itself, 0 on success and 1 on any failure. +public static class MapPresenterSmoke +{ + private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + private const int Asteroids = 24; + + public static void Run() + { + var work = Path.Combine(Path.GetTempPath(), $"map-presenter-smoke-{Guid.NewGuid():N}"); + try + { + Directory.CreateDirectory(work); + Check(work); + Debug.Log("[MapPresenterSmoke] ok"); + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + private static void Require(bool condition, string what) + { + if (!condition) throw new InvalidOperationException(what); + } + + private static void Awaken(Component component) => component.GetType().GetMethod("Awake", Any)?.Invoke(component, null); + + // flat: authored lying on the map plane (facing up), as a real icon is; the smoke's own icons are authored upright + // so that their rotation is exactly the yaw. + private static MeshRenderer NewIcon(Transform parent, bool flat) + { + var icon = GameObject.CreatePrimitive(PrimitiveType.Quad); + icon.layer = LayerMask.NameToLayer("Minimap"); + icon.transform.SetParent(parent, false); + if (flat) icon.transform.localRotation = Quaternion.Euler(90, 0, 0); + var renderer = icon.GetComponent(); + renderer.sharedMaterial = new Material(Shader.Find("Unlit/Color")) { color = new Color(1, 1, 1, .8f) }; + return renderer; + } + + // What Build leaves registered with a presenter, for the checks here and for MapPresenterCapture. + internal sealed class Fixture + { + public Zone Zone; + public MapPresenter Map; + public Ship Ship, Other; + public EntityInstance ShipInstance, OtherInstance; + public CultRecordKey SunKey, BeltKey; + public float4[] Belt; + public Transform Roots; + } + + private static void Check(string work) + { + var f = Build(work, flatIcons: false); + Checks(f); + } + + internal static Fixture Build(string work, bool flatIcons) + { + var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset") + ?? throw new InvalidOperationException("No Settings.asset."); + var planetSettings = new PlanetSettings + { + BodyRadius = new ExponentialCurve(), + WellStrength = new ExponentialCurve { Multiplier = 1f, Exponent = 1f }, + RocheLimit = new ExponentialCurve { Constant = 2f }, + AsteroidSize = new ExponentialLerp { Minimum = 2f, Maximum = 9f, Exponent = 1.7f }, + AsteroidHitpoints = new ExponentialLerp { Minimum = 10f, Maximum = 300f, Exponent = 2f }, + AsteroidRespawnTime = new ExponentialLerp { Minimum = 5f, Maximum = 60f, Exponent = 1f } + }; + var cache = AetheriaStores.Open(Path.Combine(work, "c.cc"), Path.Combine(work, "r.cc"), catalogWritable: true); + cache.Upsert(new VerseGrammar { Revision = 1 }); + var shape = new Shape(5, 4); + foreach (var cell in shape.AllCoordinates) shape[cell] = true; + cache.Upsert(new HullData { Name = "Smoke Hull", HullType = HullType.Ship, Shape = shape, Durability = 1000, Mass = 1000, Armor = 0 }); + var sunOrbit = cache.Upsert(new OrbitData()).Key; + var sunKey = cache.Upsert(new PlanetData { Name = "Sun", Orbit = new CultRecordRef(sunOrbit), Mass = 1e6f }).Key; + var rootOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef(sunOrbit), Distance = 4000f, Phase = .17f }).Key; + var beltOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef(rootOrbit), Distance = 900f, Phase = .42f }).Key; + var asteroids = Enumerable.Range(0, Asteroids).Select(i => new Asteroid + { Distance = 120f + i * 11.3f, Phase = frac(i * .618f), Size = frac(i * .37f + .1f), RotationSpeed = .15f + i * .037f }).ToArray(); + var beltKey = cache.Upsert(new AsteroidBeltData { Orbit = new CultRecordRef(beltOrbit), Asteroids = asteroids }).Key; + var pack = new ZonePack + { + Orbits = { new CultRecordRef(sunOrbit), new CultRecordRef(rootOrbit), new CultRecordRef(beltOrbit) }, + Planets = { new CultRecordRef(sunKey), new CultRecordRef(beltKey) }, + Radius = 5000f, Mass = 10000f, Time = 0 + }; + var ledger = new ProvenanceLedger(); + for (var i = 1; i <= 8; i++) ledger.Lots[i] = new Lot { Origin = new Attributed(), Quality = 1 }; + var items = new ItemManager(1u, cache, ledger, settings.GameplaySettings, _ => { }); + var zone = new Zone(items, planetSettings, pack, new GalaxyZone { Name = "Smoke", Owner = null }, null); + var hullRef = items.ItemData.RefOf(items.ItemData.GetByName("Smoke Hull")); + var ship = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000, Lot = 1 }, new EntitySettings()); + var other = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000, Lot = 2 }, new EntitySettings()); + ship.Position = float3(300, 50, 200); + other.Position = float3(-300, -90, 100); + zone.Admit(ship, piloted: false); + zone.Admit(other, piloted: false); + + // A hull heading 40 degrees from +z, pitched up 89.9 and rolled 30: its forward is almost vertical, where the pose's + // eulerAngles.y is the gimbal decomposition (yaw and roll folded together), not the heading. The heading is the + // forward projected onto the map plane, 40. The fixture asserts the two differ, so a yaw read from eulerAngles + // cannot pass. + var pose = Quaternion.Euler(0, 40, 0) * Quaternion.Euler(-89.9f, 0, 30); + var flatForward = pose * Vector3.forward; + Require(Mathf.Abs(Mathf.DeltaAngle(Mathf.Atan2(flatForward.x, flatForward.z) * Mathf.Rad2Deg, 40f)) < .5f, "The fixture's forward does not head 40."); + Require(Mathf.Abs(Mathf.DeltaAngle(pose.eulerAngles.y, 40f)) > 10f, $"The fixture's eulerAngles.y {pose.eulerAngles.y} is aligned with its heading."); + ship.Direction = float2(Mathf.Sin(40 * Mathf.Deg2Rad), Mathf.Cos(40 * Mathf.Deg2Rad)); + typeof(Ship).GetProperty("Rotation", Any).GetSetMethod(true).Invoke(ship, new object[] { new CultMath.quaternion(pose.x, pose.y, pose.z, pose.w) }); + + var map = new MapPresenter(new Material[0], AssetDatabase.LoadAssetAtPath("Assets/Materials/UI/MapAsteroid.mat"), 4, 4, 3f); + var roots = new GameObject("Smoke Roots").transform; + EntityInstance Bound(Entity entity) + { + var body = new GameObject(entity.Name); + body.transform.SetParent(roots); + var instance = body.AddComponent(); + Awaken(instance); + instance.HullColliders = new HullCollider[0]; + instance.RadiatorHardpoints = new RadiatorHardpoint[0]; + instance.ThrusterHardpoints = new ThrusterHardpoint[0]; + instance.WeaponHardpoints = new WeaponHardpoint[0]; + instance.ArticulationPoints = new ArticulationPoint[0]; + instance.MapIcon = NewIcon(body.transform, flatIcons); + var zoneRenderer = new GameObject("Smoke Zone Renderer").AddComponent(); + zoneRenderer.Settings = settings; + zoneRenderer.MapGravityMaterials = new Material[0]; + Awaken(zoneRenderer); + typeof(ZoneRenderer).GetProperty("Map").GetSetMethod(true).Invoke(zoneRenderer, new object[] { map }); + instance.SetEntity(zoneRenderer, entity); + return instance; + } + var shipInstance = Bound(ship); + var otherInstance = Bound(other); + var sunRoot = new GameObject("Smoke Sun").transform; + sunRoot.SetParent(roots); + map.RegisterPlanet(sunKey, NewIcon(sunRoot, flatIcons), sunRoot); + var belt = new float4[Asteroids]; + map.RegisterBelt(beltKey, belt); + zone.EvaluateBelt(beltKey, belt, DrawAhead.Time(zone, 0)); + map.SetFade(ship, 1f); + map.SetFade(other, 1f); + map.SetMinimapScale(2f); + map.Draw(zone, 0); + return new Fixture { Zone = zone, Map = map, Ship = ship, Other = other, ShipInstance = shipInstance, + OtherInstance = otherInstance, SunKey = sunKey, BeltKey = beltKey, Belt = belt, Roots = roots }; + } + + private static void Checks(Fixture f) + { + var (zone, map, ship, other, sunKey, beltKey, belt, roots) = (f.Zone, f.Map, f.Ship, f.Other, f.SunKey, f.BeltKey, f.Belt, f.Roots); + // Deep planet: the sun's field is deeper than 2000 below the plane, and its icon sits on the map plane inside both + // map cameras' clip range (minimap at y 10, map screen at y 1; near 0.3, far 1000, looking down). + var well = zone.GetHeight(DrawAhead.Orbit(zone, zone.PlanetInstances[sunKey].BodyData.Orbit.Key, 0), 0); + Require(well < -2000f, $"The fixture sun's well is {well}, not below -2000."); + Require(map.TryDrawn(sunKey, out var sunIcon), "The sun registered no icon."); + var y = sunIcon.GetPosition().y; + Require(y < 1f - .3f && y > 10f - 1000f && Mathf.Abs(y - MapPresenter.PlaneY) < .5f, $"The sun's icon is drawn at y {y}, not on the map plane."); + + // Axes: each icon sits at its sim position's planar x and z (a ship at (300, 200), the other at (-300, 100), the sun at + // its orbit), never swapped, and never at the 3D height (the ships ride at y 50 and -90, the sun's well is below -2000). + var shipAt = DrawAhead.Position(ship, 0); + Require(Mathf.Abs(shipAt.x - 300f) < 1e-3f && Mathf.Abs(shipAt.z - 200f) < 1e-3f, $"The ship's sim position is ({shipAt.x}, {shipAt.z}), not (300, 200)."); + var sunAt = DrawAhead.Orbit(zone, zone.PlanetInstances[sunKey].BodyData.Orbit.Key, 0); + Require(map.TryDrawn(ship, out var shipIcon, out _), "The ship registered no icon."); + Require(Mathf.Abs(shipIcon.GetPosition().x - 300f) < 1e-3f && Mathf.Abs(shipIcon.GetPosition().z - 200f) < 1e-3f, + $"The ship's icon is at ({shipIcon.GetPosition().x}, {shipIcon.GetPosition().z}), not its sim position (300, 200)."); + Require(map.TryDrawn(other, out var otherIcon, out _), "The second ship registered no icon."); + Require(Mathf.Abs(otherIcon.GetPosition().x + 300f) < 1e-3f && Mathf.Abs(otherIcon.GetPosition().z - 100f) < 1e-3f, + $"The second ship's icon is at ({otherIcon.GetPosition().x}, {otherIcon.GetPosition().z}), not (-300, 100)."); + Require(Mathf.Abs(sunIcon.GetPosition().x - sunAt.x) < 1e-3f && Mathf.Abs(sunIcon.GetPosition().z - sunAt.y) < 1e-3f, + $"The sun's icon is at ({sunIcon.GetPosition().x}, {sunIcon.GetPosition().z}), not its orbit ({sunAt.x}, {sunAt.y})."); + Require(Mathf.Abs(otherIcon.GetPosition().y - (MapPresenter.PlaneY + MapPresenter.ShipOffset)) < 1e-4f, "The second ship's icon took the 3D height."); + + // Tilted hull: the icon turns by the heading projected onto the map plane (40) times its authored rotation (identity here), with no roll or pitch. + var want = Quaternion.Euler(0, 40, 0); + Require(Quaternion.Angle(shipIcon.rotation, want) < .1f, $"The icon is rotated {shipIcon.rotation.eulerAngles}, not yaw 40 alone."); + Require(Vector3.Angle(shipIcon.rotation * Vector3.up, Vector3.up) < .1f, "The icon's up axis tilted with the hull."); + Require(Mathf.Abs(shipIcon.GetPosition().y - (MapPresenter.PlaneY + MapPresenter.ShipOffset)) < 1e-4f, "The ship's icon left the map plane."); + + // Every kind registers once: three icons drew, and no icon child remains under any registered transform. + Require(map.IconCount == 3 && map.IconDraws == 3, $"{map.IconCount} icons, {map.IconDraws} drawn; expected 3 and 3."); + Require(roots.GetComponentsInChildren(true).Length == 0, "An icon child remains under an entity or planet transform."); + + // A belt is one instanced draw of every asteroid, and its buffer holds this frame's floats. + Require(map.BeltDraws == 1 && map.LastBeltInstances == belt.Length, $"{map.BeltDraws} belt draws of {map.LastBeltInstances} instances; expected 1 of {belt.Length}."); + var read = new float4[belt.Length]; + map.ReadBelt(beltKey, read); + for (var i = 0; i < belt.Length; i++) + Require(read[i].x == belt[i].x && read[i].y == belt[i].y && read[i].z == belt[i].z && read[i].w == belt[i].w, $"Belt buffer differs from the frame's floats at {i}."); + + // Belts draw on the minimap with the map screen closed. + map.OpenMapScreen(false); + map.Draw(zone, 0); + Require(map.BeltDraws == 1, "A belt did not draw on the minimap with the map screen closed."); + + // Fade: half faded reaches the icon's colour alpha on the next draw; faded out draws nothing. + map.SetFade(ship, .5f); + map.Draw(zone, 0); + Require(map.TryDrawn(ship, out _, out var alpha) && Mathf.Abs(alpha - .4f) < 1e-3f, $"Icon alpha {alpha}, expected 0.8 * 0.5."); + map.SetFade(ship, 0f); + map.Draw(zone, 0); + Require(map.IconDraws == 2, $"{map.IconDraws} icons drew with one fully faded out; expected 2."); + } +} diff --git a/Assets/Scripts/Editor/MapPresenterSmoke.cs.meta b/Assets/Scripts/Editor/MapPresenterSmoke.cs.meta new file mode 100644 index 000000000..2fff0ea86 --- /dev/null +++ b/Assets/Scripts/Editor/MapPresenterSmoke.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 58982a1c397cc2f4d9706fd103af6654 \ No newline at end of file diff --git a/Assets/Scripts/Editor/NameTools.cs b/Assets/Scripts/Editor/NameTools.cs deleted file mode 100644 index a048dce89..000000000 --- a/Assets/Scripts/Editor/NameTools.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Buffers.Text; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using MessagePack; -using CultMath; -using UnityEngine; -using UnityEditor; -using Random = CultMath.Random; - -public class NameTools : EditorWindow -{ - public int NameGeneratorMinLength = 5; - public int NameGeneratorMaxLength = 10; - public int NameGeneratorOrder = 4; - private int minWordLength = 4; - private TextAsset nameFile; - private MarkovNameGenerator _nameGenerator; - private bool _stripNumberTokens; - - - // Add menu named "My Window" to the Window menu - [MenuItem("Window/Aetheria/Name Tools")] - static void Init() - { - // Get existing open window or if none, make a new one: - NameTools window = (NameTools)EditorWindow.GetWindow(typeof(NameTools)); - window.Show(); - } - - bool HasNonASCIIChars(string str) - { - return (System.Text.Encoding.UTF8.GetByteCount(str) != str.Length); - } - - void OnGUI() - { - - nameFile = (TextAsset) EditorGUILayout.ObjectField("Name File", nameFile, typeof(TextAsset), false); - minWordLength = EditorGUILayout.IntField("Minimum File Word Length", minWordLength); - NameGeneratorMinLength = EditorGUILayout.IntField("Generated Minimum Word Length", NameGeneratorMinLength); - NameGeneratorMaxLength = EditorGUILayout.IntField("Generated Maximum Word Length", NameGeneratorMaxLength); - NameGeneratorOrder = EditorGUILayout.IntField("Generator Order", NameGeneratorOrder); - _stripNumberTokens = GUILayout.Toggle(_stripNumberTokens, "Strip Number Tokens"); - - if (GUILayout.Button("Clean Name File")) - { - var lines = nameFile.text.Split('\n'); - using StreamWriter outputFile = new StreamWriter(Path.Combine(Application.dataPath, nameFile.name + ".csv")); - var names = new HashSet(); - foreach (var line in lines) - { - var tokens = line.Split(',', ' '); - foreach (var t in tokens) - { - if (!HasNonASCIIChars(t)) - { - var s = new string(t.Where(c => char.IsLetter(c) || c == '-' || c == '`' || c == '\'').ToArray()).Trim().Trim('`','-'); - if(s.Length >= minWordLength && !names.Contains(s)) - { - names.Add(s); - outputFile.WriteLine(s); - } - } - } - } - } - - if (GUILayout.Button("Process Name File") && nameFile != null) - { - var names = new HashSet(); - var lines = nameFile.text.Split('\n'); - foreach (var line in lines) - { - foreach(var word in line.ToUpperInvariant().Split(' ', ',', '.', '"')) - if (word.Length >= minWordLength && !names.Contains(word)) - names.Add(word); - } - Debug.Log($"Found {lines.Length} lines, with {names.Count} unique names!"); - var random = new Random(1337); - _nameGenerator = new MarkovNameGenerator(ref random, names, NameGeneratorOrder, NameGeneratorMinLength, NameGeneratorMaxLength); - } - - if (_nameGenerator != null) - { - if (GUILayout.Button("Generate Name")) - { - Debug.Log(_nameGenerator.NextName); - } - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Editor/RockMeshRadius.cs b/Assets/Scripts/Editor/RockMeshRadius.cs new file mode 100644 index 000000000..971284924 --- /dev/null +++ b/Assets/Scripts/Editor/RockMeshRadius.cs @@ -0,0 +1,112 @@ +using System; +using System.Linq; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +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 -executeMethod Aetheria.EditorTools.RockMeshRadius.Fill -logFile +// Unity -batchmode -nographics -projectPath -executeMethod Aetheria.EditorTools.RockMeshRadius.Verify -logFile +// +// Fill measures every AsteroidMeshes entry and saves the scene; Verify fails (exit 1) when any stored radius is not the measured one. +// RockMeshRadiusBuildGate runs the same check before every player build and fails the build, so a mesh with no filled radius cannot ship; +// Gate runs that gate in batch mode. +// Unity -batchmode -nographics -projectPath -executeMethod Aetheria.EditorTools.RockMeshRadius.Gate -logFile +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() => Batch(() => + { + var scene = EditorSceneManager.OpenScene(Scene, OpenSceneMode.Single); + var (renderer, meshes, measured) = Load(scene); + 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."); + return Summary("filled", measured); + }); + + public static void Verify() => Batch(() => Check(OpenSceneMode.Single)); + + // The build gate's own entry in batch mode: exit 1 exactly when a build would fail (RockMeshRadiusBuildGate). + public static void Gate() => Batch(() => + { + try { new RockMeshRadiusBuildGate().OnPreprocessBuild(null); } + catch (BuildFailedException failure) { throw new InvalidOperationException(failure.Message); } + return "the build gate passes"; + }); + + // Throws unless every AsteroidMeshes entry of ARPG.unity has a stored radius equal to its mesh's measured one (a zero radius never does). + public static string Check(OpenSceneMode mode) + { + var scene = EditorSceneManager.OpenScene(Scene, mode); + try + { + var (_, meshes, measured) = Load(scene); + 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 (failures.Length > 0) + throw new InvalidOperationException("InstancedMesh.Radius differs from the measured one, or is unset, on mesh (run RockMeshRadius.Fill): " + string.Join(", ", + failures.Select(f => f.m.Mesh.name))); + return Summary("ok", measured); + } + finally + { + if (mode == OpenSceneMode.Additive) EditorSceneManager.CloseScene(scene, true); + } + } + + private static (ZoneRenderer renderer, InstancedMesh[] meshes, float[] measured) Load(UnityEngine.SceneManagement.Scene scene) + { + var renderer = UnityEngine.Object.FindObjectsByType(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."); + return (renderer, meshes, meshes.Select(m => Measure(m.Mesh)).ToArray()); + } + + private static string Summary(string what, float[] measured) + { + var sorted = measured.OrderBy(r => r).ToArray(); + return $"{what}: {sorted.Length} meshes, radius min {sorted[0]:F3} median {sorted[sorted.Length / 2]:F3} max {sorted[^1]:F3}"; + } + + private static void Batch(Func run) + { + try + { + Debug.Log($"[RockMeshRadius] {run()}"); + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + } + + // A mesh in AsteroidMeshes with no filled radius must not ship: the build fails, naming the field. + public class RockMeshRadiusBuildGate : IPreprocessBuildWithReport + { + public int callbackOrder => 0; + + public void OnPreprocessBuild(BuildReport report) + { + try { RockMeshRadius.Check(OpenSceneMode.Additive); } + catch (InvalidOperationException failure) { throw new BuildFailedException(failure.Message); } + } + } +} diff --git a/Assets/Scripts/Editor/RockMeshRadius.cs.meta b/Assets/Scripts/Editor/RockMeshRadius.cs.meta new file mode 100644 index 000000000..f95fec963 --- /dev/null +++ b/Assets/Scripts/Editor/RockMeshRadius.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9ea93f3d164825c4b99a0d07f24d5cf6 \ No newline at end of file diff --git a/Assets/Scripts/Editor/ScenarioPickerCapture.cs b/Assets/Scripts/Editor/ScenarioPickerCapture.cs new file mode 100644 index 000000000..b39dd7a1b --- /dev/null +++ b/Assets/Scripts/Editor/ScenarioPickerCapture.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.UI; + +// A render of the New Game screen as the game builds it: the real Main Menu scene's panel prototype, docked and filled +// by ScenarioPicker.Dock and ScenarioPicker.Show with every scenario group, on a plain dark background (the cloud +// backdrop is a play-mode renderer). Writes /Temp/scenario-picker.png at 1920x1080, or the path after -pngOut (Unity empties Temp on exit). +// +// Unity -batchmode -projectPath -executeMethod ScenarioPickerCapture.Run [-pngOut ] -logFile +// +// Needs a GPU device; do not pass -nographics or -quit: Run exits the Editor itself, 0 on success and 1 on any failure. +public static class ScenarioPickerCapture +{ + private const int Width = 1920, Height = 1080; + + public static void Run() + { + try + { + Capture(); + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + // The menu's two panes built the way MainMenu.Start builds them, against the Main Menu scene. + internal static (MainMenu menu, PropertiesPanel list, PropertiesPanel detail) Build() + { + EditorSceneManager.OpenScene("Assets/Scenes/Main Menu.unity", OpenSceneMode.Single); + var menu = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)[0]; + menu.PanelPrototype.gameObject.SetActive(false); + var list = menu.PanelPrototype.Instantiate(); + var detail = menu.PanelPrototype.Instantiate(); + // Edit mode runs no Awake, which is what activates each row a panel adds. + list.Awake(); + detail.Awake(); + ScenarioPicker.Dock(detail, list); + return (menu, list, detail); + } + + internal static List<(string label, Scenario[] scenarios)> AllGroups() => new List<(string label, Scenario[] scenarios)> + { + ("modes", Scenarios.Modes), ("development", Scenarios.Development), ("test arenas", Scenarios.Tests) + }; + + private static void Capture() + { + var (menu, list, detail) = Build(); + menu.Dialog.gameObject.SetActive(false); + list.Title.text = MainMenu.TitleSubtitle("new game", "scenarios"); + ScenarioPicker.Show(list, detail, AllGroups(), _ => { }, () => { }, menu.Dialog.Confirm); + list.RefreshValues(); + detail.RefreshValues(); + + var canvas = menu.GetComponentInParent(true).rootCanvas; + var target = new RenderTexture(Width, Height, 24, RenderTextureFormat.ARGB32); + var cameraObject = new GameObject("ScenarioPickerCapture camera"); + var camera = cameraObject.AddComponent(); + camera.orthographic = true; + camera.orthographicSize = 5; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = new Color(.04f, .05f, .08f); + camera.cullingMask = 1 << LayerMask.NameToLayer("UI"); + camera.targetTexture = target; + canvas.renderMode = RenderMode.ScreenSpaceCamera; + canvas.worldCamera = camera; + canvas.planeDistance = 10; + canvas.gameObject.SetActive(true); + Canvas.ForceUpdateCanvases(); + LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform) list.transform); + LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform) detail.transform); + foreach (var text in list.transform.GetComponentsInChildren()) + text.ForceMeshUpdate(); + Canvas.ForceUpdateCanvases(); + list.Content.GetComponentInParent().onValueChanged.Invoke(Vector2.zero); + camera.Render(); + + var previous = RenderTexture.active; + RenderTexture.active = target; + var read = new Texture2D(Width, Height, TextureFormat.RGBA32, false); + read.ReadPixels(new Rect(0, 0, Width, Height), 0, 0); + RenderTexture.active = previous; + + var background = read.GetPixel(0, 0); + var differing = 0; + foreach (var pixel in read.GetPixels32()) + if (Mathf.Abs(pixel.r / 255f - background.r) + Mathf.Abs(pixel.g / 255f - background.g) + Mathf.Abs(pixel.b / 255f - background.b) > .05f) + differing++; + if (differing < Width * Height / 100) + throw new InvalidOperationException($"The capture is uniform: only {differing} pixels differ from the background."); + + var path = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Temp", "scenario-picker.png")); + var args = Environment.GetCommandLineArgs(); + var outIndex = Array.IndexOf(args, "-pngOut"); + if (outIndex >= 0) path = Path.GetFullPath(args[outIndex + 1]); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllBytes(path, read.EncodeToPNG()); + Debug.Log($"[ScenarioPickerCapture] {path} ({differing} of {Width * Height} pixels differ from the background)"); + } +} diff --git a/Assets/Scripts/Editor/ScenarioPickerCapture.cs.meta b/Assets/Scripts/Editor/ScenarioPickerCapture.cs.meta new file mode 100644 index 000000000..ebeb742e8 --- /dev/null +++ b/Assets/Scripts/Editor/ScenarioPickerCapture.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2e79f280179150e4080250c45e07cd69 \ No newline at end of file diff --git a/Assets/Scripts/Editor/ScenarioPickerVerify.cs b/Assets/Scripts/Editor/ScenarioPickerVerify.cs new file mode 100644 index 000000000..6816cafc9 --- /dev/null +++ b/Assets/Scripts/Editor/ScenarioPickerVerify.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TMPro; +using UnityEditor; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +// The picker's selection and keyboard rules, driven through the events the game delivers: Move and Cancel reach the +// focused Launch button through ExecuteEvents, as the Input System UI module sends them, and a click is Button.onClick. +// +// Unity -batchmode -projectPath -executeMethod ScenarioPickerVerify.Run -logFile +// +// Exits the Editor itself, 0 when every rule holds and 1 on the first that does not. +public static class ScenarioPickerVerify +{ + private sealed class Fake : Scenario + { + private readonly uint _seed; + private readonly bool _ambient; + public Fake(string name, uint seed = 0, bool ambient = true) { Name = name; _seed = seed; _ambient = ambient; } + public override string Name { get; } + public override string Id => Name.ToLowerInvariant(); + public override string Brief => $"brief of {Name}"; + public override uint Seed => _seed; + public override bool Ambient => _ambient; + public override Galaxy Generate(GalaxyStage stage) => throw new NotSupportedException(); + public override void Stage(ScenarioStage stage) => throw new NotSupportedException(); + } + + private sealed class Screen + { + public ScenarioPicker Picker; + public PropertiesPanel List, Detail; + public Button[] Rows; + public Button Back, Launch; + public readonly List Launched = new List(); + public int Backs; + public Scenario[] A, B, C; + } + + public static void Run() + { + try + { + SelectionFollowsClicksAndArrows(); + ArrowsStopAtTheEnds(); + LeftAndRightAreIgnored(); + LaunchIsOnePathAndFocusClears(); + CancelAndBackAreOnePath(); + DetailPaneFollowsSelection(); + OneGroupHasNoSectionLabel(); + LaunchesOnceHoweverItIsReached(); + ArrowingThroughTheRealListKeepsTheSelectionVisible(); + Debug.Log("[ScenarioPickerVerify] ok"); + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } + + private static void Require(bool condition, string what) + { + if (!condition) throw new InvalidOperationException(what); + } + + private static Screen Build(bool oneGroup = false, bool real = false) + { + var (menu, list, detail) = ScenarioPickerCapture.Build(); + // Build opens the scene afresh, which destroys the previous EventSystem. + var events = new GameObject("verify EventSystem").AddComponent(); + // Edit mode never calls OnEnable, which is what registers an EventSystem as current. + typeof(EventSystem).GetMethod("OnEnable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(events, null); + var screen = new Screen + { + List = list, Detail = detail, + A = new Scenario[] { new Fake("Alpha", 7), new Fake("Beta") }, + B = new Scenario[] { new Fake("Gamma", ambient: false) }, + C = new Scenario[] { new Fake("Delta"), new Fake("Echo") } + }; + var groups = real ? ScenarioPickerCapture.AllGroups() : oneGroup + ? new List<(string label, Scenario[] scenarios)> { ("modes", screen.A) } + : new List<(string label, Scenario[] scenarios)> { ("modes", screen.A), ("development", screen.B), ("test arenas", screen.C) }; + // The menu builds a panel while it is inactive and shows it afterwards; the picker must find its scroll all the same. + if (real) list.gameObject.SetActive(false); + screen.Picker = ScenarioPicker.Show(list, detail, groups, screen.Launched.Add, () => screen.Backs++, menu.Dialog.Confirm); + var buttons = list.GetComponentsInChildren(false).Where(button => !button.transform.IsChildOf(detail.transform)).Select(button => button.Button).ToArray(); + screen.Rows = buttons.Take(buttons.Length - 1).ToArray(); + screen.Back = buttons.Last(); + screen.Launch = detail.GetComponentsInChildren