Compare commits
814 changed files with 17678 additions and 51825 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -94,7 +94,7 @@ crashlytics-build.properties
|
|||
Packages/packages-lock.json
|
||||
GameData/run.cc
|
||||
GameData/player.cc
|
||||
GameData/RemovedMods/
|
||||
Vault/Aetheria/.obsidian/plugins/copilot/data.json
|
||||
|
||||
# Agent scratch, sandboxed toolchain caches, and throwaway checkouts. These had
|
||||
# grown to 47 untracked entries; the worktree must stay legible, so they never
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
[lfs]
|
||||
url = https://eureka.gamecult.org/forge/GameCult/Aetheria.git/info/lfs
|
||||
|
|
@ -146,5 +146,6 @@ MonoBehaviour:
|
|||
ActiveEmission: 100
|
||||
ArmedEmission: 1000
|
||||
RotationSpeed: 1
|
||||
GridOffset: 0
|
||||
HitEffect: {fileID: 1121192042590650, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
|
||||
HitEffectScale: 10
|
||||
|
|
|
|||
|
|
@ -81,6 +81,87 @@ 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
|
||||
|
|
@ -208,6 +289,7 @@ Transform:
|
|||
m_Children:
|
||||
- {fileID: 589192530128514016}
|
||||
- {fileID: 7783244518317531036}
|
||||
- {fileID: 7783244517717541195}
|
||||
- {fileID: 7783244516809707565}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
|
|
@ -225,6 +307,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Body: {fileID: 7783244518317531038}
|
||||
GravityWell: {fileID: 7783244517717541174}
|
||||
Icon: {fileID: 7783244516809707566}
|
||||
Generator: {fileID: 741236912059895623}
|
||||
--- !u!114 &741236912059895623
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ using UnityEditor.AddressableAssets.Settings;
|
|||
using UnityEditor.Build.Reporting;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
// The one way to make a player directory, for Windows or Linux. Owns what the directory contains: the player and its data,
|
||||
// The one way to make a Win64 player directory. Owns what the directory contains: Aetheria.exe 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 <repo> -executeMethod AetheriaBuild.Build
|
||||
// -aetheriaVersion <semver> -aetheriaOut <dir> [-aetheriaTarget win64|linux64] -logFile <log>
|
||||
// -aetheriaVersion <semver> -aetheriaOut <dir> -logFile <log>
|
||||
//
|
||||
// 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,14 +22,6 @@ 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";
|
||||
|
|
@ -38,7 +30,6 @@ 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";
|
||||
|
|
@ -55,8 +46,8 @@ public static class AetheriaBuild
|
|||
var report = BuildPipeline.BuildPlayer(new BuildPlayerOptions
|
||||
{
|
||||
scenes = scenes,
|
||||
locationPathName = Path.Combine(output, target.player),
|
||||
target = target.target,
|
||||
locationPathName = Path.Combine(output, "Aetheria.exe"),
|
||||
target = BuildTarget.StandaloneWindows64,
|
||||
options = BuildOptions.None,
|
||||
});
|
||||
if (report.summary.result != BuildResult.Succeeded || report.summary.totalErrors != 0)
|
||||
|
|
@ -66,7 +57,7 @@ public static class AetheriaBuild
|
|||
PlayerStaging.Stage(repoRoot, output);
|
||||
|
||||
step = "aetherdb";
|
||||
Publish(repoRoot, Path.Combine(output, "ModTools"), target.runtime, target.aetherDb);
|
||||
Publish(repoRoot, Path.Combine(output, "ModTools"));
|
||||
|
||||
step = "burst";
|
||||
var burst = Path.Combine(output, "Aetheria_BurstDebugInformation_DoNotShip");
|
||||
|
|
@ -83,11 +74,11 @@ public static class AetheriaBuild
|
|||
EditorApplication.Exit(0);
|
||||
}
|
||||
|
||||
private static void Publish(string repoRoot, string modTools, string runtime, string aetherDb)
|
||||
private static void Publish(string repoRoot, string modTools)
|
||||
{
|
||||
var info = new ProcessStartInfo("dotnet")
|
||||
{
|
||||
Arguments = $"publish tools/AetherDb -c Release -r {runtime} --self-contained -p:PublishSingleFile=true -o \"{modTools}\"",
|
||||
Arguments = $"publish tools/AetherDb -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o \"{modTools}\"",
|
||||
WorkingDirectory = repoRoot,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
|
|
@ -101,16 +92,15 @@ 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)))
|
||||
throw new InvalidOperationException($"dotnet publish produced no ModTools/{aetherDb}");
|
||||
if (!File.Exists(Path.Combine(modTools, "AetherDb.exe")))
|
||||
throw new InvalidOperationException("dotnet publish produced no ModTools/AetherDb.exe");
|
||||
}
|
||||
|
||||
private static string Argument(string name, string fallback = null)
|
||||
private static string Argument(string name)
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
var at = Array.IndexOf(args, name);
|
||||
if (at < 0 || at + 1 >= args.Length)
|
||||
return fallback ?? throw new InvalidOperationException(name + " is required");
|
||||
if (at < 0 || at + 1 >= args.Length) throw new InvalidOperationException(name + " is required");
|
||||
return args[at + 1];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ 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).
|
||||
// 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;
|
||||
// SoundbanksInfo.json is read by no code). 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";
|
||||
|
|
|
|||
83
Assets/Materials/Brushes/Gravity Waves.mat
Normal file
83
Assets/Materials/Brushes/Gravity Waves.mat
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
%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: []
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c1012d5466454651b7279be93ef9af88
|
||||
guid: 11fbaa87dcc7a3743a6bd3aa5c86e5ae
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
|
|
@ -7,8 +7,8 @@ Material:
|
|||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Gravity Field
|
||||
m_Shader: {fileID: 4800000, guid: 08d8efa80af84065b54c9bfed784e435, type: 3}
|
||||
m_Name: Gravity Well
|
||||
m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, type: 3}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
|
|
@ -57,6 +57,8 @@ Material:
|
|||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 1
|
||||
- _Depth: 80
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
|
|
@ -66,6 +68,7 @@ Material:
|
|||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _Power: 16
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
|
|
@ -8,7 +8,7 @@ Material:
|
|||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Sector Tint
|
||||
m_Shader: {fileID: 4800000, guid: 782740c1a3d7455ea75dc87fafdc8432, type: 3}
|
||||
m_Shader: {fileID: 4800000, guid: 268da230635cfb34ab4ed6ad39503fa0, 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,6 +72,7 @@ Material:
|
|||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _Phase: 0
|
||||
- _Power: 2
|
||||
- _SinePower: 1.25
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
|
|
|
|||
80
Assets/Materials/Brushes/Wormhole Gravity 1.mat
Normal file
80
Assets/Materials/Brushes/Wormhole Gravity 1.mat
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Brushes/Wormhole Gravity 1.mat.meta
Normal file
8
Assets/Materials/Brushes/Wormhole Gravity 1.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 244f3e4c0c030a24b8d7aa944895bdcf
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
Assets/Materials/Brushes/Wormhole Gravity 2.mat
Normal file
80
Assets/Materials/Brushes/Wormhole Gravity 2.mat
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Brushes/Wormhole Gravity 2.mat.meta
Normal file
8
Assets/Materials/Brushes/Wormhole Gravity 2.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0ec0689c58b99c64297b3e429e1d9112
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
82
Assets/Materials/Brushes/Wormhole Gravity.mat
Normal file
82
Assets/Materials/Brushes/Wormhole Gravity.mat
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Brushes/Wormhole Gravity.mat.meta
Normal file
8
Assets/Materials/Brushes/Wormhole Gravity.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a9ef17c902709ce41a837c54b809184b
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
84
Assets/Materials/Brushes/Wormhole Waves.mat
Normal file
84
Assets/Materials/Brushes/Wormhole Waves.mat
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Brushes/Wormhole Waves.mat.meta
Normal file
8
Assets/Materials/Brushes/Wormhole Waves.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef2f6090d5cc14a4e9ce38def3bed9ac
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
73
Assets/Materials/Fog Marching 1.mat
Normal file
73
Assets/Materials/Fog Marching 1.mat
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Fog Marching 1.mat.meta
Normal file
8
Assets/Materials/Fog Marching 1.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a72896cb7097bfa46a1a8f4092e47c72
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
99
Assets/Materials/Fog Marching Main Menu.mat
Normal file
99
Assets/Materials/Fog Marching Main Menu.mat
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Fog Marching Main Menu.mat.meta
Normal file
8
Assets/Materials/Fog Marching Main Menu.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e21702a5e6e98b7409d6913663ceab7e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
65
Assets/Materials/Fog Marching Test.mat
Normal file
65
Assets/Materials/Fog Marching Test.mat
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Fog Marching Test.mat.meta
Normal file
8
Assets/Materials/Fog Marching Test.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ad63650ae6d6cc542a5aad16694b3c46
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
99
Assets/Materials/Fog Marching.mat
Normal file
99
Assets/Materials/Fog Marching.mat
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
%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: []
|
||||
8
Assets/Materials/Fog Marching.mat.meta
Normal file
8
Assets/Materials/Fog Marching.mat.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2ff39f5d101241043ba1dcde19d92f77
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,31 +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: 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: []
|
||||
|
|
@ -189,6 +189,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
RotationSpeed: 1
|
||||
GridOffset: 0
|
||||
LabelFadeDuration: 0.5
|
||||
LabelPersistDuration: 3
|
||||
LabelDisplayAngle: 15
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
RotationSpeed: 1
|
||||
GridOffset: 0
|
||||
LabelFadeDuration: 0.5
|
||||
LabelPersistDuration: 3
|
||||
LabelDisplayAngle: 15
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
RotationSpeed: 1
|
||||
GridOffset: 0
|
||||
LabelFadeDuration: 0.5
|
||||
LabelPersistDuration: 3
|
||||
LabelDisplayAngle: 15
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
RotationSpeed: 1
|
||||
GridOffset: 0
|
||||
LabelFadeDuration: 0.5
|
||||
LabelPersistDuration: 3
|
||||
LabelDisplayAngle: 15
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
RotationSpeed: 1
|
||||
GridOffset: 0
|
||||
LabelFadeDuration: 0.5
|
||||
LabelPersistDuration: 3
|
||||
LabelDisplayAngle: 15
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ Transform:
|
|||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3973935122748960680}
|
||||
- {fileID: 3973935121058605868}
|
||||
- {fileID: 3973935122114073483}
|
||||
- {fileID: 3973935121358855036}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
|
|
@ -47,9 +49,94 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Body: {fileID: 3973935122748960683}
|
||||
GravityWell: {fileID: 3973935121058605871}
|
||||
Icon: {fileID: 3973935121358855037}
|
||||
Generator: {fileID: 0}
|
||||
SunMaterial: {fileID: 3973935122748960685}
|
||||
GravityWaves: {fileID: 3973935122114073485}
|
||||
--- !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
|
||||
|
|
@ -133,6 +220,89 @@ MeshRenderer:
|
|||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!1 &3973935122114073482
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3973935122114073483}
|
||||
- component: {fileID: 3973935122114073486}
|
||||
- component: {fileID: 3973935122114073485}
|
||||
m_Layer: 10
|
||||
m_Name: Gravity Waves
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &3973935122114073483
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3973935122114073482}
|
||||
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: 2
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
--- !u!33 &3973935122114073486
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3973935122114073482}
|
||||
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!23 &3973935122114073485
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3973935122114073482}
|
||||
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: 11fbaa87dcc7a3743a6bd3aa5c86e5ae, 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 &3973935122748960663
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
|||
|
|
@ -83,6 +83,89 @@ 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
|
||||
|
|
@ -114,6 +197,7 @@ Transform:
|
|||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8859079005270853336}
|
||||
- {fileID: 8859079004671924239}
|
||||
- {fileID: 8859079003226229097}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
|
|
@ -131,6 +215,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Body: {fileID: 8859079005270853338}
|
||||
GravityWell: {fileID: 8859079004671924338}
|
||||
Icon: {fileID: 8859079003226229098}
|
||||
Generator: {fileID: -7153121627769399805}
|
||||
--- !u!114 &-7153121627769399805
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ Transform:
|
|||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7852221720971737233}
|
||||
- {fileID: 7852221720969441490}
|
||||
- {fileID: 7852221720741322892}
|
||||
- {fileID: 7852221720816817928}
|
||||
- {fileID: 7852221720964551245}
|
||||
m_Father: {fileID: 0}
|
||||
|
|
@ -48,11 +50,96 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Body: {fileID: 7852221720971737229}
|
||||
GravityWell: {fileID: 7852221720969441484}
|
||||
Icon: {fileID: 7852221720964551246}
|
||||
Generator: {fileID: 0}
|
||||
SunMaterial: {fileID: 7852221720971737234}
|
||||
GravityWaves: {fileID: 7852221720741322894}
|
||||
FogTint: {fileID: 7852221720816817929}
|
||||
Light: {fileID: 8064377225639657765}
|
||||
--- !u!1 &7852221720741322899
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7852221720741322892}
|
||||
- component: {fileID: 7852221720741322895}
|
||||
- component: {fileID: 7852221720741322894}
|
||||
m_Layer: 10
|
||||
m_Name: Gravity Waves
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7852221720741322892
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7852221720741322899}
|
||||
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: 2
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
--- !u!33 &7852221720741322895
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7852221720741322899}
|
||||
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!23 &7852221720741322894
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7852221720741322899}
|
||||
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: 11fbaa87dcc7a3743a6bd3aa5c86e5ae, 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 &7852221720816817935
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -219,6 +306,89 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,167 @@
|
|||
%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
|
||||
|
|
@ -27,6 +189,9 @@ 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
|
||||
|
|
@ -112,3 +277,84 @@ 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}
|
||||
|
|
|
|||
111
Assets/Prefabs/RTS/Asteroid Belt.prefab
Normal file
111
Assets/Prefabs/RTS/Asteroid Belt.prefab
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
%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:
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fd5f95c7d7c63b243aadd16f6e9dc702
|
||||
ComputeShaderImporter:
|
||||
guid: 1f1bfa2175a13b647888e2949e0cb138
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
|
@ -223,6 +223,7 @@ Transform:
|
|||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5321404120083801960}
|
||||
- {fileID: 5321404118931821940}
|
||||
- {fileID: 5321404119494867799}
|
||||
- {fileID: 1093865191529778971}
|
||||
|
|
@ -244,7 +245,7 @@ MonoBehaviour:
|
|||
Label: {fileID: 5321404119494867796}
|
||||
Message: {fileID: 6196070888394201391}
|
||||
Children: []
|
||||
GravityMesh: {fileID: 0}
|
||||
GravityMesh: {fileID: 5321404120083801962}
|
||||
--- !u!1 &5321404118931821943
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -563,3 +564,93 @@ 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}
|
||||
|
|
|
|||
8
Assets/Resources/Geonames.meta
Normal file
8
Assets/Resources/Geonames.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0f3e518a4c9bcc2469abe6cebdcb3691
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Australia.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Australia.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Australia.csv.meta
Normal file
7
Assets/Resources/Geonames/Australia.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8ba25d5d0ecee31408c7ce7460b4e015
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Congo.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Congo.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Congo.csv.meta
Normal file
7
Assets/Resources/Geonames/Congo.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e43b383bf860524986a6947496776d0
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Germany.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Germany.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Germany.csv.meta
Normal file
7
Assets/Resources/Geonames/Germany.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ed470b8eb6cc68c4485ddc05d5a17ca1
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/India.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/India.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/India.csv.meta
Normal file
7
Assets/Resources/Geonames/India.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca62f6d649360184aaa975c2716dfb27
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Iran.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Iran.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Iran.csv.meta
Normal file
7
Assets/Resources/Geonames/Iran.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e2d60c7158f6a01418d78e46c5845729
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Japan.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Japan.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Japan.csv.meta
Normal file
7
Assets/Resources/Geonames/Japan.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5dd450a6f5ec92d49871674b33509135
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Mexico.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Mexico.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Mexico.csv.meta
Normal file
7
Assets/Resources/Geonames/Mexico.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b75e17ea31f6a62488628bf9605bd48c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Russia.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Russia.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Russia.csv.meta
Normal file
7
Assets/Resources/Geonames/Russia.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5c1aee30ccf3a1e44bddeb4d550edbf0
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Swahili.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Swahili.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Swahili.csv.meta
Normal file
7
Assets/Resources/Geonames/Swahili.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 63e21276398c8b0449e01d97a088add1
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/Thailand.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/Thailand.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/Thailand.csv.meta
Normal file
7
Assets/Resources/Geonames/Thailand.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2923f8bd3a2f0fb4084f64138e6d793d
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/USA.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/USA.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/USA.csv.meta
Normal file
7
Assets/Resources/Geonames/USA.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a06c0e26f73d51f489303deadad77030
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1384
Assets/Resources/Geonames/bad-words.txt
Normal file
1384
Assets/Resources/Geonames/bad-words.txt
Normal file
File diff suppressed because it is too large
Load diff
7
Assets/Resources/Geonames/bad-words.txt.meta
Normal file
7
Assets/Resources/Geonames/bad-words.txt.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 790ef350f8658b04480e0645919a21fa
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Geonames/pleiades.csv
(Stored with Git LFS)
Normal file
BIN
Assets/Resources/Geonames/pleiades.csv
(Stored with Git LFS)
Normal file
Binary file not shown.
|
7
Assets/Resources/Geonames/pleiades.csv.meta
Normal file
7
Assets/Resources/Geonames/pleiades.csv.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b532694a5ec3cc6458d42aa2835537b5
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Settings.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings.asset
(Stored with Git LFS)
Binary file not shown.
|
|
@ -409,13 +409,13 @@ Transform:
|
|||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 23420356}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: -5, z: 0}
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
m_Father: {fileID: 1037490739}
|
||||
m_LocalEulerAnglesHint: {x: 0, 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: 5120
|
||||
m_Bits: 5632
|
||||
m_RenderingPath: 1
|
||||
m_TargetTexture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2}
|
||||
m_TargetDisplay: 0
|
||||
|
|
@ -16322,7 +16322,7 @@ MonoBehaviour:
|
|||
Settings: {fileID: 11400000, guid: cd2344e2001a37544aab0960c164d4ff, type: 2}
|
||||
ZoneRoot: {fileID: 2095699057}
|
||||
SectorBrushes: {fileID: 1037490739}
|
||||
AmbientLight: {fileID: 23420357}
|
||||
SectorBoundaryBrush: {fileID: 1476765550}
|
||||
SceneCamera: {fileID: 887677622}
|
||||
FogCameras:
|
||||
- {fileID: 559840917}
|
||||
|
|
@ -16333,98 +16333,70 @@ 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}
|
||||
|
|
@ -16445,13 +16417,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
|
||||
MapAsteroidMaterial: {fileID: 2100000, guid: c1012d5466454651b7279be93ef9af88, type: 2}
|
||||
AsteroidBeltUI: {fileID: 349597964696402918, guid: 1f1bfa2175a13b647888e2949e0cb138,
|
||||
type: 3}
|
||||
Planet: {fileID: 8287217960984982033, guid: 88ace6073e3db1a46bd3aaf4f971acb9, type: 3}
|
||||
GasGiant: {fileID: 3250623180916197132, guid: 85753b4a7c762274385af384abed7a04,
|
||||
type: 3}
|
||||
|
|
@ -16491,6 +16463,7 @@ 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
|
||||
|
|
@ -16891,6 +16864,8 @@ Transform:
|
|||
m_LocalScale: {x: 3000, y: 3000, z: 3000}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1476765548}
|
||||
- {fileID: 23420357}
|
||||
- {fileID: 1108843909}
|
||||
- {fileID: 1647230682}
|
||||
- {fileID: 1254851255}
|
||||
|
|
@ -22664,6 +22639,96 @@ RectTransform:
|
|||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1 &1476765547
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1476765548}
|
||||
- component: {fileID: 1476765551}
|
||||
- component: {fileID: 1476765550}
|
||||
m_Layer: 9
|
||||
m_Name: Boundary
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1476765548
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1476765547}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1037490739}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1476765550
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1476765547}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 0
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 2
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
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: 8f45fb3a0f420d5428f1528827d55fa2, 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_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1476765551
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1476765547}
|
||||
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!1 &1485884351
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -23500,7 +23565,7 @@ Camera:
|
|||
m_Depth: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 512
|
||||
m_Bits: 1536
|
||||
m_RenderingPath: 1
|
||||
m_TargetTexture: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2}
|
||||
m_TargetDisplay: 0
|
||||
|
|
@ -38421,7 +38486,6 @@ SceneRoots:
|
|||
- {fileID: 906767442}
|
||||
- {fileID: 1009503730}
|
||||
- {fileID: 1037490739}
|
||||
- {fileID: 23420357}
|
||||
- {fileID: 2095699057}
|
||||
- {fileID: 134113271}
|
||||
- {fileID: 666227432}
|
||||
|
|
|
|||
|
|
@ -892,6 +892,7 @@ 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
|
||||
|
|
@ -981,7 +982,7 @@ Camera:
|
|||
m_Depth: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 5120
|
||||
m_Bits: 5632
|
||||
m_RenderingPath: 1
|
||||
m_TargetTexture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2}
|
||||
m_TargetDisplay: 0
|
||||
|
|
@ -1060,6 +1061,16 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 512
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 512
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720816817928, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
|
|
@ -1075,6 +1086,16 @@ 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
|
||||
|
|
|
|||
|
|
@ -911,6 +911,26 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 40
|
||||
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
|
||||
|
|
@ -1758,6 +1778,26 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 40
|
||||
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
|
||||
|
|
@ -3769,7 +3809,7 @@ Camera:
|
|||
m_Depth: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 5120
|
||||
m_Bits: 5632
|
||||
m_RenderingPath: 1
|
||||
m_TargetTexture: {fileID: 8400000, guid: 06ec5ef8a5830fc44b1ac1a6e123add5, type: 2}
|
||||
m_TargetDisplay: 0
|
||||
|
|
@ -5257,6 +5297,26 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 40
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,244 +0,0 @@
|
|||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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<GameSettings>("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<ZoneRenderer>(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<MeshFilter>().sharedMesh = Resources.GetBuiltinResource<Mesh>("Quad.fbx");
|
||||
quad.AddComponent<MeshRenderer>().sharedMaterial = AssetDatabase.LoadAssetAtPath<Material>("Assets/Materials/Brushes/Sector Tint.mat");
|
||||
return quad.transform;
|
||||
}
|
||||
|
||||
static void Check(ZoneRenderer renderer, Zone zone)
|
||||
{
|
||||
var production = AssetDatabase.LoadAssetAtPath<RenderTexture>(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<MeshRenderer>();
|
||||
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<RenderTexture>(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>();
|
||||
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<float>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 63b03af05dfdae44c89e21ca7ebec892
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
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 <project> -executeMethod Aetheria.EditorTools.BeltFloaterLayerVerify.Run -logFile <log>
|
||||
//
|
||||
// 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<int, float> 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<GameSettings>("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<Mesh>().First(m => m.name == p[1]),
|
||||
Material = p[2] == "" ? null : AssetDatabase.LoadAssetAtPath<Material>(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<OrbitData>(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<OrbitData>(beltOrbitA), Asteroids = Rocks(24, i => (i * 7 % 24) / 23f, 40f) }).Key;
|
||||
var beltB = cache.Upsert(new AsteroidBeltData { Orbit = new CultRecordRef<OrbitData>(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<OrbitData>(orbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Belt Sun", Orbit = new CultRecordRef<OrbitData>(sunOrbit), Mass = 400f }).Key));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Belt Planet", Orbit = new CultRecordRef<OrbitData>(planetOrbit), Mass = 400f }).Key));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(beltA));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(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<ZoneRenderer>();
|
||||
renderer.Settings = game;
|
||||
renderer.AsteroidMeshes = meshes;
|
||||
renderer.ItemManager = items;
|
||||
renderer.LODHandler = new GameObject("Belt LOD").AddComponent<LODHandler>();
|
||||
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>();
|
||||
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<Mesh, Vector3[]>();
|
||||
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<ZoneRenderer>(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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7d25a93db1972324fa5460b2415c1e4a
|
||||
|
|
@ -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(1u, cache, ledger, settings, _ => { });
|
||||
var items = new ItemManager(cache, ledger, settings, _ => { });
|
||||
var zone = new Zone(items, new PlanetSettings(), new ZonePack(), new GalaxyZone { Name = "Smoke", Owner = null }, null);
|
||||
var hullRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<HullData>("Smoke Hull"));
|
||||
var ship = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000, Lot = 1 }, new EntitySettings());
|
||||
|
|
@ -85,11 +85,6 @@ 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<ZoneRenderer>();
|
||||
mapRenderer.Settings = AssetDatabase.LoadAssetAtPath<GameSettings>("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);
|
||||
|
|
@ -103,11 +98,7 @@ public static class CapabilityPresentationSmoke
|
|||
var tractor = ((GameObject)PrefabUtility.InstantiatePrefab(beamPrefab, go.transform)).GetComponent<TractorBeam>();
|
||||
tractor.Power = .8f;
|
||||
tractor.Direction = Vector3.forward;
|
||||
var icon = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
icon.transform.SetParent(go.transform);
|
||||
bound.MapIcon = icon.GetComponent<MeshRenderer>();
|
||||
bound.MapIcon.sharedMaterial = new Material(Shader.Find("Unlit/Color"));
|
||||
bound.SetEntity(mapRenderer, entity);
|
||||
bound.SetEntity(null, entity);
|
||||
return (go, bound, tractor);
|
||||
}
|
||||
var (instanceObject, instance, beam) = Bound("Smoke Entity Instance", ship);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -88,13 +87,14 @@ public sealed class InspectableTemperatureDrawer : ICultInspectorDrawer
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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,13 +122,18 @@ public sealed class InspectableSchematicShapeDrawer : ICultInspectorDrawer
|
|||
{
|
||||
using (var grid = new EditorGUILayout.VerticalScope())
|
||||
{
|
||||
HullUnderlay.Draw(grid.rect, item, inspector.Records);
|
||||
if (schematic != null && Event.current.type == EventType.Repaint)
|
||||
{
|
||||
_schematicMaterial ??= new Material(Shader.Find("Legacy Shaders/Particles/Additive"));
|
||||
EditorGUI.DrawPreviewTexture(grid.rect, schematic, _schematicMaterial);
|
||||
}
|
||||
|
||||
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 => HullFootprint.Cells(hp).Contains(int2(x, y)));
|
||||
var hardpoint = hull?.Hardpoints.FirstOrDefault(hp => hp.Shape.Coordinates
|
||||
.Any(v => v.x + hp.Position.x == x && v.y + hp.Position.y == 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));
|
||||
|
|
@ -140,93 +145,3 @@ 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<CultStoredDocument> records)
|
||||
{
|
||||
if (item == null || Event.current.type != EventType.Repaint) return;
|
||||
var texture = string.IsNullOrEmpty(item.Schematic) ? null : AssetDatabase.LoadAssetAtPath<Texture2D>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,232 +0,0 @@
|
|||
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 <project> -executeMethod Aetheria.EditorTools.DrawnPoseWiringVerify.Run -logFile <log>
|
||||
//
|
||||
// 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<GameSettings>("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<GameObject>(wired["SimpleCommodityPickup"])?.GetComponent<ItemPickup>();
|
||||
var minePrefab = AssetDatabase.LoadAssetAtPath<GameObject>(wired["MinePrefab"])?.GetComponent<MineInstance>();
|
||||
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<OrbitData>(sunOrbit), Distance = 40f, Phase = 0f }).Key;
|
||||
var pack = new ZonePack { Radius = 5000f, Mass = 10000f };
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(sunOrbit));
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(planetOrbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Wiring Sun", Orbit = new CultRecordRef<OrbitData>(sunOrbit), Mass = 400f }).Key));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Wiring Planet", Orbit = new CultRecordRef<OrbitData>(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<ItemData>(items.ItemData.GetByName<SimpleCommodityData>("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>();
|
||||
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<ZoneRenderer>(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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c2270cf68a4af384eb715c8b5a55f5e8
|
||||
|
|
@ -24,8 +24,7 @@ public static class FloatingItemsDrawnSmoke
|
|||
private const string Flag = "FloatingItemsDrawnSmoke.running";
|
||||
private const string Wiring = "FloatingItemsDrawnSmoke.wiring";
|
||||
private static IEnumerator _check;
|
||||
private const double Budget = 300; // real seconds: batch frames can be far shorter than a game second
|
||||
private static double _started;
|
||||
private static int _frames;
|
||||
private static string _work;
|
||||
|
||||
static FloatingItemsDrawnSmoke()
|
||||
|
|
@ -53,7 +52,6 @@ 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;
|
||||
}
|
||||
|
|
@ -62,7 +60,7 @@ public static class FloatingItemsDrawnSmoke
|
|||
{
|
||||
try
|
||||
{
|
||||
if (Time.realtimeSinceStartupAsDouble - _started > Budget) throw new InvalidOperationException($"The check did not finish in {Budget} seconds.");
|
||||
if (++_frames > 3000) throw new InvalidOperationException("The check did not finish in 3000 frames.");
|
||||
if (_check.MoveNext()) return;
|
||||
Finish(0);
|
||||
}
|
||||
|
|
@ -90,8 +88,6 @@ public static class FloatingItemsDrawnSmoke
|
|||
{
|
||||
var settings = AssetDatabase.LoadAssetAtPath<GameSettings>("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.
|
||||
|
|
@ -124,7 +120,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(1u, cache, ledger, settings, _ => { });
|
||||
var items = new ItemManager(cache, ledger, settings, _ => { });
|
||||
var zone = new Zone(items, new PlanetSettings(), new ZonePack(), null, null);
|
||||
EquippableItem Make(string name, int lot) => new EquippableItem
|
||||
{
|
||||
|
|
@ -149,7 +145,6 @@ 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<Camera>();
|
||||
var slimeObject = new GameObject("Smoke Slime");
|
||||
slimeObject.SetActive(false); // the real Slime needs its compute shader; LoadZone only sets its radius
|
||||
|
|
@ -160,9 +155,8 @@ public static class FloatingItemsDrawnSmoke
|
|||
ItemPickup[] Pickups() => rootObject.GetComponentsInChildren<ItemPickup>(true).Where(p => p != null).ToArray();
|
||||
void At(ItemPickup pickup, FloatingItem floating, string what)
|
||||
{
|
||||
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);
|
||||
var at = floating.Body.Position;
|
||||
var want = new Vector3(at.x, zone.GetHeight(at), at.y);
|
||||
Require(Vector3.Distance(pickup.transform.position, want) < 1e-3f,
|
||||
$"{what}: the pickup is at {pickup.transform.position}, the body at {want}.");
|
||||
}
|
||||
|
|
@ -191,13 +185,10 @@ public static class FloatingItemsDrawnSmoke
|
|||
var earlyPickup = Pickups().Single(p => p.Item == early.Item);
|
||||
earlyPickup.LabelPersistDuration = 1;
|
||||
earlyPickup.LabelFadeDuration = .2f;
|
||||
// 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;
|
||||
for (var i = 0; i < 1200 && earlyPickup.ScanLabel.color.a > .05f; i++) 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.");
|
||||
labelUntil = Time.time + earlyPickup.LabelPersistDuration + earlyPickup.LabelFadeDuration + 1f;
|
||||
while (Time.time < labelUntil && earlyPickup.ScanLabel.color.a < .95f) yield return null;
|
||||
for (var i = 0; i < 1200 && earlyPickup.ScanLabel.color.a < .95f; i++) 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
|
||||
|
|
|
|||
|
|
@ -1,302 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using GameCult.Caching;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Aetheria.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Batchmode check for flow-shader-parity (campaign aetheria-fauna, cut fauna-flow-field): the HLSL globalFlow and
|
||||
/// slopeFlow of Assets/Shaders/Volumetric.cginc give the values of FlowField (ServerShared) for the same position,
|
||||
/// scroll, settings and gradient. It drives the real lowering: a Zone is stepped, VolumeSampling.LowerFlow sets the
|
||||
/// shader globals from the zone's Settings.Flow and FlowScroll, FlowParity.compute reads those globals, and the
|
||||
/// results are compared with FlowField. It also checks that Settings.asset carries the authored flow under
|
||||
/// PlanetSettings and that the shader got the zone's scroll (speed times zone time).
|
||||
///
|
||||
/// Dispatches a compute kernel, so it needs a graphics device: run WITHOUT -nographics (as ShieldPanelCut3Verify).
|
||||
/// Unity -batchmode -projectPath <repo> -executeMethod Aetheria.EditorTools.FlowFieldParityVerify.Run -logFile <path>
|
||||
/// Do not pass -quit: Run exits the Editor itself, 0 on success and 1 on any failure.
|
||||
/// </summary>
|
||||
public static class FlowFieldParityVerify
|
||||
{
|
||||
const string Tag = "[FlowFieldParityVerify]";
|
||||
// Measured disagreement between the HLSL and the C# on this tree (float32 on both sides, positions to 10000,
|
||||
// scrolls to 100, through the keyword-gated flow()): max 2.9e-5 of amplitude (the "max error" line this run
|
||||
// prints). The tolerance is 5x that noise, so constant errors of 0.05% (y factor .501, swirl or amplitude
|
||||
// x1.0005: 4e-4 to 8e-4) fail; a 0.01% error is at the noise floor and unresolvable.
|
||||
const float RelativeTolerance = 1.5e-4f;
|
||||
static float _maxError;
|
||||
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 == UnityEngine.Rendering.GraphicsDeviceType.Null)
|
||||
{
|
||||
Debug.LogError($"{Tag} FAIL: no graphics device in this session; rerun batchmode without -nographics.");
|
||||
EditorApplication.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// The flow's error lines (a shader keyword that does not exist, a missing property) fail the check.
|
||||
Application.logMessageReceived += OnLog;
|
||||
var work = Path.Combine(Path.GetTempPath(), "flow-parity-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(work);
|
||||
try
|
||||
{
|
||||
var settings = AssetDatabase.LoadAssetAtPath<GameSettings>("Assets/Resources/Settings.asset")
|
||||
?? throw new InvalidOperationException("No Settings.asset.");
|
||||
var compute = AssetDatabase.LoadAssetAtPath<ComputeShader>("Assets/Shaders/Compute/FlowParity.compute")
|
||||
?? throw new InvalidOperationException("No FlowParity.compute.");
|
||||
|
||||
CheckAuthoredFlow(settings.PlanetSettings.Flow);
|
||||
|
||||
// The authored flow (global term only: its slope amplitudes are authored zero), then a flow with every
|
||||
// term live and a scroll speed that reaches scrolls up to 100.
|
||||
var authored = settings.PlanetSettings.Flow;
|
||||
CheckParity("authored", settings, work, compute, authored, new[] { 0.0, 3.1, 40.0, 100.0 });
|
||||
var wide = new FlowSettings
|
||||
{
|
||||
GlobalScale = authored.GlobalScale, GlobalAmplitude = authored.GlobalAmplitude, Period = authored.Period,
|
||||
GlobalScrollSpeed = 1f, SlopeAmplitude = 3f, SwirlAmplitude = 5f
|
||||
};
|
||||
CheckParity("wide", settings, work, compute, wide, new[] { 0.0, 1.0, 17.0, 60.0, 100.0 });
|
||||
CheckNoiseClock(compute);
|
||||
|
||||
Debug.Log($"{Tag} max error {_maxError:G4} of amplitude (tolerance {RelativeTolerance:G4})");
|
||||
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);
|
||||
}
|
||||
|
||||
// Settings.asset carries the authored flow under PlanetSettings (it moved from DefaultEnvironment unchanged).
|
||||
static void CheckAuthoredFlow(FlowSettings flow)
|
||||
{
|
||||
if (flow == null) { Fail("authored-flow", "PlanetSettings.Flow is null"); return; }
|
||||
if (flow.GlobalScale != 512f || flow.GlobalAmplitude != 15f || flow.GlobalScrollSpeed != .025f ||
|
||||
flow.Period != 8f || flow.SlopeAmplitude != 0f || flow.SwirlAmplitude != 0f)
|
||||
Fail("authored-flow", $"PlanetSettings.Flow is {flow.GlobalScale}/{flow.GlobalAmplitude}/{flow.GlobalScrollSpeed}/" +
|
||||
$"{flow.Period}/{flow.SlopeAmplitude}/{flow.SwirlAmplitude}, authored 512/15/0.025/8/0/0");
|
||||
}
|
||||
|
||||
internal static Zone BuildZone(GameSettings settings, string work, FlowSettings flow, double time = 0)
|
||||
{
|
||||
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(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);
|
||||
}
|
||||
|
||||
static void CheckParity(string label, GameSettings settings, string work, ComputeShader compute, FlowSettings flow,
|
||||
double[] zoneTimes)
|
||||
{
|
||||
var zone = BuildZone(settings, work, flow);
|
||||
var kernel = compute.FindKernel("Evaluate");
|
||||
var rng = new System.Random(label.Length * 7919);
|
||||
const int positionCount = 256, gradientCount = 64;
|
||||
const int count = positionCount;
|
||||
|
||||
var positions = new Vector3[count];
|
||||
for (var i = 0; i < positionCount; i++)
|
||||
positions[i] = new Vector3((float) (rng.NextDouble() * 20000 - 10000), (float) (rng.NextDouble() * 200 - 100),
|
||||
(float) (rng.NextDouble() * 20000 - 10000));
|
||||
// Gradients of any direction and magnitude, plus one zero gradient.
|
||||
var gradients = new Vector2[count];
|
||||
for (var i = 1; i < gradientCount; i++)
|
||||
{
|
||||
var angle = rng.NextDouble() * Math.PI * 2;
|
||||
var magnitude = (float) Math.Exp(rng.NextDouble() * 8 - 4);
|
||||
gradients[i] = new Vector2((float) Math.Cos(angle) * magnitude, (float) Math.Sin(angle) * magnitude);
|
||||
}
|
||||
gradients[0] = Vector2.zero;
|
||||
|
||||
var positionBuffer = new ComputeBuffer(count, 12);
|
||||
var gradientBuffer = new ComputeBuffer(count, 8);
|
||||
var globalBuffer = new ComputeBuffer(count, 12);
|
||||
var slopeBuffer = new ComputeBuffer(count, 12);
|
||||
var gatedBuffer = new ComputeBuffer(count, 12);
|
||||
ComputeBuffer noiseBuffer = null;
|
||||
// flow() reads the nebula's gravity height under FLOW_SLOPE. A ramp rising along the texture's x gives every
|
||||
// position inside the grid the gradient direction (1, 0), so the slope term is live where a flat height
|
||||
// would zero it (and the FLOW_SLOPE keyword and its pragma would not matter).
|
||||
const int rampSize = 64;
|
||||
var height = new Texture2D(rampSize, rampSize, TextureFormat.RFloat, false)
|
||||
{ filterMode = FilterMode.Point, wrapMode = TextureWrapMode.Clamp };
|
||||
var ramp = new float[rampSize * rampSize];
|
||||
for (var i = 0; i < ramp.Length; i++) ramp[i] = (i % rampSize) / (float) (rampSize - 1);
|
||||
height.SetPixelData(ramp, 0);
|
||||
height.Apply();
|
||||
var rampGradient = new CultMath.float2(1f, 0f);
|
||||
try
|
||||
{
|
||||
positionBuffer.SetData(positions);
|
||||
gradientBuffer.SetData(gradients);
|
||||
compute.SetBuffer(kernel, "positions", positionBuffer);
|
||||
compute.SetBuffer(kernel, "gradients", gradientBuffer);
|
||||
compute.SetBuffer(kernel, "globalOut", globalBuffer);
|
||||
compute.SetBuffer(kernel, "slopeOut", slopeBuffer);
|
||||
compute.SetBuffer(kernel, "gatedOut", gatedBuffer);
|
||||
compute.SetTexture(kernel, "_NebulaSurfaceHeight", height);
|
||||
compute.SetVector("_NebulaSurfaceHeight_TexelSize", new Vector4(1f / rampSize, 1f / rampSize, rampSize, rampSize));
|
||||
// The grid spans 40000: every sampled position (|xz| to 10000) is inside the ramp's uv range.
|
||||
Shader.SetGlobalVector("_GridTransform", new Vector4(0f, 0f, 40000f, 0f));
|
||||
noiseBuffer = new ComputeBuffer(count, 4);
|
||||
compute.SetBuffer(kernel, "noiseOut", noiseBuffer);
|
||||
compute.SetInt("count", count);
|
||||
|
||||
var elapsed = 0.0;
|
||||
foreach (var target in zoneTimes)
|
||||
{
|
||||
// Step the zone to the target by an awkward step; the sum is the zone's time.
|
||||
while (elapsed < target - 1e-9)
|
||||
{
|
||||
var dt = (float) Math.Min(0.37, target - elapsed);
|
||||
zone.Update(dt);
|
||||
elapsed += dt;
|
||||
}
|
||||
VolumeSampling.LowerFlow(zone.Settings.Flow, zone.ExactTime);
|
||||
|
||||
// The shader got the zone's scroll and cycle: the same zone time Zone.Flow evaluates at.
|
||||
var zoneScroll = zone.FlowScroll;
|
||||
var lowered = Shader.GetGlobalVector("_FlowScroll");
|
||||
if (Math.Abs(lowered.x - zoneScroll.x) > 1e-6 || Math.Abs(lowered.y - zoneScroll.y) > 1e-6)
|
||||
Fail($"{label}-lowered-scroll", $"at zone time {elapsed:F3} the shader got _FlowScroll ({lowered.x}, {lowered.y}), the zone's is ({zoneScroll.x}, {zoneScroll.y})");
|
||||
if (Math.Abs(Shader.GetGlobalFloat("_FlowCycle") - zone.CyclePhase(flow.Period)) > 1e-6)
|
||||
Fail($"{label}-lowered-cycle", $"at zone time {elapsed:F3} the shader got _FlowCycle {Shader.GetGlobalFloat("_FlowCycle")}, the zone's phase is {zone.CyclePhase(flow.Period)}");
|
||||
if (Shader.GetGlobalFloat("_FlowScale") != flow.GlobalScale || Shader.GetGlobalFloat("_FlowAmplitude") != flow.GlobalAmplitude ||
|
||||
Shader.GetGlobalFloat("_FlowSlopeAmplitude") != flow.SlopeAmplitude ||
|
||||
Shader.GetGlobalFloat("_FlowSwirlAmplitude") != flow.SwirlAmplitude || Shader.GetGlobalFloat("_FlowPeriod") != flow.Period)
|
||||
Fail($"{label}-lowered-settings", "a flow uniform does not equal the zone's FlowSettings");
|
||||
|
||||
compute.Dispatch(kernel, (count + 63) / 64, 1, 1);
|
||||
var globalOut = new Vector3[count];
|
||||
var slopeOut = new Vector3[count];
|
||||
var gatedOut = new Vector3[count];
|
||||
globalBuffer.GetData(globalOut);
|
||||
slopeBuffer.GetData(slopeOut);
|
||||
gatedBuffer.GetData(gatedOut);
|
||||
|
||||
var at = $"{label} zone time {elapsed:F2} scroll {zoneScroll.x:F4}";
|
||||
for (var i = 0; i < positionCount; i++)
|
||||
{
|
||||
var p = positions[i];
|
||||
var expected = FlowField.Global(flow, new CultMath.float3(p.x, p.y, p.z), zoneScroll);
|
||||
Compare($"{label}-global", at, i, new Vector3(expected.x, expected.y, expected.z), globalOut[i], flow.GlobalAmplitude);
|
||||
}
|
||||
// flow() is the keyword-gated function the nebula and stardust call: the global term plus, under
|
||||
// FLOW_SLOPE, the slope term of the ramp's gradient. A flow that is live must not read zero.
|
||||
var live = 0;
|
||||
var gatedScale = Math.Max(Math.Abs(flow.GlobalAmplitude), Math.Max(Math.Abs(flow.SlopeAmplitude), Math.Abs(flow.SwirlAmplitude)));
|
||||
for (var i = 0; i < positionCount; i++)
|
||||
{
|
||||
var p = positions[i];
|
||||
var expected = FlowField.At(flow, new CultMath.float3(p.x, p.y, p.z), zoneScroll, rampGradient);
|
||||
var e = new Vector3(expected.x, expected.y, expected.z);
|
||||
if (e.magnitude > .1f * gatedScale) live++;
|
||||
Compare($"{label}-flow-keywords", at, i, e, gatedOut[i], gatedScale);
|
||||
}
|
||||
if (gatedScale != 0f && live < positionCount / 4)
|
||||
Fail($"{label}-flow-keywords", $"{at}: only {live} of {positionCount} zone flows are live; the check is vacuous");
|
||||
var slopeScale = Math.Max(Math.Abs(flow.SlopeAmplitude), Math.Abs(flow.SwirlAmplitude));
|
||||
for (var i = 0; i < gradientCount; i++)
|
||||
{
|
||||
var g = gradients[i];
|
||||
var expected = FlowField.Slope(flow, new CultMath.float2(g.x, g.y));
|
||||
Compare($"{label}-slope", at, i, new Vector3(expected.x, expected.y, expected.z), slopeOut[i], slopeScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
positionBuffer.Release(); gradientBuffer.Release(); globalBuffer.Release(); slopeBuffer.Release(); gatedBuffer.Release(); noiseBuffer?.Release(); UnityEngine.Object.DestroyImmediate(height);
|
||||
}
|
||||
}
|
||||
|
||||
// The nebula noise the flow advects evolves on the flow clock VolumeSampling lowers as _NebulaNoiseTime, never on
|
||||
// _Time: with the speed non-zero, the noise changes when _NebulaNoiseTime does and not otherwise (a frame clock
|
||||
// would not follow the global set here).
|
||||
static void CheckNoiseClock(ComputeShader compute)
|
||||
{
|
||||
const int count = 64;
|
||||
var kernel = compute.FindKernel("Evaluate");
|
||||
var rng = new System.Random(4217);
|
||||
var positions = new Vector3[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
positions[i] = new Vector3((float) (rng.NextDouble() * 4000 - 2000), (float) (rng.NextDouble() * 100 - 50), (float) (rng.NextDouble() * 4000 - 2000));
|
||||
var buffers = new[] { new ComputeBuffer(count, 12), new ComputeBuffer(count, 8), new ComputeBuffer(count, 12), new ComputeBuffer(count, 12), new ComputeBuffer(count, 12), new ComputeBuffer(count, 4) };
|
||||
var height = new Texture2D(1, 1, TextureFormat.RFloat, false);
|
||||
try
|
||||
{
|
||||
buffers[0].SetData(positions);
|
||||
buffers[1].SetData(new Vector2[count]);
|
||||
compute.SetBuffer(kernel, "positions", buffers[0]);
|
||||
compute.SetBuffer(kernel, "gradients", buffers[1]);
|
||||
compute.SetBuffer(kernel, "globalOut", buffers[2]);
|
||||
compute.SetBuffer(kernel, "slopeOut", buffers[3]);
|
||||
compute.SetBuffer(kernel, "gatedOut", buffers[4]);
|
||||
compute.SetBuffer(kernel, "noiseOut", buffers[5]);
|
||||
compute.SetTexture(kernel, "_NebulaSurfaceHeight", height);
|
||||
compute.SetInt("count", count);
|
||||
Shader.SetGlobalFloat("_NebulaNoiseSpeed", 1f);
|
||||
float[] Noise(float noiseTime)
|
||||
{
|
||||
Shader.SetGlobalFloat("_NebulaNoiseTime", noiseTime);
|
||||
compute.Dispatch(kernel, 1, 1, 1);
|
||||
var result = new float[count];
|
||||
buffers[5].GetData(result);
|
||||
return result;
|
||||
}
|
||||
var a = Noise(0f);
|
||||
var b = Noise(3f);
|
||||
var again = Noise(3f);
|
||||
int moved = 0, repeated = 0;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (Math.Abs(a[i] - b[i]) > 1e-4f) moved++;
|
||||
if (b[i] == again[i]) repeated++;
|
||||
}
|
||||
if (moved < count / 2) Fail("nebula-noise-clock", $"only {moved} of {count} noise samples follow _NebulaNoiseTime");
|
||||
if (repeated != count) Fail("nebula-noise-clock", $"{count - repeated} of {count} noise samples changed with _NebulaNoiseTime held");
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var buffer in buffers) buffer.Release();
|
||||
UnityEngine.Object.DestroyImmediate(height);
|
||||
}
|
||||
}
|
||||
|
||||
// Error is measured against the term's amplitude; a term with no amplitude must be exactly zero and never NaN.
|
||||
static void Compare(string check, string at, int index, Vector3 expected, Vector3 actual, float amplitude)
|
||||
{
|
||||
var error = (expected - actual).magnitude;
|
||||
if (float.IsNaN(error)) { Fail(check, $"{at} sample {index}: NaN (expected {expected}, shader {actual})"); return; }
|
||||
if (amplitude != 0f) _maxError = Math.Max(_maxError, error / Math.Abs(amplitude));
|
||||
var tolerance = RelativeTolerance * Math.Abs(amplitude);
|
||||
if (error > tolerance)
|
||||
Fail(check, $"{at} sample {index}: expected {expected}, shader {actual}, error {error:G4} > {tolerance:G4}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8fae18f5f32f10f43ba892a097f18caa
|
||||
|
|
@ -1,392 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Aetheria.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Batchmode check for the flow as the scene runs it (campaign aetheria-fauna, cut fauna-flow-field; target
|
||||
/// invariants flow-one-owner and flow-shader-parity). FlowFieldParityVerify proves the HLSL against FlowField with
|
||||
/// no scene loaded; this opens ARPG.unity, lets VolumeSampling and Stardust initialise as the game does, drives
|
||||
/// their own Start and Update with a zone attached, and asserts what the presenters did:
|
||||
/// - nothing threw and nothing logged an error (a type that cannot initialise in a scene, as VolumeSampling once
|
||||
/// could when it created its keywords in a static field initializer, throws on its first use);
|
||||
/// - FLOW_GLOBAL and FLOW_SLOPE are enabled, _FlowAmplitude is the zone's, and the nebula noise clock is the zone's;
|
||||
/// - the stardust particle buffer changes with the flow and freezes when the zone pauses;
|
||||
/// - CloudShader pass 0 carries the FLOW_* keywords.
|
||||
/// The scene is opened before any Aetheria type is touched: run this method alone in its own Unity session, never
|
||||
/// after FlowFieldParityVerify (which would initialise VolumeSampling outside a MonoBehaviour constructor).
|
||||
/// Dispatches compute and renders, so it needs a graphics device: run WITHOUT -nographics.
|
||||
/// Unity -batchmode -projectPath <repo> -executeMethod Aetheria.EditorTools.FlowFieldSceneVerify.Run -logFile <path>
|
||||
/// Do not pass -quit: Run exits the Editor itself, 0 on success and 1 on any failure.
|
||||
/// </summary>
|
||||
public static class FlowFieldSceneVerify
|
||||
{
|
||||
const string Tag = "[FlowFieldSceneVerify]";
|
||||
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;
|
||||
try
|
||||
{
|
||||
// Before anything else touches the project's types: the scene constructs its MonoBehaviours here.
|
||||
EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Single);
|
||||
Check();
|
||||
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;
|
||||
}
|
||||
EditorApplication.Exit(_ok ? 0 : 1);
|
||||
}
|
||||
|
||||
static void Check()
|
||||
{
|
||||
var settings = AssetDatabase.LoadAssetAtPath<GameSettings>("Assets/Resources/Settings.asset")
|
||||
?? throw new InvalidOperationException("No Settings.asset.");
|
||||
var agm = UnityEngine.Object.FindObjectsByType<ActionGameManager>(FindObjectsInactive.Include, FindObjectsSortMode.None).FirstOrDefault();
|
||||
var volume = UnityEngine.Object.FindObjectsByType<VolumeSampling>(FindObjectsInactive.Include, FindObjectsSortMode.None).FirstOrDefault();
|
||||
var dust = UnityEngine.Object.FindObjectsByType<Stardust>(FindObjectsInactive.Include, FindObjectsSortMode.None).FirstOrDefault();
|
||||
if (agm == null || volume == null || dust == null)
|
||||
{
|
||||
Fail("scene-objects", $"ARPG.unity has ActionGameManager={agm != null}, VolumeSampling={volume != null}, Stardust={dust != null}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Every flow term live, so FLOW_SLOPE is on; and the same flow with every amplitude zero.
|
||||
var on = new FlowSettings
|
||||
{
|
||||
GlobalScale = 512f, GlobalAmplitude = 15f, GlobalScrollSpeed = .025f, Period = 8f, SlopeAmplitude = 3f, SwirlAmplitude = 5f
|
||||
};
|
||||
var off = new FlowSettings { GlobalScale = on.GlobalScale, GlobalScrollSpeed = on.GlobalScrollSpeed, Period = on.Period };
|
||||
var work = Path.Combine(Path.GetTempPath(), "flow-scene-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(work);
|
||||
try
|
||||
{
|
||||
var zone = FlowFieldParityVerify.BuildZone(settings, work, on, 86400.0 * 3 + 12.3);
|
||||
typeof(ActionGameManager).GetProperty("Instance").GetSetMethod(true).Invoke(null, new object[] { agm });
|
||||
typeof(ActionGameManager).GetProperty("Zone").GetSetMethod(true).Invoke(agm, new object[] { zone });
|
||||
if (agm.Settings == null) agm.Settings = settings;
|
||||
|
||||
Call(volume, "Start");
|
||||
Call(volume, "Update");
|
||||
if (!Shader.IsKeywordEnabled("FLOW_GLOBAL")) Fail("scene-keywords", "FLOW_GLOBAL is not enabled after VolumeSampling.Update");
|
||||
if (!Shader.IsKeywordEnabled("FLOW_SLOPE")) Fail("scene-keywords", "FLOW_SLOPE is not enabled after VolumeSampling.Update");
|
||||
if (Shader.GetGlobalFloat("_FlowAmplitude") != on.GlobalAmplitude)
|
||||
Fail("scene-amplitude", $"_FlowAmplitude is {Shader.GetGlobalFloat("_FlowAmplitude")}, the zone's flow has {on.GlobalAmplitude}");
|
||||
var scroll = Shader.GetGlobalVector("_FlowScroll");
|
||||
if (scroll.x != zone.FlowScroll.x || scroll.y != zone.FlowScroll.y)
|
||||
Fail("scene-scroll", $"_FlowScroll ({scroll.x}, {scroll.y}) is not the zone's ({zone.FlowScroll.x}, {zone.FlowScroll.y})");
|
||||
var noiseTime = Shader.GetGlobalFloat("_NebulaNoiseTime");
|
||||
if (noiseTime != (float) zone.ExactTime)
|
||||
Fail("scene-noise-clock", $"_NebulaNoiseTime is {noiseTime}, the zone's time is {(float) zone.ExactTime}");
|
||||
|
||||
CheckCloudKeywords();
|
||||
|
||||
// Stardust follows the flow through its own dispatch: the particle buffer read back each frame.
|
||||
Call(dust, "Start");
|
||||
var bufferField = typeof(Stardust).GetField("_particlesBuffer", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
float[] Frame()
|
||||
{
|
||||
Call(dust, "Update");
|
||||
var buffer = (ComputeBuffer) bufferField.GetValue(dust);
|
||||
var data = new float[buffer.count * 7];
|
||||
buffer.GetData(data);
|
||||
return data;
|
||||
}
|
||||
var flowing = Frame();
|
||||
VolumeSampling.LowerFlow(off, VolumeSampling.FlowClock);
|
||||
var still = Frame();
|
||||
if (Differing(flowing, still) == 0) Fail("scene-stardust", "the particle buffer is the same with the zone's flow and with no flow");
|
||||
|
||||
// A paused zone freezes the particles and the nebula noise clock; a running one moves both.
|
||||
Call(volume, "Update");
|
||||
zone.Update(0f);
|
||||
Call(volume, "Update");
|
||||
if (Differing(flowing, Frame()) != 0) Fail("scene-stardust-pause", "the particle buffer moved while the zone was paused");
|
||||
if (Shader.GetGlobalFloat("_NebulaNoiseTime") != noiseTime) Fail("scene-noise-clock", "_NebulaNoiseTime moved while the zone was paused");
|
||||
zone.Update(.05f);
|
||||
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
|
||||
{
|
||||
try { Directory.Delete(work, true); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 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<CameraEvent>().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<MonoBehaviour>().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<VolumeCloudRenderer>();
|
||||
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<MonoBehaviour>().Where(behaviour => behaviour.enabled).ToArray();
|
||||
var fields = new System.Collections.Generic.List<Texture2D>();
|
||||
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<Collider>());
|
||||
|
||||
// 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<Texture2D> 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()
|
||||
{
|
||||
var shader = AssetDatabase.LoadAssetAtPath<Shader>("Assets/Shaders/Raymarching/CloudShader.shader");
|
||||
if (shader == null) { Fail("scene-cloud-keywords", "No CloudShader.shader."); return; }
|
||||
var keywords = ShaderUtil.GetPassKeywords(shader, new PassIdentifier(0, 0), UnityEditor.Rendering.ShaderType.Fragment)
|
||||
.Select(keyword => keyword.name).ToArray();
|
||||
foreach (var name in new[] { "FLOW_GLOBAL", "FLOW_SLOPE" })
|
||||
if (!keywords.Contains(name)) Fail("scene-cloud-keywords", $"CloudShader pass 0 does not carry {name}");
|
||||
}
|
||||
|
||||
static int Differing(float[] a, float[] b)
|
||||
{
|
||||
var count = 0;
|
||||
for (var i = 0; i < a.Length; i++)
|
||||
if (Math.Abs(a[i] - b[i]) > 1e-4f) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
// The presenters' own Start and Update, as the game calls them; an exception is a failure that names the type.
|
||||
static void Call(object target, string method)
|
||||
{
|
||||
var info = target.GetType().GetMethod(method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
if (info == null) { Fail("scene-presenter-exception", $"{target.GetType().Name}.{method} not found"); return; }
|
||||
try { info.Invoke(target, null); }
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
Fail("scene-presenter-exception", $"{target.GetType().Name}.{method} threw {e.InnerException?.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 25fbd79fca1de49498c3bebf9f63243b
|
||||
|
|
@ -1,428 +0,0 @@
|
|||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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<GameSettings>("Assets/Resources/Settings.asset")
|
||||
?? throw new InvalidOperationException("No Settings.asset.");
|
||||
var material = AssetDatabase.LoadAssetAtPath<Material>("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<Vector2> { new Vector2(-130f, 85f), new Vector2(610f, -240f), new Vector2(670f, -270f), new Vector2(1700f, 300f) };
|
||||
var masses = new List<float> { 14152f, 273.9f, 29.2f, 0f };
|
||||
var parents = new List<int> { -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<OrbitData>(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<OrbitData>(orbit), Mass = masses[i] }).Key;
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(orbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(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>();
|
||||
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<float>(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<ZoneRenderer>(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<Camera>(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<Camera>(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<Camera>(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<MapRenderer>(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<ZoneRenderer>(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<Renderer>(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<GameObject>(path);
|
||||
if (prefab == null) { Fail("wiring", $"no prefab at {path}"); continue; }
|
||||
foreach (var r in prefab.GetComponentsInChildren<Renderer>(true))
|
||||
if (r.gameObject.layer == GravityLowering.FieldLayer)
|
||||
Fail("wiring", $"renderer {r.name} in {path} sits on layer {GravityLowering.FieldLayer}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
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 <project> -executeMethod MapBeltPerf.Run -logFile <log>
|
||||
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<GameSettings>("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<OrbitData>(sunOrbit), Mass = 400f }).Key;
|
||||
var beltOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef<OrbitData>(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<OrbitData>(beltOrbit), Asteroids = asteroids }).Key;
|
||||
var pack = new ZonePack
|
||||
{
|
||||
Orbits = { new CultRecordRef<OrbitData>(sunOrbit), new CultRecordRef<OrbitData>(beltOrbit) },
|
||||
Planets = { new CultRecordRef<BodyData>(sunKey), new CultRecordRef<BodyData>(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<Material>("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}");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2bf3d3dcd7e3dd940add2640db93e3fe
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c3e6b631c94f2384c87cf1906907af9f
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
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 <project> -executeMethod MapPresenterCapture.Run -logFile <log>
|
||||
//
|
||||
// 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<Camera>(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<MeshFilter>().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<GameObject>("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}");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cfa012aaae63b5044bdfd1b6b44ca9da
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
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 <project> -executeMethod MapPresenterSmoke.Run -logFile <log>
|
||||
//
|
||||
// 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<MeshRenderer>();
|
||||
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<GameSettings>("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<OrbitData>(sunOrbit), Mass = 1e6f }).Key;
|
||||
var rootOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef<OrbitData>(sunOrbit), Distance = 4000f, Phase = .17f }).Key;
|
||||
var beltOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef<OrbitData>(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<OrbitData>(beltOrbit), Asteroids = asteroids }).Key;
|
||||
var pack = new ZonePack
|
||||
{
|
||||
Orbits = { new CultRecordRef<OrbitData>(sunOrbit), new CultRecordRef<OrbitData>(rootOrbit), new CultRecordRef<OrbitData>(beltOrbit) },
|
||||
Planets = { new CultRecordRef<BodyData>(sunKey), new CultRecordRef<BodyData>(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<ItemData>(items.ItemData.GetByName<HullData>("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<Material>("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<EntityInstance>();
|
||||
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>();
|
||||
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<MeshRenderer>(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.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58982a1c397cc2f4d9706fd103af6654
|
||||
93
Assets/Scripts/Editor/NameTools.cs
Normal file
93
Assets/Scripts/Editor/NameTools.cs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
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<string>();
|
||||
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<string>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ceb2bdab0804463b1998471d47e82fa
|
||||
guid: 88b5f578fe63a904b997331630e61416
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
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 <project> -executeMethod Aetheria.EditorTools.RockMeshRadius.Fill -logFile <log>
|
||||
// Unity -batchmode -nographics -projectPath <project> -executeMethod Aetheria.EditorTools.RockMeshRadius.Verify -logFile <log>
|
||||
//
|
||||
// Fill measures every AsteroidMeshes entry and saves the scene; Verify fails (exit 1) when any stored radius is not the measured one.
|
||||
// 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 <project> -executeMethod Aetheria.EditorTools.RockMeshRadius.Gate -logFile <log>
|
||||
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<ZoneRenderer>(FindObjectsInactive.Include, FindObjectsSortMode.None)
|
||||
.Single(r => r.gameObject.scene == scene);
|
||||
var meshes = renderer.AsteroidMeshes;
|
||||
if (meshes == null || meshes.Length == 0) throw new InvalidOperationException("ARPG.unity's ZoneRenderer has no AsteroidMeshes.");
|
||||
var bad = meshes.Where(m => m.Mesh == null).ToArray();
|
||||
if (bad.Length > 0) throw new InvalidOperationException($"{bad.Length} AsteroidMeshes entries have no mesh.");
|
||||
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<string> 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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ea93f3d164825c4b99a0d07f24d5cf6
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
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 <project>/Temp/scenario-picker.png at 1920x1080, or the path after -pngOut (Unity empties Temp on exit).
|
||||
//
|
||||
// Unity -batchmode -projectPath <project> -executeMethod ScenarioPickerCapture.Run [-pngOut <file>] -logFile <log>
|
||||
//
|
||||
// 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<MainMenu>(FindObjectsInactive.Include, FindObjectsSortMode.None)[0];
|
||||
menu.PanelPrototype.gameObject.SetActive(false);
|
||||
var list = menu.PanelPrototype.Instantiate<PropertiesPanel>();
|
||||
var detail = menu.PanelPrototype.Instantiate<PropertiesPanel>();
|
||||
// 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<Canvas>(true).rootCanvas;
|
||||
var target = new RenderTexture(Width, Height, 24, RenderTextureFormat.ARGB32);
|
||||
var cameraObject = new GameObject("ScenarioPickerCapture camera");
|
||||
var camera = cameraObject.AddComponent<Camera>();
|
||||
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<TMPro.TMP_Text>())
|
||||
text.ForceMeshUpdate();
|
||||
Canvas.ForceUpdateCanvases();
|
||||
list.Content.GetComponentInParent<ScrollRect>().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)");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e79f280179150e4080250c45e07cd69
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
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 <project> -executeMethod ScenarioPickerVerify.Run -logFile <log>
|
||||
//
|
||||
// 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<Scenario> Launched = new List<Scenario>();
|
||||
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<EventSystem>();
|
||||
// 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<PropertyButton>(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<Button>(false).Single();
|
||||
if (real) { list.gameObject.SetActive(true); Layout(menu, list, detail); }
|
||||
return screen;
|
||||
}
|
||||
|
||||
// A live 1920x1080 layout, as the game has one a frame after Show; edit mode has no frames, so it is forced here.
|
||||
private static void Layout(MainMenu menu, PropertiesPanel list, PropertiesPanel detail)
|
||||
{
|
||||
menu.Dialog.gameObject.SetActive(false);
|
||||
var canvas = menu.GetComponentInParent<Canvas>(true).rootCanvas;
|
||||
var camera = new GameObject("verify camera").AddComponent<Camera>();
|
||||
camera.targetTexture = new RenderTexture(1920, 1080, 24);
|
||||
canvas.renderMode = RenderMode.ScreenSpaceCamera;
|
||||
canvas.worldCamera = camera;
|
||||
canvas.planeDistance = 10;
|
||||
canvas.gameObject.SetActive(true);
|
||||
Canvas.ForceUpdateCanvases();
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform) canvas.transform);
|
||||
foreach (var text in list.GetComponentsInChildren<TMP_Text>()) text.ForceMeshUpdate();
|
||||
Canvas.ForceUpdateCanvases();
|
||||
Scroll(list).onValueChanged.Invoke(Vector2.zero);
|
||||
}
|
||||
|
||||
private static ScrollRect Scroll(PropertiesPanel list) => list.Content.GetComponentInParent<ScrollRect>();
|
||||
|
||||
// Wholly inside the viewport, in world space, and not hidden by FitViewport's CanvasGroup.
|
||||
private static bool Shown(Button row, ScrollRect scroll)
|
||||
{
|
||||
var view = new Vector3[4];
|
||||
((RectTransform) scroll.viewport).GetWorldCorners(view);
|
||||
var rect = new Vector3[4];
|
||||
((RectTransform) row.transform).GetWorldCorners(rect);
|
||||
var alpha = 1f;
|
||||
for (var t = row.transform; t != null && t != scroll.content; t = t.parent)
|
||||
if (t.TryGetComponent<CanvasGroup>(out var group)) alpha = Mathf.Min(alpha, group.alpha);
|
||||
return rect[0].y >= view[0].y - .01f && rect[1].y <= view[1].y + .01f && alpha == 1;
|
||||
}
|
||||
|
||||
private static void Move(Screen screen, MoveDirection direction) =>
|
||||
ExecuteEvents.Execute(screen.Launch.gameObject, new AxisEventData(EventSystem.current) { moveDir = direction }, ExecuteEvents.moveHandler);
|
||||
|
||||
private static void Cancel(Screen screen) =>
|
||||
ExecuteEvents.Execute(screen.Launch.gameObject, new BaseEventData(EventSystem.current), ExecuteEvents.cancelHandler);
|
||||
|
||||
private static string Selected(Screen screen) => screen.Picker.Selected.Name;
|
||||
|
||||
private static void SelectionFollowsClicksAndArrows()
|
||||
{
|
||||
var screen = Build();
|
||||
Require(screen.Rows.Length == 5, $"Expected 5 scenario rows, found {screen.Rows.Length}.");
|
||||
Require(Selected(screen) == "Alpha", "The first scenario is not selected on Show.");
|
||||
Require(EventSystem.current.currentSelectedGameObject == screen.Launch.gameObject, "Launch is not focused on Show.");
|
||||
Move(screen, MoveDirection.Down);
|
||||
Require(Selected(screen) == "Beta", "Down did not select the next row.");
|
||||
Move(screen, MoveDirection.Down);
|
||||
Require(Selected(screen) == "Gamma", "Down did not cross into the next group.");
|
||||
Move(screen, MoveDirection.Up);
|
||||
Require(Selected(screen) == "Beta", "Up did not select the previous row.");
|
||||
screen.Rows[3].onClick.Invoke();
|
||||
Require(Selected(screen) == "Delta", "Clicking a name did not select it.");
|
||||
Require(screen.Launched.Count == 0, "Clicking a name launched it.");
|
||||
Require(EventSystem.current.currentSelectedGameObject == screen.Launch.gameObject, "A click on a name took focus off Launch.");
|
||||
Require(screen.Rows.All(row => row.navigation.mode == Navigation.Mode.None) && screen.Launch.navigation.mode == Navigation.Mode.None
|
||||
&& screen.Back.navigation.mode == Navigation.Mode.None, "A picker button still takes Unity navigation.");
|
||||
var accent = screen.Rows[3].targetGraphic.color;
|
||||
Require(screen.Rows.Where((row, i) => i != 3).All(row => row.targetGraphic.color != accent), "Only the selected row is drawn in the accent.");
|
||||
Move(screen, MoveDirection.Up);
|
||||
Require(screen.Rows[2].targetGraphic.color == accent && screen.Rows[3].targetGraphic.color != accent, "The accent did not follow the selection.");
|
||||
}
|
||||
|
||||
private static void ArrowsStopAtTheEnds()
|
||||
{
|
||||
var screen = Build();
|
||||
Move(screen, MoveDirection.Up);
|
||||
Require(Selected(screen) == "Alpha", "Up wrapped off the first row.");
|
||||
for (var i = 0; i < 10; i++) Move(screen, MoveDirection.Down);
|
||||
Require(Selected(screen) == "Echo", "Down wrapped or stopped short of the last row.");
|
||||
}
|
||||
|
||||
private static void LeftAndRightAreIgnored()
|
||||
{
|
||||
var screen = Build();
|
||||
Move(screen, MoveDirection.Down);
|
||||
Move(screen, MoveDirection.Left);
|
||||
Move(screen, MoveDirection.Right);
|
||||
Require(Selected(screen) == "Beta", "Left or Right moved the selection.");
|
||||
}
|
||||
|
||||
private static void LaunchIsOnePathAndFocusClears()
|
||||
{
|
||||
var screen = Build();
|
||||
Move(screen, MoveDirection.Down);
|
||||
screen.Launch.onClick.Invoke();
|
||||
Require(screen.Launched.Count == 1 && screen.Launched[0] == screen.A[1], "Launch did not launch the selected scenario once.");
|
||||
Require(EventSystem.current.currentSelectedGameObject == null, "Focus survived Launch, so a second Enter could launch again.");
|
||||
Require(screen.Backs == 0, "Launch went back.");
|
||||
}
|
||||
|
||||
private static void CancelAndBackAreOnePath()
|
||||
{
|
||||
var screen = Build();
|
||||
Cancel(screen);
|
||||
Require(screen.Backs == 1 && screen.Launched.Count == 0, "Cancel did not go back exactly once.");
|
||||
Require(EventSystem.current.currentSelectedGameObject == null, "Focus survived Cancel.");
|
||||
screen = Build();
|
||||
screen.Back.onClick.Invoke();
|
||||
Require(screen.Backs == 1 && screen.Launched.Count == 0, "The Back button did not go back exactly once.");
|
||||
Require(EventSystem.current.currentSelectedGameObject == null, "Focus survived Back.");
|
||||
}
|
||||
|
||||
private static string DetailText(Screen screen)
|
||||
{
|
||||
screen.Detail.RefreshValues();
|
||||
return string.Join("|", screen.Detail.GetComponentsInChildren<TextMeshProUGUI>(false).Select(text => text.text));
|
||||
}
|
||||
|
||||
private static void DetailPaneFollowsSelection()
|
||||
{
|
||||
var screen = Build();
|
||||
var first = DetailText(screen);
|
||||
Require(first.Contains("brief of Alpha") && first.Contains("7") && first.Contains("on"), $"Detail does not describe Alpha: {first}");
|
||||
Require(first.Contains("modes") && first.Contains("Alpha"), "Detail heading is not the group and name.");
|
||||
Move(screen, MoveDirection.Down);
|
||||
Move(screen, MoveDirection.Down);
|
||||
var third = DetailText(screen);
|
||||
Require(third.Contains("brief of Gamma") && third.Contains("new each launch") && third.Contains("off"), $"Detail does not describe Gamma: {third}");
|
||||
Require(third.Contains("development") && !third.Contains("Alpha"), "Detail heading did not follow the selection.");
|
||||
Require(screen.Detail.GetComponentsInChildren<Button>(false).Single() == screen.Launch, "Selecting rebuilt the Launch button.");
|
||||
}
|
||||
|
||||
private static void OneGroupHasNoSectionLabel()
|
||||
{
|
||||
var many = Build();
|
||||
var one = Build(oneGroup: true);
|
||||
Require(many.List.Children == 5 + 3 + 2 + 1, $"Three groups should list 5 rows, 3 section labels, 2 spacers and Back, not {many.List.Children} children.");
|
||||
Require(one.List.Children == 2 + 1, $"One group should list its 2 rows and Back, not {one.List.Children} children.");
|
||||
}
|
||||
|
||||
private static void LaunchesOnceHoweverItIsReached()
|
||||
{
|
||||
var screen = Build();
|
||||
screen.Launch.onClick.Invoke();
|
||||
screen.Launch.onClick.Invoke();
|
||||
ExecuteEvents.Execute(screen.Launch.gameObject, new BaseEventData(EventSystem.current), ExecuteEvents.submitHandler);
|
||||
Require(screen.Launched.Count == 1, $"Launch ran {screen.Launched.Count} times after a click, a second click and Submit.");
|
||||
Require(screen.Launch.navigation.mode == Navigation.Mode.None && Scroll(screen.List).verticalScrollbar.navigation.mode == Navigation.Mode.None,
|
||||
"The list scrollbar or Launch takes Unity navigation.");
|
||||
}
|
||||
|
||||
// The real scenario groups overflow the list's viewport; every row the arrows reach must end wholly visible.
|
||||
private static void ArrowingThroughTheRealListKeepsTheSelectionVisible()
|
||||
{
|
||||
var screen = Build(real: true);
|
||||
var scroll = Scroll(screen.List);
|
||||
var content = (RectTransform) scroll.content;
|
||||
var viewport = (RectTransform) scroll.viewport;
|
||||
Require(content.rect.height > viewport.rect.height + 1, $"The real list ({content.rect.height}) does not overflow its viewport ({viewport.rect.height}); the scroll rules are unmeasured.");
|
||||
Require(Shown(screen.Rows[0], scroll), "The first row is not shown on Show.");
|
||||
for (var i = 1; i < screen.Rows.Length; i++)
|
||||
{
|
||||
Move(screen, MoveDirection.Down);
|
||||
Canvas.ForceUpdateCanvases();
|
||||
Require(Shown(screen.Rows[i], scroll), $"Down to row {i} ({Selected(screen)}) left it hidden or cut off.");
|
||||
}
|
||||
for (var i = screen.Rows.Length - 2; i >= 0; i--)
|
||||
{
|
||||
Move(screen, MoveDirection.Up);
|
||||
Canvas.ForceUpdateCanvases();
|
||||
Require(Shown(screen.Rows[i], scroll), $"Up to row {i} ({Selected(screen)}) left it hidden or cut off.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue