diff --git a/Chroma/ChromaController.cs b/Chroma/ChromaController.cs index fc3477bb..8e225ea6 100644 --- a/Chroma/ChromaController.cs +++ b/Chroma/ChromaController.cs @@ -44,6 +44,10 @@ internal static class ChromaController internal const string V2_COLLISION = "_collision"; internal const string V2_MATERIALS = "_materials"; internal const string V2_MATERIAL = "_material"; + internal const string V2_MESH = "_mesh"; + internal const string V2_VERTICES = "_vertices"; + internal const string V2_UV = "_uv"; + internal const string V2_TRIANGLES = "_triangles"; internal const string V2_ATTENUATION = "_attenuation"; internal const string V2_OFFSET = "_offset"; @@ -74,6 +78,10 @@ internal static class ChromaController internal const string COLLISION = "collision"; internal const string MATERIALS = "materials"; internal const string MATERIAL = "material"; + internal const string MESH = "mesh"; + internal const string VERTICES = "vertices"; + internal const string UV = "uv"; + internal const string TRIANGLES = "triangles"; internal const string ASSIGN_FOG_TRACK = "AssignFogTrack"; internal const string ANIMATE_COMPONENT = "AnimateComponent"; diff --git a/Chroma/EnvironmentEnhancement/GeometryFactory.cs b/Chroma/EnvironmentEnhancement/GeometryFactory.cs index c39891c7..2b96c6e7 100644 --- a/Chroma/EnvironmentEnhancement/GeometryFactory.cs +++ b/Chroma/EnvironmentEnhancement/GeometryFactory.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; using Chroma.Colorizer; using Chroma.Extras; using Chroma.HarmonyPatches.Colorizer.Initialize; using Chroma.HarmonyPatches.EnvironmentComponent; using CustomJSONData.CustomBeatmap; +using Heck; using IPA.Utilities; using JetBrains.Annotations; using UnityEngine; @@ -24,7 +26,8 @@ internal enum GeometryType Cube, Plane, Quad, - Triangle + Triangle, + Custom } internal enum ShaderType @@ -34,6 +37,44 @@ internal enum ShaderType TransparentLight } + internal struct MeshData + { + public Vector3[] Vertices; // vertex positions + public Vector2[]? Uv; // texture coordinates UV0. must be in 0-1 range + public int[]? Triangles; // must be in order of vertices and multiple of 3 + + public MeshData(CustomData customData, bool v2) + { + Vertices = customData.GetRequiredFromArrayOfFloatArray(v2 ? V2_VERTICES : VERTICES, v => new Vector3(v[0], v[1], v[2])).ToArray(); + Uv = customData.GetFromArrayOfFloatArray(v2 ? V2_UV : UV, v => new Vector2(v[0], v[1]))?.ToArray(); + Triangles = customData.Get>(v2 ? V2_TRIANGLES : TRIANGLES)?.Select(Convert.ToInt32).ToArray(); + } + + public Mesh ToMesh() + { + Mesh mesh = new() + { + vertices = Vertices, + }; + + if (Uv != null) + { + mesh.uv = Uv; + } + + if (Triangles != null) + { + mesh.triangles = Triangles; + } + + mesh.RecalculateBounds(); + mesh.RecalculateNormals(); + mesh.RecalculateTangents(); + + return mesh; + } + } + internal class GeometryFactory { private static readonly FieldAccessor.Accessor _mainEffectPostProcessEnabledAccessor = FieldAccessor.GetAccessor("_mainEffectPostProcessEnabled"); @@ -94,6 +135,7 @@ internal GameObject Create(CustomData customData) GeometryType.Plane => PrimitiveType.Plane, GeometryType.Quad => PrimitiveType.Quad, GeometryType.Triangle => PrimitiveType.Quad, + GeometryType.Custom => PrimitiveType.Quad, _ => throw new ArgumentOutOfRangeException($"Geometry type {geometryType} does not match a primitive!", nameof(geometryType)) }; @@ -109,16 +151,23 @@ internal GameObject Create(CustomData customData) // Shared material is usually better performance as far as I know meshRenderer.sharedMaterial = materialInfo.Material; - if (geometryType == GeometryType.Triangle) + Mesh? customMesh = geometryType switch { - Mesh mesh = ChromaUtils.CreateTriangleMesh(); - gameObject.GetComponent().sharedMesh = mesh; + GeometryType.Custom => new MeshData(customData.GetRequired(_v2 ? V2_MESH : MESH), _v2).ToMesh(), + GeometryType.Triangle => ChromaUtils.CreateTriangleMesh(), + _ => null + }; + + if (customMesh != null) + { + gameObject.GetComponent().sharedMesh = customMesh; + if (collision) { MeshCollider meshCollider = gameObject.GetComponent(); if (meshCollider != null) { - meshCollider.sharedMesh = mesh; + meshCollider.sharedMesh = customMesh; } } } diff --git a/Chroma/Extras/CustomDataExtensions.cs b/Chroma/Extras/CustomDataExtensions.cs new file mode 100644 index 00000000..501eb63b --- /dev/null +++ b/Chroma/Extras/CustomDataExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CustomJSONData; +using CustomJSONData.CustomBeatmap; + +namespace Heck +{ + // TODO: Move to CustomJsonData + public static class CustomDataExtensions + { + public static IEnumerable? GetFromArrayOfFloatArray(this CustomData customData, string key, Func convert) + { + return customData.Get>>(key)?.Select(o => convert(o.Select(Convert.ToSingle).ToArray())); + } + + public static IEnumerable GetRequiredFromArrayOfFloatArray(this CustomData customData, string key, Func convert) + { + return customData.GetFromArrayOfFloatArray(key, convert) ?? throw new JsonNotDefinedException(key); + } + } +}