Decompiled source of MapDestruction v1.8.1
Mods/MapDestruction.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BoneLib; using BoneLib.BoneMenu; using BoneLib.Notifications; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.AI; using Il2CppSLZ.Marrow.Audio; using Il2CppSLZ.Marrow.Combat; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSystem; using MapDestruction; using MapDestruction.Assets; using MapDestruction.Core; using MapDestruction.Destruction; using MapDestruction.Detection; using MapDestruction.Effects; using MapDestruction.Meshing; using MapDestruction.Util; using MapDestruction.Voxel; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "MapDestruction", "1.8.1", "MapDestruction", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })] [assembly: InternalsVisibleTo("Tests")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] namespace MapDestruction { public class Mod : MelonMod { public override void OnInitializeMelon() { Settings.Init(); GpuGuard.CheckPreviousCrash(); UnityApi.Init(); BundleMeshSetup.Start(); Hooking.OnLevelLoaded += delegate { DestructionManager.OnLevelLoaded(); MenuSetup.TryBuild(); }; Hooking.OnLevelUnloaded += DestructionManager.OnLevelUnloaded; Hooking.OnPostFireGun += ImpactRouter.OnGunFired; MenuSetup.TryBuild(); ((MelonBase)this).LoggerInstance.Msg($"MapDestruction 로드 완료 (품질 {Settings.Tier}, 복셀 {Settings.VoxelSize * 100f:0}cm)"); } public override void OnUpdate() { Log.Pump(); DestructionManager.Tick(); MenuSetup.Tick(); Prof.Frame(Time.unscaledDeltaTime, Time.realtimeSinceStartup, DebrisSystem.ActiveCount, Chips.Count); } } } namespace MapDestruction.Voxel { public static class DamageModel { public struct Result { public float RemovedVolume; public bool AnyErosion; public bool AnyDisp; public float MaxPotential; public HashSet<Int3> ChangedChunks; public HashSet<Int3> NeighborChunks; public bool Changed { get { if (!AnyErosion) { return AnyDisp; } return true; } } } private enum Mode { Apply, Recompute, Potential } private const float DeepCarry = 0.95f; private static float KindScale(ImpactKind k) { return k switch { ImpactKind.Explosion => 1.6f, ImpactKind.Bullet => 0.45f, _ => 1f, }; } public static float Units(in ImpactEvent e, MaterialProfile p) { return e.Energy * Settings.DamageMultiplier.Value / MathF.Max(p.Toughness, 1f); } public static float CraterRadius(in ImpactEvent e, MaterialProfile p) { float x = MathF.Max(Units(in e, p), 0f); return MathF.Min(e.ContactRadius + p.CraterScale * KindScale(e.Kind) * MathF.Cbrt(x), 1.5f); } public static float InfluenceRadius(in ImpactEvent e, MaterialProfile p) { return CraterRadius(in e, p) * ((p.Ductility > 0.01f) ? 1.6f : MathF.Max(1.35f, p.Cone * 1.08f)); } private static float MaxErosionFor(VoxelChunk ch, MaterialProfile p) { float num = p.MaxErosion; if (!Settings.AllowHoles.Value) { num = MathF.Min(num, 0.06f); } if (ch.Capability == 1) { num = MathF.Min(num, 0.02f); } if (ch.Capability <= 0) { num = 0f; } return num; } private static float MaxDentFor(VoxelChunk ch, MaterialProfile p) { float num = p.MaxDent; if (ch.Capability == 1) { num = MathF.Min(num, 0.02f); } if (ch.Capability <= 0) { num = 0f; } return num; } public static Result Apply(in ImpactEvent e, MaterialProfile hitProf, List<Int3> chunkKeys, HashSet<Int3> flipped) { return Run(in e, hitProf, chunkKeys, flipped, Mode.Apply); } public static Result Recompute(in ImpactEvent e, MaterialProfile hitProf, List<Int3> chunkKeys, HashSet<Int3> flipped) { return Run(in e, hitProf, chunkKeys, flipped, Mode.Recompute); } public static float PotentialErosion(in ImpactEvent e, MaterialProfile hitProf, List<Int3> chunkKeys) { return Run(in e, hitProf, chunkKeys, null, Mode.Potential).MaxPotential; } private static Result Run(in ImpactEvent e, MaterialProfile hitProf, List<Int3> chunkKeys, HashSet<Int3> flipped, Mode mode) { Result result = default(Result); float cell = VoxelWorld.Cell; float num = MathF.Max(CraterRadius(in e, hitProf), cell * 0.75f); float num2 = num * 1.6f; float num3 = InfluenceRadius(in e, hitProf); V3 point = e.Point; V3 v = (-e.Normal * 0.6f + e.Direction * 0.4f).Normalized; if (v.SqrMag < 0.5f) { v = -e.Normal; } float num4 = 1f / (cell * 2.5f); V3 b = -e.Normal; float num5 = ((mode == Mode.Potential) ? 0f : MathF.Max(0f, 0f - VoxelWorld.SampleSdf0(point, 0f))); float num6 = num5 * MathUtil.Clamp01((num5 - cell * 0.5f) / cell) * 0.95f; foreach (Int3 chunkKey in chunkKeys) { VoxelChunk voxelChunk = VoxelWorld.Get(chunkKey); if (voxelChunk == null || !voxelChunk.Voxelized) { continue; } bool flag = false; int num7 = 16; int num8 = 16; int num9 = 16; int num10 = 0; int num11 = 0; int num12 = 0; int num13 = Math.Max(0, (int)MathF.Floor((point.x - num3 - voxelChunk.Origin.x) / cell)); int num14 = Math.Max(0, (int)MathF.Floor((point.y - num3 - voxelChunk.Origin.y) / cell)); int num15 = Math.Max(0, (int)MathF.Floor((point.z - num3 - voxelChunk.Origin.z) / cell)); int num16 = Math.Min(16, (int)MathF.Ceiling((point.x + num3 - voxelChunk.Origin.x) / cell)); int num17 = Math.Min(16, (int)MathF.Ceiling((point.y + num3 - voxelChunk.Origin.y) / cell)); int num18 = Math.Min(16, (int)MathF.Ceiling((point.z + num3 - voxelChunk.Origin.z) / cell)); for (int i = num15; i <= num18; i++) { for (int j = num14; j <= num17; j++) { for (int k = num13; k <= num16; k++) { V3 v2 = voxelChunk.PointPos(k, j, i); float mag = (v2 - point).Mag; if (mag >= num3) { continue; } int num19 = VoxelChunk.Idx(k, j, i); MaterialProfile materialProfile = MaterialPalette.ProfileOf(voxelChunk.Mat[num19]) ?? hitProf; float num20 = Units(in e, materialProfile); float num21 = mag; if (materialProfile.Cone > 1.001f) { float num22 = V3.Dot(v2 - point, b); num21 = MathF.Sqrt(MathF.Max(0f, mag * mag - num22 * num22) / (materialProfile.Cone * materialProfile.Cone) + num22 * num22); } float v3 = 0.5f; float edge = 1f; bool flag2 = Fracture.Chunky(materialProfile) && num21 < num * 1.25f; if (flag2) { v3 = Fracture.Cell(Fracture.Warp(v2, materialProfile, e.Grain), Fracture.SeedOf(materialProfile), out edge); num21 *= Fracture.DistanceScale(v3, materialProfile); } float num23 = MathUtil.Falloff(num21 / num); if (num23 > 0f && materialProfile.ErosionScale > 0f) { if (mode == Mode.Apply) { voxelChunk.Damage[num19] += num20 * num23; } float num24 = MathUtil.Fbm(v2 * num4, 1337); float num25 = (flag2 ? Fracture.MultFrom(v3, edge, materialProfile, num24) : (0.6f + 0.8f * num24)); float num26 = MathF.Max(0f, voxelChunk.Damage[num19] - materialProfile.Threshold) * materialProfile.ErosionScale * num25; if (num6 > 0f && num26 > 0f) { num26 += num6 * MathUtil.Clamp01((1f - num21 / num) * 2.5f); } if (mode == Mode.Potential) { if (MathF.Abs(voxelChunk.Sdf0[num19]) < cell * 0.6f) { result.MaxPotential = MathF.Max(result.MaxPotential, MathF.Min(num26, materialProfile.MaxErosion)); } continue; } num26 = MathF.Min(num26, MaxErosionFor(voxelChunk, materialProfile)); float num27 = voxelChunk.Erosion[num19]; if (num26 > num27 + 0.0001f) { float num28 = voxelChunk.Sdf0[num19]; if (num28 >= (0f - cell) * 0.5f && num28 < cell * 0.5f) { result.RemovedVolume += (num26 - num27) * cell * cell; } bool num29 = num28 + num27 < 0f; bool flag3 = num28 + num26 < 0f; if (num29 && !flag3 && num28 < (0f - cell) * 0.5f) { result.RemovedVolume += cell * cell * cell; } voxelChunk.Erosion[num19] = num26; voxelChunk.HasErosion = true; result.AnyErosion = true; flag = true; VoxelWorld.MarkDamaged(v2); if (k < num7) { num7 = k; } if (k > num10) { num10 = k; } if (j < num8) { num8 = j; } if (j > num11) { num11 = j; } if (i < num9) { num9 = i; } if (i > num12) { num12 = i; } if (num29 && !flag3) { flipped?.Add(new Int3(chunkKey.X * 16 + k, chunkKey.Y * 16 + j, chunkKey.Z * 16 + i)); } } } if (mode != Mode.Apply || !(materialProfile.Ductility > 0.001f)) { continue; } float num30 = MathUtil.Falloff(mag / num2); float num31 = MaxDentFor(voxelChunk, materialProfile); if (!(num30 > 0f) || !(num31 > 0f)) { continue; } V3 v4 = voxelChunk.Disp[num19] + v * (materialProfile.Ductility * materialProfile.DentScale * num20 * num30); float mag2 = v4.Mag; if (mag2 > num31) { v4 *= num31 / mag2; } if ((v4 - voxelChunk.Disp[num19]).SqrMag > 1E-06f) { flag = true; VoxelWorld.MarkDamaged(v2); if (k < num7) { num7 = k; } if (k > num10) { num10 = k; } if (j < num8) { num8 = j; } if (j > num11) { num11 = j; } if (i < num9) { num9 = i; } if (i > num12) { num12 = i; } } voxelChunk.Disp[num19] = v4; voxelChunk.HasDisp = true; result.AnyDisp = true; } } } if (!flag) { continue; } ref HashSet<Int3> changedChunks = ref result.ChangedChunks; (changedChunks ?? (changedChunks = new HashSet<Int3>())).Add(chunkKey); int num32 = ((num7 <= 2) ? (-1) : 0); int num33 = ((num10 >= 14) ? 1 : 0); int num34 = ((num8 <= 2) ? (-1) : 0); int num35 = ((num11 >= 14) ? 1 : 0); int num36 = ((num9 <= 2) ? (-1) : 0); int num37 = ((num12 >= 14) ? 1 : 0); for (int l = num36; l <= num37; l++) { for (int m = num34; m <= num35; m++) { for (int n = num32; n <= num33; n++) { if (n != 0 || m != 0 || l != 0) { Int3 @int = new Int3(chunkKey.X + n, chunkKey.Y + m, chunkKey.Z + l); if (VoxelWorld.Get(@int) != null) { changedChunks = ref result.NeighborChunks; (changedChunks ?? (changedChunks = new HashSet<Int3>())).Add(@int); } } } } } } return result; } } public sealed class MaterialProfile { public int Id; public string Name; public float Toughness; public float CraterScale; public float ErosionScale; public float Threshold; public float MaxErosion; public float Ductility; public float DentScale; public float MaxDent; public float Density; public float DebrisSize; public float BrokenTint; public bool Splinters; public float ChunkSize; public float Chunkiness; public float Cone = 1f; public float Rough; public V3 Stretch = new V3(1f, 1f, 1f); public float GrainStretch = 1f; public MaterialProfile Fracture(float chunk, float chunkiness, float cone, float rough) { ChunkSize = chunk; Chunkiness = chunkiness; Cone = cone; Rough = rough; return this; } } public static class MaterialProfiles { private static int _id; public static readonly MaterialProfile Concrete; public static readonly MaterialProfile Brick; public static readonly MaterialProfile Rock; public static readonly MaterialProfile Plaster; public static readonly MaterialProfile Wood; public static readonly MaterialProfile Metal; public static readonly MaterialProfile Glass; public static readonly MaterialProfile Dirt; public static readonly MaterialProfile Default; private static readonly (string[] keys, MaterialProfile prof)[] Table; private static MaterialProfile P(string name, float tough, float crater, float ero, float thr, float maxEro, float duct, float dent, float maxDent, float dens, float dsize, float tint, bool splinter = false) { return new MaterialProfile { Id = ++_id, Name = name, Toughness = tough, CraterScale = crater, ErosionScale = ero, Threshold = thr, MaxErosion = maxEro, Ductility = duct, DentScale = dent, MaxDent = maxDent, Density = dens, DebrisSize = dsize, BrokenTint = tint, Splinters = splinter }; } static MaterialProfiles() { Concrete = P("Concrete", 140f, 0.13f, 0.075f, 0.06f, 3f, 0f, 0f, 0f, 2400f, 1f, 0.72f).Fracture(0.14f, 0.8f, 1.25f, 0.03f); Brick = P("Brick", 120f, 0.13f, 0.08f, 0.05f, 3f, 0f, 0f, 0f, 1900f, 1.1f, 0.7f).Fracture(0.13f, 0.85f, 1.2f, 0.025f); Rock = P("Rock", 200f, 0.12f, 0.07f, 0.08f, 3f, 0f, 0f, 0f, 2600f, 1.2f, 0.75f).Fracture(0.22f, 0.85f, 1.15f, 0.045f); Plaster = P("Plaster", 50f, 0.16f, 0.11f, 0.02f, 3f, 0f, 0f, 0f, 900f, 0.7f, 0.85f).Fracture(0.08f, 0.65f, 1.4f, 0.012f); Wood = P("Wood", 110f, 0.11f, 0.07f, 0.06f, 3f, 0.15f, 0.03f, 0.05f, 600f, 0.9f, 0.8f, splinter: true).Fracture(0.09f, 0.75f, 1f, 0.02f); Metal = P("Metal", 300f, 0.11f, 0.05f, 1.6f, 2f, 1f, 0.06f, 0.3f, 7800f, 0.6f, 0.6f); Glass = P("Glass", 30f, 0.2f, 0f, 0f, 0f, 0f, 0f, 0f, 2500f, 0.6f, 1f); Dirt = P("Dirt", 60f, 0.15f, 0.1f, 0.02f, 3f, 0f, 0f, 0f, 1600f, 0.9f, 0.6f).Fracture(0.18f, 0.35f, 1.1f, 0.02f); Default = P("Default", 140f, 0.13f, 0.075f, 0.06f, 3f, 0f, 0f, 0f, 2200f, 1f, 0.72f).Fracture(0.14f, 0.75f, 1.2f, 0.028f); Table = new(string[], MaterialProfile)[8] { (new string[3] { "glass", "window", "mirror" }, Glass), (new string[13] { "metal", "steel", "iron", "alumin", "chrome", "pipe", "rail", "sheet", "tin", "copper", "brass", "grate", "vent" }, Metal), (new string[7] { "wood", "plank", "timber", "plywood", "crate", "log", "bark" }, Wood), (new string[7] { "plaster", "drywall", "gypsum", "stucco", "ceiling", "foam", "cardboard" }, Plaster), (new string[1] { "brick" }, Brick), (new string[8] { "dirt", "soil", "mud", "sand", "grass", "ground", "gravel", "terrain" }, Dirt), (new string[7] { "rock", "stone", "cliff", "boulder", "granite", "marble", "cave" }, Rock), (new string[8] { "concrete", "cement", "asphalt", "tile", "floor", "wall", "pillar", "road" }, Concrete) }; Brick.Stretch = new V3(0.55f, 1.6f, 0.55f); Wood.GrainStretch = 0.25f; } public static MaterialProfile TryClassify(string text) { if (string.IsNullOrEmpty(text)) { return null; } string text2 = text.ToLowerInvariant(); (string[], MaterialProfile)[] table = Table; for (int i = 0; i < table.Length; i++) { (string[], MaterialProfile) tuple = table[i]; string[] item = tuple.Item1; MaterialProfile item2 = tuple.Item2; string[] array = item; foreach (string value in array) { if (text2.Contains(value)) { return item2; } } } return null; } } public sealed class PaletteEntry { public byte Index; public MaterialProfile Profile; public Material RenderMaterial; public GameObject ImpactSource; public PhysicMaterial PhysicMat; public int Layer; private Material _broken; public Material Broken { get { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if ((Object)(object)_broken == (Object)null && (Object)(object)RenderMaterial != (Object)null) { _broken = new Material(RenderMaterial); ((Object)_broken).name = ((Object)RenderMaterial).name + "_MDBroken"; TintColor(_broken, "_BaseColor", Profile.BrokenTint); TintColor(_broken, "_Color", Profile.BrokenTint); } return _broken; } } private static void TintColor(Material m, string prop, float k) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) try { if (m.HasProperty(prop)) { Color color = m.GetColor(prop); color.r *= k; color.g *= k; color.b *= k; m.SetColor(prop, color); } } catch { } } public void DestroyRuntime() { if ((Object)(object)_broken != (Object)null) { Object.Destroy((Object)(object)_broken); } _broken = null; } } public static class MaterialPalette { private static readonly List<PaletteEntry> _entries = new List<PaletteEntry> { null }; private static readonly Dictionary<int, byte> _byCollider = new Dictionary<int, byte>(); private static readonly Dictionary<long, byte> _byKey = new Dictionary<long, byte>(); public static PaletteEntry Get(byte i) { if (i <= 0 || i >= _entries.Count) { return null; } return _entries[i]; } public static MaterialProfile ProfileOf(byte i) { return Get(i)?.Profile; } public static byte ForCollider(Collider c) { if ((Object)(object)c == (Object)null) { return 0; } int instanceID = ((Object)c).GetInstanceID(); if (_byCollider.TryGetValue(instanceID, out var value)) { return value; } if (StaticIndex.TryGetPiecePalette(instanceID, out var palette)) { _byCollider[instanceID] = palette; return palette; } Material val = FindRenderMaterial(c); MaterialProfile materialProfile = MaterialProfiles.TryClassify(SlzBridge.SurfaceName(((Component)c).gameObject)); if (materialProfile == null) { string text = ""; try { if ((Object)(object)c.sharedMaterial != (Object)null) { text = text + ((Object)c.sharedMaterial).name + " "; } } catch { } if ((Object)(object)val != (Object)null) { text = text + ((Object)val).name + " "; } text += ((Object)((Component)c).gameObject).name; materialProfile = MaterialProfiles.TryClassify(text) ?? MaterialProfiles.Default; } byte orAdd = GetOrAdd(materialProfile, val); _byCollider[instanceID] = orAdd; PaletteEntry paletteEntry = _entries[orAdd]; if ((Object)(object)paletteEntry.ImpactSource == (Object)null) { paletteEntry.ImpactSource = ((Component)c).gameObject; paletteEntry.Layer = ((Component)c).gameObject.layer; try { paletteEntry.PhysicMat = c.sharedMaterial; } catch { } } Log.Debug($"Palette: '{((Object)((Component)c).gameObject).name}' → {materialProfile.Name} / {(((Object)(object)val != (Object)null) ? ((Object)val).name : "(invisible)")}"); return orAdd; } public static byte ForRendererMaterial(Material m, MaterialProfile fallback) { return GetOrAdd(MaterialProfiles.TryClassify(((Object)(object)m != (Object)null) ? ((Object)m).name : null) ?? fallback ?? MaterialProfiles.Default, m); } private static byte GetOrAdd(MaterialProfile prof, Material renderMat) { long key = ((long)prof.Id << 32) | (uint)(((Object)(object)renderMat != (Object)null) ? ((Object)renderMat).GetInstanceID() : 0); if (_byKey.TryGetValue(key, out var value)) { return value; } if (_entries.Count >= 255) { return 1; } value = (byte)_entries.Count; _entries.Add(new PaletteEntry { Index = value, Profile = prof, RenderMaterial = renderMat }); _byKey[key] = value; return value; } private static Material FindRenderMaterial(Collider c) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) try { MeshRenderer component = ((Component)c).GetComponent<MeshRenderer>(); if ((Object)(object)component != (Object)null && (Object)(object)((Renderer)component).sharedMaterial != (Object)null) { return ((Renderer)component).sharedMaterial; } component = ((Component)c).GetComponentInParent<MeshRenderer>(); if ((Object)(object)component != (Object)null && (Object)(object)((Renderer)component).sharedMaterial != (Object)null) { return ((Renderer)component).sharedMaterial; } } catch { } StaticIndex.RendererEntry rendererEntry = StaticIndex.FindRendererOverlapping(Box3.FromBounds(c.bounds)); if (!((Object)(object)rendererEntry?.Renderer != (Object)null)) { return null; } return ((Renderer)rendererEntry.Renderer).sharedMaterial; } public static void Clear() { foreach (PaletteEntry entry in _entries) { entry?.DestroyRuntime(); } _entries.Clear(); _entries.Add(null); _byCollider.Clear(); _byKey.Clear(); } } public sealed class VoxelizeJob { private struct Ev { public float T; public bool Enter; public byte Mat; } private struct Interval { public float A; public float B; public byte Mat; } private const int P = 17; private const int N = 16; private const float Margin = 4f; private const float Eps = 0.002f; private const int MaxHitsPerPass = 24; public readonly VoxelChunk Chunk; private readonly float[] _dist = new float[14739]; private readonly sbyte[] _state = new sbyte[14739]; private readonly byte[] _mat = new byte[14739]; private int _axis; private int _line; private readonly int[] _coord = new int[3]; private static readonly List<Ev> _events = new List<Ev>(64); private static readonly List<Interval> _intervals = new List<Interval>(32); private static readonly Comparison<Ev> _cmp = delegate(Ev p, Ev q) { int num = p.T.CompareTo(q.T); if (num != 0) { return num; } return (p.Enter != q.Enter) ? ((!p.Enter) ? 1 : (-1)) : 0; }; public bool Done { get; private set; } public VoxelizeJob(VoxelChunk ch) { Chunk = ch; } public bool Step(Stopwatch sw, double budgetMs) { while (_axis < 3) { ProcessLine(_axis, _line % 17, _line / 17); _line++; if (_line >= 289) { _line = 0; _axis++; } if (sw.Elapsed.TotalMilliseconds > budgetMs) { break; } } if (_axis >= 3 && !Done) { Finish(); Done = true; } return Done; } private void ProcessLine(int a, int u, int v) { int num = (a + 1) % 3; int num2 = (a + 2) % 3; float cell = Chunk.Cell; float num3 = 16f * cell + 8f; V3 origin = Chunk.Origin; origin[num] += (float)u * cell; origin[num2] += (float)v * cell; origin[a] -= 4f; V3 v2 = V3.Axis(a); _events.Clear(); CastPass(origin, v2, num3, forward: true); CastPass(origin + v2 * num3, -v2, num3, forward: false); _events.Sort(_cmp); BuildIntervals(num3); int[] coord = _coord; coord[num] = u; coord[num2] = v; for (int i = 0; i <= 16; i++) { coord[a] = i; int num4 = VoxelChunk.Idx(coord[0], coord[1], coord[2]); int num5 = a * 4913 + num4; if (_intervals.Count == 0) { _state[num5] = -1; continue; } float num6 = 4f + (float)i * cell; bool flag = false; float num7 = float.MaxValue; byte b = 0; for (int j = 0; j < _intervals.Count; j++) { Interval interval = _intervals[j]; if (num6 >= interval.A && num6 <= interval.B) { flag = true; } float x = MathF.Abs(num6 - interval.A); float y = MathF.Abs(num6 - interval.B); float num8 = MathF.Min(x, y); if (num8 < num7 || (num6 >= interval.A && num6 <= interval.B && num8 <= num7)) { num7 = num8; b = interval.Mat; } } _state[num5] = (flag ? ((sbyte)1) : ((sbyte)0)); _dist[num5] = MathF.Min(num7, cell * 2f); _mat[num5] = b; } } private static void CastPass(V3 start, V3 dir, float L, bool forward) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Vector3 u = dir.U; float num = 0f; int mapLayerMask = StaticIndex.MapLayerMask; RaycastHit val = default(RaycastHit); for (int i = 0; i < 24; i++) { if (!(num < L)) { break; } if (!Physics.Raycast((start + dir * num).U, u, ref val, L - num, mapLayerMask, (QueryTriggerInteraction)1)) { break; } float num2 = num + ((RaycastHit)(ref val)).distance; Collider collider = ((RaycastHit)(ref val)).collider; if ((Object)(object)collider != (Object)null && StaticIndex.IsMapCollider(collider)) { _events.Add(new Ev { T = (forward ? num2 : (L - num2)), Enter = forward, Mat = MaterialPalette.ForCollider(collider) }); } num = num2 + 0.002f; } } private static void BuildIntervals(float L) { _intervals.Clear(); float num = MathF.Max(0.05f, Settings.AssumedThickness.Value); int num2 = 0; float num3 = 0f; float num4 = 0f; byte mat = 0; foreach (Ev @event in _events) { if (@event.Enter) { if (num2 == 0) { num3 = @event.T; mat = @event.Mat; } num2++; } else if (num2 == 0) { float num5 = MathF.Max(num4, @event.T - num); if (@event.T - num5 > 0.0001f) { _intervals.Add(new Interval { A = num5, B = @event.T, Mat = @event.Mat }); } } else { num2--; if (num2 == 0) { _intervals.Add(new Interval { A = num3, B = @event.T, Mat = mat }); } } num4 = @event.T; } if (num2 > 0) { float b = MathF.Min(L, MathF.Max(num3 + num, num4)); _intervals.Add(new Interval { A = num3, B = b, Mat = mat }); } } private void Finish() { float num = Chunk.Cell * 2f; for (int i = 0; i < 4913; i++) { int num2 = 0; int num3 = 0; float num4 = float.MaxValue; float num5 = float.MaxValue; byte b = 0; byte b2 = 0; for (int j = 0; j < 3; j++) { int num6 = j * 4913 + i; sbyte b3 = _state[num6]; if (b3 < 0) { continue; } if (b3 == 1) { num2++; if (_dist[num6] < num4) { num4 = _dist[num6]; b = _mat[num6]; } } else { num3++; if (_dist[num6] < num5) { num5 = _dist[num6]; b2 = _mat[num6]; } } } if (num2 == 0 && num3 == 0) { Chunk.Sdf0[i] = num; Chunk.Mat[i] = 0; } else { bool flag = num2 > num3 || (num2 == num3 && num4 <= num5); Chunk.Sdf0[i] = (flag ? (0f - MathF.Min(num4, num)) : MathF.Min(num5, num)); Chunk.Mat[i] = (flag ? b : ((b2 != 0) ? b2 : b)); } } Chunk.Voxelized = true; } } public sealed class VoxelChunk { public const int N = 16; public const int P = 17; public const int PointCount = 4913; public readonly Int3 Key; public readonly V3 Origin; public readonly float Cell; public readonly float[] Sdf0 = new float[4913]; public readonly byte[] Mat = new byte[4913]; public readonly float[] Damage = new float[4913]; public readonly float[] Erosion = new float[4913]; public readonly V3[] Disp = new V3[4913]; public bool Voxelized; public bool HasErosion; public bool HasDisp; public int Capability = 2; public Box3 Bounds => new Box3(Origin, Origin + new V3(16f * Cell, 16f * Cell, 16f * Cell)); public VoxelChunk(Int3 key, float cell) { Key = key; Cell = cell; Origin = new V3((float)(key.X * 16) * cell, (float)(key.Y * 16) * cell, (float)(key.Z * 16) * cell); } public static int Idx(int x, int y, int z) { return x + 17 * (y + 17 * z); } public V3 PointPos(int x, int y, int z) { return new V3(Origin.x + (float)x * Cell, Origin.y + (float)y * Cell, Origin.z + (float)z * Cell); } } public static class VoxelWorld { private static readonly Dictionary<Int3, VoxelChunk> _chunks = new Dictionary<Int3, VoxelChunk>(); private static float _cell; public const float CoarseSize = 0.4f; private static readonly HashSet<Int3> _damaged = new HashSet<Int3>(); public static int DamagedCoarseCount => _damaged.Count; public static float Cell => _cell; public static float ChunkWorld => _cell * 16f; public static int Count => _chunks.Count; public static IEnumerable<VoxelChunk> All => _chunks.Values; public static void MarkDamaged(V3 p) { _damaged.Add(Int3.Floor(p / 0.4f)); } public static void DamageBoxesNear(Box3 region, float margin, List<Box3> outBoxes) { outBoxes.Clear(); if (_damaged.Count == 0 || region.IsEmpty) { return; } Box3 box = region.Expanded(margin + 0.4f); Int3 @int = Int3.Floor(box.Min / 0.4f); Int3 int2 = Int3.Floor(box.Max / 0.4f); if ((long)(int2.X - @int.X + 1) * (long)(int2.Y - @int.Y + 1) * (int2.Z - @int.Z + 1) > _damaged.Count) { foreach (Int3 item in _damaged) { if (item.X >= @int.X && item.X <= int2.X && item.Y >= @int.Y && item.Y <= int2.Y && item.Z >= @int.Z && item.Z <= int2.Z) { outBoxes.Add(CoarseBox(item, margin)); } } return; } for (int i = @int.Z; i <= int2.Z; i++) { for (int j = @int.Y; j <= int2.Y; j++) { for (int k = @int.X; k <= int2.X; k++) { Int3 int3 = new Int3(k, j, i); if (_damaged.Contains(int3)) { outBoxes.Add(CoarseBox(int3, margin)); } } } } } private static Box3 CoarseBox(Int3 k, float margin) { return new Box3(new V3(k.X, k.Y, k.Z) * 0.4f, new V3(k.X + 1, k.Y + 1, k.Z + 1) * 0.4f).Expanded(margin); } public static void Reset(float cell) { _chunks.Clear(); _damaged.Clear(); _cell = cell; } public static Int3 ChunkKeyOf(V3 p) { return Int3.Floor(p / ChunkWorld); } public static VoxelChunk Get(Int3 key) { if (!_chunks.TryGetValue(key, out var value)) { return null; } return value; } public static VoxelChunk GetOrCreate(Int3 key) { if (!_chunks.TryGetValue(key, out var value)) { value = new VoxelChunk(key, _cell); _chunks[key] = value; } return value; } public static void ChunksInSphere(V3 c, float r, List<Int3> outKeys) { outKeys.Clear(); Int3 @int = ChunkKeyOf(c - new V3(r, r, r)); Int3 int2 = ChunkKeyOf(c + new V3(r, r, r)); float chunkWorld = ChunkWorld; for (int i = @int.Z; i <= int2.Z; i++) { for (int j = @int.Y; j <= int2.Y; j++) { for (int k = @int.X; k <= int2.X; k++) { if (new Box3(new V3((float)k * chunkWorld, (float)j * chunkWorld, (float)i * chunkWorld), new V3((float)(k + 1) * chunkWorld, (float)(j + 1) * chunkWorld, (float)(i + 1) * chunkWorld)).IntersectsSphere(c, r)) { outKeys.Add(new Int3(k, j, i)); } } } } } private static bool Locate(V3 p, out VoxelChunk ch, out int x, out int y, out int z, out float fx, out float fy, out float fz) { float num = p.x / _cell; float num2 = p.y / _cell; float num3 = p.z / _cell; int num4 = (int)MathF.Floor(num); int num5 = (int)MathF.Floor(num2); int num6 = (int)MathF.Floor(num3); fx = num - (float)num4; fy = num2 - (float)num5; fz = num3 - (float)num6; Int3 key = new Int3(Int3.FloorDiv(num4, 16), Int3.FloorDiv(num5, 16), Int3.FloorDiv(num6, 16)); x = num4 - key.X * 16; y = num5 - key.Y * 16; z = num6 - key.Z * 16; _chunks.TryGetValue(key, out ch); if (ch != null) { return ch.Voxelized; } return false; } private static float Tri(float[] a, int x, int y, int z, float fx, float fy, float fz) { int num = VoxelChunk.Idx(x, y, z); float num2 = a[num] + (a[num + 1] - a[num]) * fx; float num3 = a[num + 17] + (a[num + 17 + 1] - a[num + 17]) * fx; float num4 = a[num + 289] + (a[num + 289 + 1] - a[num + 289]) * fx; float num5 = a[num + 289 + 17] + (a[num + 289 + 17 + 1] - a[num + 289 + 17]) * fx; float num6 = num2 + (num3 - num2) * fy; float num7 = num4 + (num5 - num4) * fy; return num6 + (num7 - num6) * fz; } public static float SampleSdf0(V3 p, float fallback) { if (!Locate(p, out var ch, out var x, out var y, out var z, out var fx, out var fy, out var fz)) { return fallback; } return Tri(ch.Sdf0, x, y, z, fx, fy, fz); } public static float SampleErosion(V3 p) { if (!Locate(p, out var ch, out var x, out var y, out var z, out var fx, out var fy, out var fz)) { return 0f; } if (!ch.HasErosion) { return 0f; } return Tri(ch.Erosion, x, y, z, fx, fy, fz); } public static V3 SampleDisp(V3 p) { if (!Locate(p, out var ch, out var x, out var y, out var z, out var fx, out var fy, out var fz)) { return V3.Zero; } if (!ch.HasDisp) { return V3.Zero; } V3[] disp = ch.Disp; int num = VoxelChunk.Idx(x, y, z); V3 a = V3.Lerp(disp[num], disp[num + 1], fx); V3 b = V3.Lerp(disp[num + 17], disp[num + 17 + 1], fx); V3 a2 = V3.Lerp(disp[num + 289], disp[num + 289 + 1], fx); return V3.Lerp(b: V3.Lerp(a2, V3.Lerp(disp[num + 289 + 17], disp[num + 289 + 17 + 1], fx), fy), a: V3.Lerp(a, b, fy), t: fz); } public static bool TryGetPoint(int gx, int gy, int gz, out VoxelChunk ch, out int idx) { Int3 key = new Int3(Int3.FloorDiv(gx, 16), Int3.FloorDiv(gy, 16), Int3.FloorDiv(gz, 16)); idx = 0; if (!_chunks.TryGetValue(key, out ch) || !ch.Voxelized) { return false; } idx = VoxelChunk.Idx(gx - key.X * 16, gy - key.Y * 16, gz - key.Z * 16); return true; } public static void ForEachCopy(int gx, int gy, int gz, Action<VoxelChunk, int> act) { int num = Int3.FloorDiv(gx, 16); int num2 = Int3.FloorDiv(gy, 16); int num3 = Int3.FloorDiv(gz, 16); int num4 = gx - num * 16; int num5 = gy - num2 * 16; int num6 = gz - num3 * 16; for (int i = 0; i <= ((num6 == 0) ? 1 : 0); i++) { for (int j = 0; j <= ((num5 == 0) ? 1 : 0); j++) { for (int k = 0; k <= ((num4 == 0) ? 1 : 0); k++) { Int3 key = new Int3(num - k, num2 - j, num3 - i); if (_chunks.TryGetValue(key, out var value) && value.Voxelized) { act(value, VoxelChunk.Idx(num4 + k * 16, num5 + j * 16, num6 + i * 16)); } } } } } public static float FieldAt(VoxelChunk ch, int idx, V3 pointPos) { float num = ch.Sdf0[idx]; if (ch.HasDisp) { V3 v = ch.Disp[idx]; if (v.SqrMag > 1E-08f) { num = SampleSdf0(pointPos - v, num); } } return num + (ch.HasErosion ? ch.Erosion[idx] : 0f); } public static float FieldAtGlobal(int gx, int gy, int gz, out float erosion, out byte mat) { erosion = 0f; mat = 0; if (!TryGetPoint(gx, gy, gz, out var ch, out var idx)) { return _cell * 2f; } erosion = (ch.HasErosion ? ch.Erosion[idx] : 0f); mat = ch.Mat[idx]; return FieldAt(ch, idx, new V3((float)gx * _cell, (float)gy * _cell, (float)gz * _cell)); } public static void Clear() { _chunks.Clear(); _damaged.Clear(); } public static void RemoveInBox(Box3 b, List<Int3> removed) { removed.Clear(); foreach (KeyValuePair<Int3, VoxelChunk> chunk in _chunks) { if (chunk.Value.Bounds.Intersects(b)) { removed.Add(chunk.Key); } } foreach (Int3 item in removed) { _chunks.Remove(item); } } } } namespace MapDestruction.Util { public static class BackgroundJobs { public sealed class Job { internal Func<object> Work; public volatile bool Done; public object Result; public string Error; public volatile bool Cancelled; } private static readonly ConcurrentQueue<Job> _q = new ConcurrentQueue<Job>(); private static readonly AutoResetEvent _wake = new AutoResetEvent(initialState: false); private static Thread _thread; public static Job Run(Func<object> work) { Job job = new Job { Work = work }; if (_thread == null) { _thread = new Thread(Loop) { IsBackground = true, Name = "MapDestruction 계산", Priority = ThreadPriority.BelowNormal }; _thread.Start(); } _q.Enqueue(job); _wake.Set(); return job; } private static void Loop() { while (true) { if (_q.TryDequeue(out var result)) { if (result.Cancelled) { result.Done = true; continue; } try { result.Result = result.Work(); } catch (Exception ex) { result.Error = ex.Message; } result.Done = true; } else { _wake.WaitOne(1000); } } } } public static class Fracture { public static float Cell(V3 q, int seed, out float edge) { int num = (int)MathF.Floor(q.x); int num2 = (int)MathF.Floor(q.y); int num3 = (int)MathF.Floor(q.z); float num4 = float.MaxValue; float num5 = float.MaxValue; int x = 0; int y = 0; int z = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { int num6 = num + k; int num7 = num2 + j; int num8 = num3 + i; float num9 = (float)num6 + 0.1f + 0.8f * MathUtil.Hash01(num6, num7, num8, seed) - q.x; float num10 = (float)num7 + 0.1f + 0.8f * MathUtil.Hash01(num6, num7, num8, seed + 1) - q.y; float num11 = (float)num8 + 0.1f + 0.8f * MathUtil.Hash01(num6, num7, num8, seed + 2) - q.z; float num12 = num9 * num9 + num10 * num10 + num11 * num11; if (num12 < num4) { num5 = num4; num4 = num12; x = num6; y = num7; z = num8; } else if (num12 < num5) { num5 = num12; } } } } edge = (MathF.Sqrt(num5) - MathF.Sqrt(num4)) * 0.5f; return MathUtil.Hash01(x, y, z, seed + 3); } public static V3 Warp(V3 p, MaterialProfile prof, V3 grain) { V3 stretch = prof.Stretch; V3 v = new V3(p.x * stretch.x, p.y * stretch.y, p.z * stretch.z) / prof.ChunkSize; if (prof.GrainStretch != 1f && grain.SqrMag > 0.5f) { v += grain * (V3.Dot(grain, v) * (prof.GrainStretch - 1f)); } return v; } public static float ErosionMult(V3 p, MaterialProfile prof, V3 grain, float smooth) { if (!Chunky(prof)) { return 0.6f + 0.8f * smooth; } float edge; return MultFrom(Cell(Warp(p, prof, grain), SeedOf(prof), out edge), edge, prof, smooth); } public static bool Chunky(MaterialProfile prof) { if (prof != null && prof.ChunkSize > 0f) { return prof.Chunkiness > 0f; } return false; } public static int SeedOf(MaterialProfile prof) { return 4242 + prof.Id * 31; } public static float MultFrom(float v, float edge, MaterialProfile prof, float smooth) { float num = 0.6f + 0.8f * smooth; float num2 = 0.5f + 0.9f * v; num2 += 0.25f * MathUtil.Clamp01(1f - edge / 0.12f); return num + (num2 - num) * prof.Chunkiness; } public static float DistanceScale(float v, MaterialProfile prof) { return 1f - prof.Chunkiness * 0.3f * (v - 0.5f); } public static float Relief(V3 p, MaterialProfile prof, V3 grain) { if (prof == null || prof.Rough <= 0f) { return 0f; } V3 stretch = prof.Stretch; V3 v = new V3(p.x * stretch.x, p.y * stretch.y, p.z * stretch.z); float num = 1f - MathF.Abs(MathUtil.ValueNoise(v / 0.3f, 911 + prof.Id) * 2f - 1f); float num2 = MathUtil.ValueNoise(v / 0.13f, 937 + prof.Id) - 0.5f; return prof.Rough * ((num - 0.55f) * 1.8f + num2 * 0.8f); } } public static class GpuGuard { public const string Gpu = "gpu"; public const string MeshInfo = "meshinfo"; private static string _path; private static bool _active; public static string PendingNotice; private static string PathOf() { if (_path != null) { return _path; } try { _path = Path.Combine(MelonEnvironment.UserDataDirectory, "MapDestruction_native.lock"); } catch { _path = "MapDestruction_native.lock"; } return _path; } public static void CheckPreviousCrash() { try { string path = PathOf(); if (File.Exists(path)) { string text = ""; try { text = File.ReadAllText(path).Trim(); } catch { } try { File.Delete(path); } catch { } int num = text.IndexOf('|'); string obj3 = ((num >= 0) ? text.Substring(0, num) : text); string text2 = ((num >= 0) ? text.Substring(num + 1) : ""); string value; if (obj3 == "meshinfo") { Settings.MeshInfoQueries.Value = false; value = "잠긴 메시의 삼각형 수 조회"; } else { Settings.GpuMeshRead.Value = false; value = "그래픽 카드 메모리 읽기"; } Settings.Save(); Log.Warn($"[안전장치] 지난번 실행에서 {value} 도중 게임이 꺼졌습니다{((text2.Length > 0) ? (" (대상: " + text2 + ")") : "")}. 이 기능을 자동으로 껐습니다."); PendingNotice = "Safety: a risky feature was turned off after the last crash (see log)"; } } catch { } } public static void Begin(string feature, string what) { try { File.WriteAllText(PathOf(), feature + "|" + (what ?? "?")); _active = true; } catch { } } public static void End() { if (!_active) { return; } _active = false; try { File.Delete(PathOf()); } catch { } } } public struct V3 { public float x; public float y; public float z; public static readonly V3 Zero = new V3(0f, 0f, 0f); public static readonly V3 One = new V3(1f, 1f, 1f); public float SqrMag => x * x + y * y + z * z; public float Mag => MathF.Sqrt(x * x + y * y + z * z); public V3 Normalized { get { float mag = Mag; if (!(mag > 1E-08f)) { return Zero; } return this / mag; } } public float this[int i] { get { return i switch { 1 => y, 0 => x, _ => z, }; } set { switch (i) { case 0: x = value; break; case 1: y = value; break; default: z = value; break; } } } public Vector3 U => new Vector3 { x = x, y = y, z = z }; public V3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public static V3 operator +(V3 a, V3 b) { return new V3(a.x + b.x, a.y + b.y, a.z + b.z); } public static V3 operator -(V3 a, V3 b) { return new V3(a.x - b.x, a.y - b.y, a.z - b.z); } public static V3 operator -(V3 a) { return new V3(0f - a.x, 0f - a.y, 0f - a.z); } public static V3 operator *(V3 a, float s) { return new V3(a.x * s, a.y * s, a.z * s); } public static V3 operator *(float s, V3 a) { return new V3(a.x * s, a.y * s, a.z * s); } public static V3 operator /(V3 a, float s) { float num = 1f / s; return new V3(a.x * num, a.y * num, a.z * num); } public static float Dot(V3 a, V3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } public static V3 Cross(V3 a, V3 b) { return new V3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } public static V3 Lerp(V3 a, V3 b, float t) { return new V3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t); } public static V3 Min(V3 a, V3 b) { return new V3(MathF.Min(a.x, b.x), MathF.Min(a.y, b.y), MathF.Min(a.z, b.z)); } public static V3 Max(V3 a, V3 b) { return new V3(MathF.Max(a.x, b.x), MathF.Max(a.y, b.y), MathF.Max(a.z, b.z)); } public static V3 Axis(int a) { return a switch { 1 => new V3(0f, 1f, 0f), 0 => new V3(1f, 0f, 0f), _ => new V3(0f, 0f, 1f), }; } public static V3 F(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new V3(v.x, v.y, v.z); } public static V3 RotateFromTo(V3 v, V3 a, V3 b) { V3 v2 = Cross(a, b); float mag = v2.Mag; float num = Dot(a, b); if (mag < 1E-06f) { if (num > 0f) { return v; } V3 v3 = ((MathF.Abs(a.x) < 0.9f) ? Cross(a, new V3(1f, 0f, 0f)).Normalized : Cross(a, new V3(0f, 1f, 0f)).Normalized); return v3 * (2f * Dot(v, v3)) - v; } V3 v4 = v2 / mag; return v * num + Cross(v4, v) * mag + v4 * (Dot(v4, v) * (1f - num)); } public override string ToString() { return $"({x:0.###}, {y:0.###}, {z:0.###})"; } } public struct V2 { public float x; public float y; public Vector2 U => new Vector2 { x = x, y = y }; public V2(float x, float y) { this.x = x; this.y = y; } public static V2 Lerp(V2 a, V2 b, float t) { return new V2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } public static V2 operator +(V2 a, V2 b) { return new V2(a.x + b.x, a.y + b.y); } public static V2 operator -(V2 a, V2 b) { return new V2(a.x - b.x, a.y - b.y); } public static V2 operator *(V2 a, float s) { return new V2(a.x * s, a.y * s); } public static V2 F(Vector2 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) return new V2(v.x, v.y); } } public struct V4 { public float x; public float y; public float z; public float w; public V3 Xyz => new V3(x, y, z); public Vector4 U => new Vector4 { x = x, y = y, z = z, w = w }; public V4(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; } public static V4 F(Vector4 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new V4(v.x, v.y, v.z, v.w); } } public struct C4 { public float r; public float g; public float b; public float a; public static readonly C4 White = new C4(1f, 1f, 1f, 1f); public Color U => new Color { r = r, g = g, b = b, a = a }; public C4(float r, float g, float b, float a) { this.r = r; this.g = g; this.b = b; this.a = a; } public static C4 Lerp(C4 p, C4 q, float t) { return new C4(p.r + (q.r - p.r) * t, p.g + (q.g - p.g) * t, p.b + (q.b - p.b) * t, p.a + (q.a - p.a) * t); } public static C4 F(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new C4(c.r, c.g, c.b, c.a); } } public struct Box3 { public V3 Min; public V3 Max; public static Box3 Empty => new Box3(new V3(float.MaxValue, float.MaxValue, float.MaxValue), new V3(float.MinValue, float.MinValue, float.MinValue)); public bool IsEmpty => Min.x > Max.x; public V3 Center => (Min + Max) * 0.5f; public V3 Size => Max - Min; public Box3(V3 min, V3 max) { Min = min; Max = max; } public void Encapsulate(V3 p) { Min = V3.Min(Min, p); Max = V3.Max(Max, p); } public void Encapsulate(Box3 b) { if (!b.IsEmpty) { Min = V3.Min(Min, b.Min); Max = V3.Max(Max, b.Max); } } public Box3 Expanded(float d) { return new Box3(Min - new V3(d, d, d), Max + new V3(d, d, d)); } public V3 ClosestPoint(V3 p) { return V3.Min(V3.Max(p, Min), Max); } public bool Contains(V3 p) { if (p.x >= Min.x && p.x <= Max.x && p.y >= Min.y && p.y <= Max.y && p.z >= Min.z) { return p.z <= Max.z; } return false; } public bool Intersects(Box3 o) { if (Min.x <= o.Max.x && Max.x >= o.Min.x && Min.y <= o.Max.y && Max.y >= o.Min.y && Min.z <= o.Max.z) { return Max.z >= o.Min.z; } return false; } public bool IntersectsSphere(V3 c, float r) { float num = MathF.Max(MathF.Max(Min.x - c.x, 0f), c.x - Max.x); float num2 = MathF.Max(MathF.Max(Min.y - c.y, 0f), c.y - Max.y); float num3 = MathF.Max(MathF.Max(Min.z - c.z, 0f), c.z - Max.z); return num * num + num2 * num2 + num3 * num3 <= r * r; } public static Box3 FromBounds(Bounds b) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) V3 v = V3.F(((Bounds)(ref b)).center); V3 v2 = V3.F(((Bounds)(ref b)).extents); return new Box3(v - v2, v + v2); } } public struct Mat4 { public float m00; public float m01; public float m02; public float m03; public float m10; public float m11; public float m12; public float m13; public float m20; public float m21; public float m22; public float m23; public static Mat4 Identity => new Mat4 { m00 = 1f, m11 = 1f, m22 = 1f }; public float Det3 => m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m12 * m20) + m02 * (m10 * m21 - m11 * m20); public static Mat4 F(Matrix4x4 m) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) return new Mat4 { m00 = m.m00, m01 = m.m01, m02 = m.m02, m03 = m.m03, m10 = m.m10, m11 = m.m11, m12 = m.m12, m13 = m.m13, m20 = m.m20, m21 = m.m21, m22 = m.m22, m23 = m.m23 }; } public V3 Point(V3 p) { return new V3(m00 * p.x + m01 * p.y + m02 * p.z + m03, m10 * p.x + m11 * p.y + m12 * p.z + m13, m20 * p.x + m21 * p.y + m22 * p.z + m23); } public V3 Vector(V3 v) { return new V3(m00 * v.x + m01 * v.y + m02 * v.z, m10 * v.x + m11 * v.y + m12 * v.z, m20 * v.x + m21 * v.y + m22 * v.z); } public Mat4 NormalMatrix() { float det = Det3; if (MathF.Abs(det) < 1E-12f) { return this; } float num = 1f / det; return new Mat4 { m00 = (m11 * m22 - m12 * m21) * num, m01 = (0f - (m10 * m22 - m12 * m20)) * num, m02 = (m10 * m21 - m11 * m20) * num, m10 = (0f - (m01 * m22 - m02 * m21)) * num, m11 = (m00 * m22 - m02 * m20) * num, m12 = (0f - (m00 * m21 - m01 * m20)) * num, m20 = (m01 * m12 - m02 * m11) * num, m21 = (0f - (m00 * m12 - m02 * m10)) * num, m22 = (m00 * m11 - m01 * m10) * num }; } } public readonly struct Int3 : IEquatable<Int3> { public readonly int X; public readonly int Y; public readonly int Z; public Int3(int x, int y, int z) { X = x; Y = y; Z = z; } public bool Equals(Int3 o) { if (X == o.X && Y == o.Y) { return Z == o.Z; } return false; } public override bool Equals(object obj) { if (obj is Int3 o) { return Equals(o); } return false; } public override int GetHashCode() { return (X * 73856093) ^ (Y * 19349663) ^ (Z * 83492791); } public static bool operator ==(Int3 a, Int3 b) { return a.Equals(b); } public static bool operator !=(Int3 a, Int3 b) { return !a.Equals(b); } public static Int3 operator +(Int3 a, Int3 b) { return new Int3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } public static Int3 operator -(Int3 a, Int3 b) { return new Int3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static Int3 operator *(Int3 a, int s) { return new Int3(a.X * s, a.Y * s, a.Z * s); } public static int FloorDiv(int a, int d) { if (a < 0) { return -((-a + d - 1) / d); } return a / d; } public static Int3 FloorDiv(Int3 a, int d) { return new Int3(FloorDiv(a.X, d), FloorDiv(a.Y, d), FloorDiv(a.Z, d)); } public static Int3 Floor(V3 v) { return new Int3((int)MathF.Floor(v.x), (int)MathF.Floor(v.y), (int)MathF.Floor(v.z)); } public override string ToString() { return $"({X},{Y},{Z})"; } } public static class MathUtil { public static float Hash01(int x, int y, int z, int seed) { int num = x * 374761393 + y * 668265263 + z * 1274126177 + seed * 144665; int num2 = (num ^ (num >>> 13)) * 1274126177; return (float)(uint)((num2 ^ (num2 >>> 16)) & 0xFFFFFF) / 16777215f; } private static float Smooth(float t) { return t * t * (3f - 2f * t); } public static float ValueNoise(V3 p, int seed) { int num = (int)MathF.Floor(p.x); int num2 = (int)MathF.Floor(p.y); int num3 = (int)MathF.Floor(p.z); float num4 = Smooth(p.x - (float)num); float num5 = Smooth(p.y - (float)num2); float num6 = Smooth(p.z - (float)num3); float num7 = Hash01(num, num2, num3, seed); float num8 = Hash01(num + 1, num2, num3, seed); float num9 = Hash01(num, num2 + 1, num3, seed); float num10 = Hash01(num + 1, num2 + 1, num3, seed); float num11 = Hash01(num, num2, num3 + 1, seed); float num12 = Hash01(num + 1, num2, num3 + 1, seed); float num13 = Hash01(num, num2 + 1, num3 + 1, seed); float num14 = Hash01(num + 1, num2 + 1, num3 + 1, seed); float num15 = num7 + (num8 - num7) * num4; float num16 = num9 + (num10 - num9) * num4; float num17 = num11 + (num12 - num11) * num4; float num18 = num13 + (num14 - num13) * num4; float num19 = num15 + (num16 - num15) * num5; float num20 = num17 + (num18 - num17) * num5; return num19 + (num20 - num19) * num6; } public static float Fbm(V3 p, int seed) { return ValueNoise(p, seed) * 0.65f + ValueNoise(p * 2.13f, seed + 17) * 0.35f; } public static float Falloff(float x) { if (x >= 1f) { return 0f; } float num = 1f - x * x; return num * num; } public static float Clamp(float v, float a, float b) { if (!(v < a)) { if (!(v > b)) { return v; } return b; } return a; } public static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } } public sealed class Rng { private uint _s; public float Value => (float)(NextU() & 0xFFFFFF) / 16777215f; public Rng(uint seed) { _s = ((seed == 0) ? 2654435769u : seed); } public uint NextU() { _s ^= _s << 13; _s ^= _s >> 17; _s ^= _s << 5; return _s; } public float Range(float a, float b) { return a + (b - a) * Value; } public int Range(int a, int b) { return a + (int)(NextU() % (uint)Math.Max(1, b - a)); } public V3 InsideSphere() { V3 result; do { result = new V3(Range(-1f, 1f), Range(-1f, 1f), Range(-1f, 1f)); } while (!(result.SqrMag <= 1f)); return result; } } internal static class UnityApi { public struct SubMeshInfo { public int IndexStart; public int IndexCount; public int BaseVertex; public int FirstVertex; public int VertexCount; public int Topology; } private const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public; private static readonly Dictionary<string, PropertyInfo> _props = new Dictionary<string, PropertyInfo>(); private static readonly Dictionary<string, MethodInfo> _methods = new Dictionary<string, MethodInfo>(); private static readonly HashSet<string> _warned = new HashSet<string>(); public const int AttrPosition = 0; public const int AttrNormal = 1; public const int AttrTangent = 2; public const int AttrColor = 3; public const int AttrTexCoord0 = 4; public const int AttrTexCoord1 = 5; public const int FmtFloat32 = 0; public const int FmtFloat16 = 1; public const int FmtUNorm8 = 2; public const int FmtSNorm8 = 3; public const int FmtUNorm16 = 4; public const int FmtSNorm16 = 5; public static readonly string[] RendererProps = new string[12] { "shadowCastingMode", "receiveShadows", "lightmapIndex", "lightmapScaleOffset", "realtimeLightmapIndex", "realtimeLightmapScaleOffset", "lightProbeUsage", "reflectionProbeUsage", "renderingLayerMask", "probeAnchor", "allowOcclusionWhenDynamic", "staticShadowCaster" }; public static bool GpuReadAvailable { get; private set; } public static string LastError { get; private set; } public static bool CanUse32BitIndices => FindProp(typeof(Mesh), "indexFormat")?.CanWrite ?? false; public static int FormatSize(int fmt) { switch (fmt) { default: return 1; case 1: case 4: case 5: return 2; case 0: return 4; } } public static void Init() { bool flag = FindMethod(typeof(Mesh), "GetVertexBufferImpl", 1) != null || FindMethod(typeof(Mesh), "GetVertexBuffer", 1) != null; bool flag2 = FindMethod(typeof(Mesh), "GetIndexBufferImpl", 0) != null || FindMethod(typeof(Mesh), "GetIndexBuffer", 0) != null; bool flag3 = FindMethod(typeof(Mesh), "GetSubMesh", 1) != null; bool flag4 = FindMethod(typeof(Mesh), "GetVertexAttributeOffset", 1) != null; GpuReadAvailable = flag && flag2 && flag3 && flag4; Log.Msg($"[API] GPU 메시 읽기: {(GpuReadAvailable ? "가능" : "불가")} (VB:{flag} IB:{flag2} SubMesh:{flag3} Attr:{flag4})"); Log.Msg($"[API] subMeshStartIndex: {FindProp(typeof(MeshRenderer), "subMeshStartIndex") != null}, isPartOfStaticBatch: {FindProp(typeof(Renderer), "isPartOfStaticBatch") != null}"); } private static PropertyInfo FindProp(Type t, string name) { string key = t.FullName + "." + name; if (_props.TryGetValue(key, out var value)) { return value; } try { value = t.GetProperty(name, BindingFlags.Instance | BindingFlags.Public); } catch { value = null; } _props[key] = value; return value; } private static MethodInfo FindMethod(Type t, string name, int paramCount, Func<ParameterInfo[], bool> extra = null, string tag = "") { string key = t.FullName + "." + name + "/" + paramCount + tag; if (_methods.TryGetValue(key, out var value)) { return value; } value = null; try { MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != name) && !methodInfo.IsGenericMethodDefinition) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == paramCount && (extra == null || extra(parameters))) { value = methodInfo; break; } } } } catch { } _methods[key] = value; return value; } private static void WarnOnce(string what, Exception e = null) { if (_warned.Add(what)) { Log.Warn("[API] " + what + " 사용 불가" + ((e != null) ? (": " + (e.InnerException ?? e).Message) : "")); } } private static object Get(object o, string prop) { PropertyInfo propertyInfo = FindProp(o.GetType(), prop); if (propertyInfo == null) { return null; } try { return propertyInfo.GetValue(o); } catch (Exception e) { WarnOnce(prop, e); return null; } } private static bool Set(object o, string prop, object value) { PropertyInfo propertyInfo = FindProp(o.GetType(), prop); if (propertyInfo == null || !propertyInfo.CanWrite) { WarnOnce(o.GetType().Name + "." + prop); return false; } try { propertyInfo.SetValue(o, value); return true; } catch (Exception e) { WarnOnce(prop, e); return false; } } private static object EnumValue(Type enumType, int v) { return Enum.ToObject(enumType, v); } private static int ReadIntMember(object o, string name) { Type type = o.GetType(); PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public); if (property != null) { return Convert.ToInt32(property.GetValue(o)); } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0) { return Convert.ToInt32(fieldInfo.GetValue(o)); } } throw new MissingMemberException(type.Name, name); } public static void CopyProperties(object src, object dst, string[] names) { foreach (string text in names) { PropertyInfo propertyInfo = FindProp(src.GetType(), text); PropertyInfo propertyInfo2 = FindProp(dst.GetType(), text); if (!(propertyInfo == null) && !(propertyInfo2 == null) && propertyInfo2.CanWrite) { try { propertyInfo2.SetValue(dst, propertyInfo.GetValue(src)); } catch (Exception e) { WarnOnce("복사 " + text, e); } } } } public static void MakeProbeLit(Renderer r) { Set(r, "lightmapIndex", -1); PropertyInfo propertyInfo = FindProp(((object)r).GetType(), "lightProbeUsage"); if (propertyInfo != null && propertyInfo.CanWrite) { try { propertyInfo.SetValue(r, EnumValue(propertyInfo.PropertyType, 1)); } catch { } } } public static bool IsStaticBatched(Renderer r) { object obj = Get(r, "isPartOfStaticBatch"); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } public static int SubMeshStartIndex(MeshRenderer r) { object obj = Get(r, "subMeshStartIndex"); if (obj == null) { return -1; } return Convert.ToInt32(obj); } public static void ForceLod(LODGroup g, int level) { MethodInfo methodInfo = FindMethod(typeof(LODGroup), "ForceLOD", 1); if (methodInfo == null) { WarnOnce("LODGroup.ForceLOD"); return; } try { methodInfo.Invoke(g, new object[1] { level }); } catch (Exception e) { WarnOnce("ForceLOD", e); } } public static void SetTangents(Mesh m, Vector4[] v) { Set(m, "tangents", new Il2CppStructArray<Vector4>(v)); } public static void SetUv2(Mesh m, Vector2[] v) { Set(m, "uv2", new Il2CppStructArray<Vector2>(v)); } public static void SetColors(Mesh m, Color[] v) { Set(m, "colors", new Il2CppStructArray<Color>(v)); } public static bool SetIndexFormat32(Mesh m) { PropertyInfo propertyInfo = FindProp(typeof(Mesh), "indexFormat"); if (propertyInfo == null || !propertyInfo.CanWrite) { WarnOnce("Mesh.indexFormat"); return false; } try { propertyInfo.SetValue(m, EnumValue(propertyInfo.PropertyType, 1)); return true; } catch (Exception e) { WarnOnce("indexFormat", e); return false; } } public static void SetSubmeshTriangles(Mesh m, int[] tris, int sub) { Il2CppStructArray<int> val = new Il2CppStructArray<int>(tris); MethodInfo methodInfo = null; for (int i = 2; i <= 4; i++) { if (!(methodInfo == null)) { break; } methodInfo = FindMethod(typeof(Mesh), "SetTriangles", i, (ParameterInfo[] ps) => ps[0].ParameterType.Name.StartsWith("Il2CppStructArray") && ps[1].ParameterType == typeof(int), "arr"); } if (methodInfo != null) { ParameterInfo[] parameters = methodInfo.GetParameters(); object[] array = new object[parameters.Length]; array[0] = val; array[1] = sub; for (int num = 2; num < parameters.Length; num++) { array[num] = ((parameters[num].ParameterType == typeof(bool)) ? ((object)true) : ((object)0)); } try { methodInfo.Invoke(m, array); return; } catch (Exception e) { WarnOnce("SetTriangles", e); } } if (sub == 0) { m.triangles = val; } } public static int IsReadable(Mesh m) { if (!(Get(m, "isReadable") is bool flag)) { return -1; } return flag ? 1 : 0; } public static Vector4[] GetTangents(Mesh m) { if (!(Get(m, "tangents") is Il2CppStructArray<Vector4> val)) { return null; } return Il2CppArrayBase<Vector4>.op_Implicit((Il2CppArrayBase<Vector4>)(object)val); } public static Vector2[] GetUv2(Mesh m) { if (!(Get(m, "uv2") is Il2CppStructArray<Vector2> val)) { return null; } return Il2CppArrayBase<Vector2>.op_Implicit((Il2CppArrayBase<Vector2>)(object)val); } public static Color[] GetColors(Mesh m) { if (!(Get(m, "colors") is Il2CppStructArray<Color> val)) { return null; } return Il2CppArrayBase<Color>.op_Implicit((Il2CppArrayBase<Color>)(object)val); } public static int[] GetTriangles(Mesh m, int sub) { MethodInfo methodInfo = FindMethod(typeof(Mesh), "GetTriangles", 1, (ParameterInfo[] ps) => ps[0].ParameterType == typeof(int), "int"); if (methodInfo != null) { try { if (methodInfo.Invoke(m, new object[1] { sub }) is Il2CppStructArray<int> val) { return Il2CppArrayBase<int>.op_Implicit((Il2CppArrayBase<int>)(object)val); } } catch (Exception e) { WarnOnce("GetTriangles", e); } } if (sub == 0) { return Il2CppArrayBase<int>.op_Implicit((Il2CppArrayBase<int>)(object)m.triangles); } return null; } public static bool TryGetSubMesh(Mesh m, int i, out SubMeshInfo info) { info = default(SubMeshInfo); MethodInfo methodInfo = FindMethod(typeof(Mesh), "GetSubMesh", 1); if (methodInfo == null) { return false; } try { object o = methodInfo.Invoke(m, new object[1] { i }); info.IndexStart = ReadIntMember(o, "indexStart"); info.IndexCount = ReadIntMember(o, "indexCount"); info.BaseVertex = ReadIntMember(o, "baseVertex"); info.FirstVertex = ReadIntMember(o, "firstVertex"); info.VertexCount = ReadIntMember(o, "vertexCount"); info.Topology = ReadIntMember(o, "topology"); return true; } catch (Exception e) { WarnOnce("GetSubMesh", e); return false; } } public static long GetIndexCount(Mesh m, int sub) { MethodInfo methodInfo = FindMethod(typeof(Mesh), "GetIndexCount", 1, (ParameterInfo[] ps) => ps[0].ParameterType == typeof(int), "int"); if (methodInfo == null) { return -1L; } try { return Convert.ToInt64(methodInfo.Invoke(m, new object[1] { sub })); } catch (Exception e) { WarnOnce("GetIndexCount", e); return -1L; } } private static object CallAttr(Mesh m, string name, int attr) { MethodInfo methodInfo = FindMethod(typeof(Mesh), name, 1); if (methodInfo == null) { return null; } try { return methodInfo.Invoke(m, new object[1] { EnumValue(methodInfo.GetParameters()[0].ParameterType, attr) }); } catch (Exception e) { WarnOnce(name, e); return null; } } public static bool TryGetAttribute(Mesh m, int attr, out int stream, out int offset, out int format, out int dim) { stream = (offset = (format = (dim = 0))); object obj = CallAttr(m, "HasVertexAttribute", attr); if (!(obj is bool) || !(bool)obj) { return false; } object obj2 = CallAttr(m, "GetVertexAttributeStream", attr); object obj3 = CallAttr(m, "GetVertexAttributeOffset", attr); object obj4 = CallAttr(m, "GetVertexAttributeFormat", attr); object obj5 = CallAttr(m, "GetVertexAttributeDimension", attr); if (obj2 == null || obj3 == null || obj4 == null || obj5 == null) { return false; } stream = Convert.ToInt32(obj2); offset = Convert.ToInt32(obj3); format = Convert.ToInt32(obj4); dim = Convert.ToInt32(obj5); return offset >= 0; } public static int GetVertexBufferStride(Mesh m, int stream) { MethodInfo methodInfo = FindMethod(typeof(Mesh), "GetVertexBufferStride", 1); if (methodInfo == null) { return 0; } try { return Convert.ToInt32(methodInfo.Invoke(m, new object[1] { stream })); } catch { return 0; } } public static bool IndexIs32Bit(Mesh m) { object obj = Get(m, "indexFormat"); if (obj != null) { return Convert.ToInt32(obj) == 1; } return false; } private static byte[] ReadBuffer(object gb, int byteOffset, int byteCount) { if (gb == null) { LastError = "버퍼를 받지 못함(null)"; return null; } if (byteCount <= 0) { LastError = "읽을 크기 0"; return null; } LastError = null; try { MethodInfo methodInfo = FindMethod(gb.GetType(), "InternalGetData", 5, (ParameterInfo[] ps) => ps[0].ParameterType.Name == "Array" && ps[4].ParameterType == typeof(int), "arr5"); Il2CppStructArray<byte> val = new Il2CppStructArray<byte>((long)byteCount); if (methodInfo != null) { object obj = Activator.CreateInstance(methodInfo.GetParameters()[0].ParameterType, ((Il2CppObjectBase)val).Pointer); methodInfo.Invoke(gb, new object[5] { obj, 0, byteOffset, byteCount, 1 }); } else { MethodInfo methodInfo2 = FindMethod(gb.GetType(), "GetData", 4, (ParameterInfo[] ps) => ps[0].ParameterType.Name == "Array" && ps[1].ParameterType == typeof(int), "arr"); if (methodInfo2 == null) { WarnOnce("GraphicsBuffer 읽기 함수 없음"); return null; } object obj2 = Activator.CreateInstance(methodInfo2.GetParameters()[0].ParameterType, ((Il2CppObjectBase)val).Pointer); methodInfo2.Invoke(gb, new object[4] { obj2, 0, byteOffset, byteCount }); } return Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)val); } catch (Exception ex) { LastError = (ex.InnerException ?? ex).Message; WarnOnce("GPU 버퍼 읽기", ex); return null; } finally { try { FindMethod(gb.GetType(), "Dispose", 0)?.Invoke(gb, null); } catch { } } } private static object GetBuffer(Mesh m, string implName, string wrapperName, object[] args) { int paramCount = ((args != null) ? args.Length : 0); MethodInfo methodInfo = FindMethod(typeof(Mesh), implName, paramCount) ?? FindMethod(typeof(Mesh), wrapperName, paramCount); if (methodInfo == null) { LastError = wrapperName + " 없음"; WarnOnce(wrapperName); return null; } try { return methodInfo.Invoke(m, args); } catch (Exception ex) { LastError = methodInfo.Name + ": " + (ex.InnerException ?? ex).Message; WarnOnce(methodInfo.Name, ex); return null; } } public static byte[] ReadVertexBytes(Mesh m, int stream, int byteOffset, int byteCount) { return ReadBuffer(GetBuffer(m, "GetVertexBufferImpl", "GetVertexBuffer", new object[1] { stream }), byteOffset, byteCount); } public static int[] ReadIndices(Mesh m, int indexStart, int count) { if (count <= 0) { return null; } bool flag = IndexIs32Bit(m); int num = (flag ? 4 : 2); byte[] array = ReadBuffer(GetBuffer(m, "GetIndexBufferImpl", "GetIndexBuffer", null), indexStart * num, count * num); if (array == null) { return null; } int[] array2 = new int[count]; for (int i = 0; i < count; i++) { array2[i] = (flag ? BitConverter.ToInt32(array, i * 4) : BitConverter.ToUInt16(array, i * 2)); } return array2; } } } namespace MapDestruction.Meshing { public sealed class ChunkedMesh { public sealed class Group { public Int3 Key; public List<int>[] Sub; public Box3 Box = Box3.Empty; public int TriangleCount { get { int num = 0; List<int>[] sub = Sub; foreach (List<int> list in sub) { num += list.Count / 3; } return num; } } } public readonly MeshData Src; private readonly int _subs; private readonly float _chunk; private readonly int _gc; private readonly float _group; private readonly Dictionary<Int3, Group> _groups = new Dictionary<Int3, Group>(); private readonly HashSet<Int3> _detached = new HashSet<Int3>(); private readonly Dictionary<(long, int, float), int> _splitCache = new Dictionary<(long, int, float), int>(); private readonly List<int> _below = new List<int>(16); private readonly List<int> _above = new List<int>(16); public IEnumerable<Group> Groups => _groups.Values; public int GroupCount => _groups.Count; public bool Overflow { get; private set; } public ChunkedMesh(MeshData src, float chunkSize, int groupChunks, int maxTriangles) { Src = src; _subs = src.Sub.Length; _chunk = chunkSize; _gc = groupChunks; _group = chunkSize * (float)groupChunks; int num = 0; for (int i = 0; i < _subs; i++) { List<int> list = src.Sub[i]; for (int j = 0; j + 2 < list.Count; j += 3) { List<int> poly = new List<int>(3) { list[j], list[j + 1], list[j + 2] }; num += SplitToGroups(i, poly, 0); if (num > maxTriangles) { Overflow = true; return; } } } } private static long EdgeKey(int a, int b) { if (a >= b) { return ((long)b << 32) | (uint)a; } return ((long)a << 32) | (uint)b; } private int SplitVertex(int a, int b, int axis, float v) { (long, int, float) key = (EdgeKey(a, b), axis, v); if (_splitCache.TryGetValue(key, out var value)) { return value; } int num = Math.Min(a, b); int num2 = Math.Max(a, b); float num3 = Src.Pos[num][axis]; float num4 = Src.Pos[num2][axis]; float t = MathUtil.Clamp01((v - num3) / (num4 - num3)); value = Src.LerpVertex(Src, num, num2, t); _splitCache[key] = value; return value; } private void SplitPoly(List<int> poly, int axis, float v, List<int> below, List<int> above) { below.Clear(); above.Clear(); int count = poly.Count; for (int i = 0; i < count; i++) { int num = poly[i]; int num2 = poly[(i + 1) % count]; float num3 = Src.Pos[num][axis] - v; float num4 = Src.Pos[num2][axis] - v; if (num3 <= 0f) { below.Add(num); } if (num3 >= 0f) { above.Add(num); } if ((num3 < 0f && num4 > 0f) || (num3 > 0f && num4 < 0f)) { int item = SplitVertex(num, num2, axis, v); below.Add(item); above.Add(item); } } } private Box3 PolyBox(List<int> poly) { Box3 empty = Box3.Empty; foreach (int item in poly) { empty.Encapsulate(Src.Pos[item]); } return empty; } private static void CellRange(float min, float max, float size, out int a, out int b) { float num = size * 0.0001f; a = (int)MathF.Floor((min + num) / size); b = (int)MathF.Floor((max - num) / size); if (b < a) { b = a; } } private static int Fan(List<int> poly, List<int> dst) { if (poly.Count < 3) { return 0; } for (int i = 1; i + 1 < poly.Count; i++) { dst.Add(poly[0]); dst.Add(poly[i]); dst.Add(poly[i + 1]); } return poly.Count - 2; } private int SplitToGroups(int sub, List<int> poly, int depth) { if (poly.Count < 3) { return 0; } Box3 b = PolyBox(poly); for (int i = 0; i < 3; i++) { if (depth >= 64) { break; } CellRange(b.Min[i], b.Max[i], _group, out var a, out var b2); if (b2 > a) { float v = (float)(a + (b2 - a + 1) / 2) * _group; SplitPoly(poly, i, v, _below, _above); List<int> poly2 = new List<int>(_below); List<int> poly3 = new List<int>(_above); return SplitToGroups(sub, poly2, depth + 1) + SplitToGroups(sub, poly3, depth + 1); } } CellRange(b.Min.x, b.Max.x, _group, out var a2, out var b3); CellRange(b.Min.y, b.Max.y, _group, out var a3, out b3); CellRange(b.Min.z, b.Max.z, _group, out var a4, out b3); Int3 key = new Int3(a2, a3, a4); if (!_groups.TryGetValue(key, out var value)) { value = new Group { Key = key, Sub = new List<int>[_subs] }; for (int j = 0; j < _subs; j++) { value.Sub[j] = new List<int>(); } _groups[key] = value; } value.Box.Encapsulate(b); return Fan(poly, value.Sub[sub]); } private Box3 ChunkBox(Int3 k) { return new Box3(new V3(k.X, k.Y, k.Z) * _chunk, new V3(k.X + 1, k.Y + 1, k.Z + 1) * _chunk); } public bool IsDetached(Int3 chunkKey) { return _detached.Contains(chunkKey); } public bool Covers(Int3 chunkKey) { if (_detached.Contains(chunkKey)) { return true; } if (!_groups.TryGetValue(Int3.FloorDiv(chunkKey, _gc), out var value)) { return false; } if (!value.Box.IsEmpty) { return value.Box.Expanded(_chunk * 0.01f).Intersects(ChunkBox(chunkKey)); } return false; } public void CollectChunks(V3 c, float r, List<Int3> outKeys) { Int3 @int = Int3.Floor((c - new V3(r, r, r)) / _chunk); Int3 int2 = Int3.Floor((c + new V3(r, r, r)) / _chunk); for (int i = @int.Z; i <= int2.Z; i++) { for (int j = @int.Y; j <= int2.Y; j++) { for (int k = @int.X; k <= int2.X; k++) { Int3 int3 = new Int3(k, j, i); if (ChunkBox(int3).IntersectsSphere(c, r) && Covers(int3)) { outKeys.Add(int3); } } } } } public MeshData Detach(Int3 chunkKey, out Group changed) { changed = null; if (!_groups.TryGetValue(Int3.FloorDiv(chunkKey, _gc), out var value)) { _detached.Add(chunkKey); return null; } if (!_detached.Add(chunkKey)) { return null; } Box3 o = ChunkBox(chunkKey); List<int>[] array = new List<int>[_subs]; int num = 0; List<int> list = new List<int>(16); List<int> list2 = new List<int>(16); List<int> list3 = new List<int>(16); for (int i = 0; i < _subs; i++) { array[i] = new List<int>(); List<int> list4 = value.Sub[i]; List<int> list5 = new List<int>(list4.Count); for (int j = 0; j + 2 < list4.Count; j += 3) { int num2 = list4[j]; int num3 = list4[j + 1]; int num4 = list4[j + 2]; if (!new Box3(V3.Min(V3.Min(Src.Pos[num2], Src.Pos[num3]), Src.Pos[num4]), V3.Max(V3.Max(Src.Pos[num2], Src.Pos[num3]), Src.Pos[num4])).Intersects(o)) { list5.Add(num2); list5.Add(num3); list5.Add(num4); continue; } list.Clear(); list.Add(num2); list.Add(num3); list.Add(num4); bool flag = false; for (int k = 0; k < 3; k++) { if (list.Count < 3) { break; } if (flag) { break; } int num5 = PlaneSide(list, k, o.Min[k]); if (num5 < 0) { flag = true; break; } if (num5 == 2) { SplitPoly(list, k, o.Min[k], _below, _above); list2.Clear(); list2.AddRange(_below); list3.Clear(); list3.AddRange(_above); if (Area2(list2) > 1E-12f) { Fan(list2, list5); } list.Clear(); list.AddRange(list3); if (list.Count < 3) { break; } } num5 = PlaneSide(list, k, o.Max[k]); if (num5 >= 0 && num5 != 2) { flag = true; break; } if (num5 == 2) { SplitPoly(list, k, o.Max[k], _below, _above); list2.Clear(); list2.AddRange(_above); list3.Clear(); list3.AddRange(_below); if (Area2(list2) > 1E-12f) { Fan(list2, list5); } list.Clear(); list.AddRange(list3); } } if (flag) { if (Area2(list) > 1E-12f) { Fan(list, list5); } } else if (list.Count >= 3 && Area2(list) > 1E-12f) { num += Fan(list, array[i]); } } value.Sub[i] = list5; } value.Box = Box3.Empty; List<int>[] sub = value.Sub; for (int l = 0; l < sub.Length; l++) { foreach (int item in sub[l]) { value.Box.Encapsulate(Src.Pos[item]); } } changed = value; if (num <= 0) { return null; } return Src.Extract(array); } public MeshData SplitOff(Func<V3, V3, bool> pred, Box3 region, List<Group> changed) { List<int>[] array = new List<int>[_subs]; for (int i = 0; i < _subs; i++) { array[i] = new List<int>(); } bool flag = false; foreach (Group value in _groups.Values) { if (value.Box.IsEmpty || !value.Box.Intersects(region)) { continue; } bool flag2 = false; for (int j = 0; j < _subs; j++) { List<int> list = value.Sub[j]; List<int> list2 = new List<int>(list.Count); for (int k = 0; k + 2 < list.Count; k += 3) { int num = list[k]; int num2 = list[k + 1]; int num3 = list[k + 2]; V3 v = Src.Pos[num]; V3 v2 = Src.Pos[num2]; V3 v3 = Src.Pos[num3]; if (pred((v + v2 + v3) / 3f, V3.Cross(v2 - v, v3 - v).Normalized)) { array[j].Add(num); array[j].Add(num2); array[j].Add(num3); flag = true; flag2 = true; } else { list2.Add(num); list2.Add(num2); list2.Add(num3); } } value.Sub[j] = list2; } if (!flag2) { continue; } value.Box = Box3.Empty; List<int>[] sub = value.Sub; for (int l = 0; l < sub.Length; l++) { foreach (int item in sub[l]) { value.Box.Encapsulate(Src.Pos[item]); } } changed.Add(value); } if (!flag) { return null; } return Src.Extract(array); } private int PlaneSide(List<int> poly, int axis, float v) { float num = _chunk * 1E-05f; bool flag = false; bool flag2 = false; foreach (int item in poly) { float num2 = Src.Pos[item][axis] - v; if (num2 < 0f - num) { flag = true; } else if (num2 > num) { flag2 = true; } } if (flag && flag2) { return 2; } if (flag) { return -1; } return flag2 ? 1 : 0; } private float Area2(List<int> poly) { if (poly.Count < 3) { return 0f; } V3 zero = V3.Zero; V3 v = Src.Pos[poly[0]]; for (int i = 1; i + 1 < poly.Count; i++) { zero += V3.Cross(Src.Pos[poly[i]] - v, Src.Pos[poly[i + 1]] - v); } return zero.SqrMag; } } public static class CraterMesher { public sealed class Result { public MeshData Render; public readonly List<byte> SubPalette = new List<byte>(); public readonly List<V3> ColPos = new List<V3>(); public readonly List<int> ColIdx = new List<int>(); public bool IsEmpty { get { if (Render == null || Render.TriangleCount == 0) { return ColIdx.Count == 0; } return false; } } } private const int N = 16; private const int G = 18; private const int CG = 17; private const float MinErosion = 0.004f; private const float BiasMax = 0.015f; private static readonly float[] _f = new float[5832]; private static readonly float[] _e = new float[5832]; private static readonly float[] _ed = new float[5832]; private static readonly byte[] _m = new byte[5832]; private static readonly int[] _cellVert = new int[4913]; private static readonly int[] _cellColVert = new int[4913]; private static readonly List<bool> _border = new List<bool>(); private const float CreaseCos = 0.707f; private const float ReliefStart = 0.03f; private const float ReliefFull = 0.08f; private static readonly float[] _cf = new float[8]; private static int PI(int x, int y, int z) { return x + 1 + 18 * (y + 1 + 18 * (z + 1)); } private static int CI(int x, int y, int z) { return x + 1 + 17 * (y + 1 + 17 * (z + 1)); } public static Result Build(VoxelChunk ch, float rimEps, float uvScale, bool wantCollision) { if (!ch.Voxelized || !ch.HasErosion) { return null; } float cell = ch.Cell; int num = ch.Key.X * 16; int num2 = ch.Key.Y * 16; int num3 = ch.Key.Z * 16; bool flag = false; for (int i = -1; i <= 16; i++) { for (int j = -1; j <= 16; j++) { for (int k = -1; k <= 16; k++) { int num4 = PI(k, j, i); float num5 = VoxelWorld.FieldAtGlobal(num + k, num2 + j, num3 + i, out _e[num4], out _m[num4]); _f[num4] = num5 + 0.015f; if (_e[num4] > 0.004f) { flag = true; } } } } if (!flag) { return null; } for (int l = -1; l <= 16; l++) { for (int m = -1; m <= 16; m++) { for (int n = -1; n <= 16; n++) { float num6 = 0f; for (int num7 = -1; num7 <= 1; num7++) { for (int num8 = -1; num8 <= 1; num8++) { for (int num9 = -1; num9 <= 1; num9++) { int num10 = n + num9; int num11 = m + num8; int num12 = l + num7; if (num10 >= -1 && num11 >= -1 && num12 >= -1 && num10 <= 16 && num11 <= 16 && num12 <= 16) { float num13 = _e[PI(num10, num11, num12)]; if (num13 > num6) { num6 = num13; } } } } } _ed[PI(n, m, l)] = num6; } } } for (int num14 = 0; num14 < _cellVert.Length; num14++) { _cellVert[num14] = -1; _cellColVert[num14] = -1; } _border.Clear(); Result result = new Result(); MeshData meshData = new MeshData(0, nrm: true, tan: true, uv0: true, uv1: false, col: false); meshData.Sub = new List<int>[0]; List<List<int>> list = new List<List<int>>(); Dictionary<byte, int> dictionary = new Dictionary<byte, int>(); float num15 = 0.004f; for (int num16 = 0; num16 < 3; num16++) { int num17 = (num16 + 1) % 3; int num18 = (num16 + 2) % 3; for (int num19 = 0; num19 < 16; num19++) { for (int num20 = 0; num20 < 16; num20++) { for (int num21 = 0; num21 < 16; num21++) { int x = num21 + ((num16 == 0) ? 1 : 0); int y = num20 + ((num16 == 1) ? 1 : 0); int z = num19 + ((num16 == 2) ? 1 : 0); int num22 = PI(num21, num20, num19); int num23 = PI(x, y, z); float num24 = _f[num22]; float num25 = _f[num23]; bool flag2 = num24 < 0f; bool flag3 = num25 < 0f; if (flag2 == flag3 || MathF.Max(_ed[num22], _ed[num23]) <= num15) { continue; } int[] array = new int[3] { num21, num20, num19 }; int[] array2 = new int[3] { array[0], array[1], array[2] }; array2[num17]--; array2[num18]--; int[] array3 = new int[3] { array[0], array[1], array[2] }; array3[num18]--; int[] cmin = new int[3] { array[0], array[1], array[2] }; int[] array4 = new int[3] { array[0], array[1], array[2] }; array4[num17]--; byte b = (flag2 ? _m[num22] : _m[num23]); PaletteEntry paletteEntry = MaterialPalette.Get(b); if (paletteEntry != null && (Object)(object)paletteEntry.RenderMaterial != (Object)null) { MaterialProfile prof = MaterialPalette.ProfileOf(b); int num26 = CellVertex(meshData, ch, array2, cell, uvScale, prof); int num27 = CellVertex(meshData, ch, array3, cell, uvScale, prof); int num28 = CellVertex(meshData, ch, cmin, cell, uvScale, prof); int num29 = CellVertex(meshData, ch, array4, cell, uvScale, prof); if (num26 >= 0 && num27 >= 0 && num28 >= 0 && num29 >= 0) { if (!dictionary.TryGetValue(b, out var value)) { value = list.Count; list.Add(new List<int>()); dictionary[b] = value; result.SubPalette.Add(b); } AddQuadShort(meshData, list[value], num26, num27, num28, num29, flag2); } } if (wantCollision) { int num30 = CellColVertex(result, ch, array2, cell); int num31 = CellColVertex(result, ch, array3, cell); int num32 = CellColVertex(result, ch, cmin, cell); int num33 = CellColVertex(result, ch, array4, cell); if (num30 >= 0 && num31 >= 0 && num32 >= 0 && num33 >= 0) { AddQuad(result.ColIdx, num30, num31, num32, num33, flag2); } } } } } } meshData.Sub = list.ToArray(); Facet(meshData); ProjectUVs(meshData, uvScale); result.Render = meshData; return result; } private static void Facet(MeshData md) { int count = md.Pos.Count; if (count == 0) { return; } int num = 0; List<int>[] sub = md.Sub; foreach (List<int> list in sub) { num += list.Count / 3; } if (num == 0) { return; } V3[] array = new V3[num]; int[] array2 = new int[count + 1]; int num2 = 0; sub = md.Sub; foreach (List<int> list2 in sub) { int num3 = 0; while (num3 + 2 < list2.Count) { V3 v = md.Pos[list2[num3]]; V3 v2 = md.Pos[list2[num3 + 1]]; V3 v3 = md.Pos[list2[num3 + 2]]; array[num2] = V3.Cross(v2 - v, v3 - v); array2[list2[num3]]++; array2[list2[num3 + 1]]++; array2[list2[num3 + 2]]++; num3 += 3; num2++; } } int[] array3 = new int[count + 1]; for (int j = 0; j < count; j++) { array3[j + 1] = array3[j] + array2[j]; } int[] array4 = new int[count]; int[] array5 = new int[array3[count]]; for (int k = 0; k < md.Sub.Length; k++) { List<int> list3 = md.Sub[k]; for (int l = 0; l < list3.Count; l++) { int num4 = list3[l]; array5[array3[num4] + array4[num4]++] = (k << 24) | l; } } int[] array6 = new int[md.Sub.Length]; for (int m = 1; m < md.Sub.Length; m++) { array6[m] = array6[m - 1] + md.Sub[m - 1].Count / 3; } List<(V3, int)> list4 = new List<(V3, int)>(4); for (int n = 0; n < count; n++) { if (n < _border.Count && _border[n]) { continue; } list4.Clear(); for (int num5 = array3[n]; num5 < array3[n + 1]; num5++) { int num6 = array5[num5] >> 24; int num7 = array5[num5] & 0xFFFFFF; V3 v4 = array[array6[num6] + num7 / 3]; float mag = v4.Mag; if (mag < 1E-12f) { continue; } V3 b = v4 / mag; int num8 = -1; for (int num9 = 0; num9 < list4.Count; num9++) { if (V3.Dot(list4[num9].Item1.Normalized, b) >= 0.707f) { num8 = num9; break; } } if (num8 < 0) { int item = n; if (list4.Count > 0) { md.Pos.Add(md.Pos[n]); md.Nrm.Add(md.Nrm[n]); md.Tan.Add(md.Tan[n]); md.Uv0.Add(md.Uv0[n]); item = md.Pos.Count - 1; } list4.Add((v4, item)); num8 = list4.Count - 1; } else { list4[num8] = (list4[num8].Item1 + v4, list4[num8].Item2); } md.Sub[num6][num7] = list4[num8].Item2; } foreach (var item4 in list4) { V3 item2 = item4.Item1; int item3 = item4.Item2; V3 normalized = item2.Normalized; if (!(normalized.SqrMag < 0.5f)) { md.Nrm[item3] = normalized; V4 v5 = md.Tan[item3]; V3 v6 = new V3(v5.x, v5.y, v5.z); v6 = (v6 - normalized * V3.Dot(normalized, v6)).Normalized; if (v6.SqrMag < 0.5f) { v6 = V3.Cross(normalized, (MathF.Abs(normalized.y) < 0.9f) ? new V3(0f, 1f, 0f) : new V3(1f, 0f, 0f)).Normalized; } md.Tan[item3] = new V4(v6.x, v6.y, v6.z, v5.w); } } } } private static void AddQuad(List<int> l, int v00, int v10, int v11, int v01, bool in0) { if (in0) { l.Add(v00); l.Add(v10); l.Add(v11); l.Add(v00); l.Add(v11); l.Add(v01); } else { l.Add(v00); l.Add(v11); l.Add(v10); l.Add(v00); l.Add(v01); l.Add(v11); } } private static void AddQuadShort(MeshData md, List<int> l, int v00, int v10, int v11, int v01, bool in0) { float sqrMag = (md.Pos[v00] - md.Pos[v11]).SqrMag; float sqrMag2 = (md.Pos[v10] - md.Pos[v01]).SqrMag; if (sqrMag <= sqrMag2) { AddQuad(l, v00, v10, v11, v01, in0); } else if (in0) { l.Add(v10); l.Add(v11); l.Add(v01); l.Add(v10); l.Add(v01); l.Add(v00); } else { l.Add(v10); l.Add(v01); l.Add(v11); l.Add(v10); l.Add(v00); l.Add(v01); } } private static void ProjectUVs(MeshData md, float uvScale) { int count = md.Pos.Count; if (count == 0) { return; } sbyte[] array = new sbyte[count]; for (int i = 0; i < count; i++) { array[i] = -1; } Dictionary<long, int> dictionary = new Dictionary<long, int>(); List<int>[] sub = md.Sub; foreach (List<int> list in sub) { for (int k = 0; k + 2 < list.Count; k += 3) { V3 v = md.Pos[list[k]]; V3 v2 = md.Pos[list[k + 1]]; V3 v3 = V3.Cross(b: md.Pos[list[k + 2]] - v, a: v2 - v); if (v3.SqrMag < 1E-14f) { v3 = md.Nrm[list[k]]; } v3 = v3.Normalized; int num = BestAxis(v3); float num2 = MathF.Abs(v3[num >> 1]); int num3 = array[list[k]]; if (num3 >= 0 && num3 != num && AxisDot(v3, num3) >= num2 * 0.75f) { num = num3; } for (int l = 0; l < 3; l++) { int num4 = list[k + l]; if (array[num4] < 0) { array[num4] = (sbyte)num; SetUv(md, num4, num, uvScale); } else if (array[num4] != num) { long key = (long)num4 * 8L + num; if (!dictionary.TryGetValue(key, out var value)) { md.Pos.Add(md.Pos[num4]); md.Nrm.Add(md.Nrm[num4]); md.Tan.Add(md.Tan[num4]); md.Uv0.Add(md.Uv0[num4]); value = md.Pos.Count - 1; SetUv(md, value, num, uvScale); dictionary[key] = value; } list[k + l] = value; } } } } } private static int BestAxis(V3 n) { float num = MathF.Abs(n.x); float num2 = MathF.Abs(n.y); float num3 = MathF.Abs(n.z); if (num >= num2 && num >= num3) { return (!(n.x >= 0f)) ? 1 : 0; } if (num2 >= num3) { if (!(n.y >= 0f)) { return 3; } return 2; } if (!(n.z >= 0f)) { return 5; } return 4; } private static float AxisDot(V3 n, int axis) { float num = n[axis >> 1]; if ((axis & 1) != 0) { return 0f - num; } return num; } private static void SetUv(MeshData md, int v, int axis, float uvScale) { V3 v2; V3 v3; switch (axis >> 1) { case 0: v2 = new V3(0f, 0f, ((axis & 1) != 0) ? 1 : (-1)); v3 = new V3(0f, 1f, 0f); break; case 1: v2 = new V3(1f, 0f, 0f); v3 = new V3(0f, 0f, ((axis & 1) == 0) ? 1 : (-1)); break; default: v2 = new V3(((axis & 1) == 0) ? 1 : (-1), 0f, 0f); v3 = new V3(0f, 1f, 0f); break; } V3 a = md.Pos[v]; V3 v4 = md.Nrm[v]; md.Uv0[v] = new V2(V3.Dot(a, v2) * uvScale, V3.Dot(a, v3) * uvScale); V3 normalized = (v2 - v4 * V3.Dot(v4, v2)).Normalized; if (normalized.SqrMag < 0.5f) { normalized = (v3 - v4 * V3.Dot(v4, v3)).Normalized; } float w = ((V3.Dot(V3.Cross(v4, normalized), v3) >= 0f) ? 1f : (-1f)); md.Tan[v] = new V4(normalized.x, normalized.y, normalized.z, w); } private static void AddVertex(MeshData md, V3 pos, V3 n, float uvScale) { float num = MathF.Abs(n.x); float num2 = MathF.Abs(n.y); float num3 = MathF.Abs(n.z); V3 v; V3 b; if (num >= num2 && num >= num3) { v = new V3(0f, 0f, (!(n.x >= 0f)) ? 1 : (-1)); b = new V3(0f, 1f, 0f); } else if (num2 >= num3) { v = new V3(1f, 0f, 0f); b = new V3(0f, 0f, (n.y >= 0f) ? 1 : (-1)); } else { v = new V3((n.z >= 0f) ? 1 : (-1), 0f, 0f); b = new V3(0f, 1f, 0f); } V2 item = new V2(V3.Dot(pos, v) * uvScale, V3.Dot(pos, b) * uvScale); V3 normalized = (v - n * V3.Dot(n, v)).Normalized; float w = ((V3.Dot(V3.Cross(n, normalized), b) >= 0f) ? 1f : (-1f)); md.Pos.Add(pos); md.Nrm.Add(n); md.Tan.Add(new V4(normalized.x, normalized.y, normalized.z, w)); md.Uv0.Add(item); } private static bool CellCorners(int[] cmin, float[] cf) { for (int i = 0; i < 8; i++) { cf[i] = _f[PI(cmin[0] + (i & 1), cmin[1] + ((i >> 1) & 1), cmin[2] + ((i >> 2) & 1))]; } return true; } private static bool CellSurfacePoint(int[] cmin, out V3 local) { local = V3.Zero; if (cmin[0] < -1 || cmin[1] < -1 || cmin[2] < -1 || cmin[0] > 15 || cmin[1] > 15 || cmin[2] > 15) { return false; } CellCorners(cmin, _cf); int num = 0; V3 zero = V3.Zero; for (int i = 0; i < 8; i++) { for (int num2 = 1; num2 <= 4; num2 <<= 1) { if ((i & num2) == 0) { int num3 = i | num2; float num4 = _cf[i]; float num5 = _cf[num3]; if (num4 < 0f != num5 < 0f) { float t = num4 / (num4 - num5); V3 a = new V3(i & 1, (i >> 1) & 1, (i >> 2) & 1); V3 b = new V3(num3 & 1, (num3 >> 1) & 1, (num3 >> 2) & 1); zero += V3.Lerp(a, b, t); num++; } } } } if (num == 0) { return false; } local = zero / num; return true; } private static V3 Gradient(V3 u) { float x = u.x; float y = u.y; float z = u.z; float[] cf = _cf; float x2 = (1f - y) * (1f - z) * (cf[1] - cf[0]) + y * (1f - z) * (cf[3] - cf[2]) + (1f - y) * z * (cf[5] - cf[4]) + y * z * (cf[7] - cf[6]); float y2 = (1f - x) * (1f - z) * (cf[2] - cf[0]) + x * (1f - z) * (cf[3] - cf[1]) + (1f - x) * z * (cf[6] - cf[4]) + x * z * (cf[7] - cf[5]); float z2 = (1f - x) * (1f - y) * (cf[4] - cf[0]) + x * (1f - y) * (cf[5] - cf[1]) + (1f - x) * y * (cf[6] - cf[2]) + x * y * (cf[7] - cf[3]); V3 normalized = new V3(x2, y2, z2).Normalized; if (!(normalized.SqrMag > 0.5f)) { return new V3(0f, 1f, 0f); } return normalized; } private static int CellVertex(MeshData md, VoxelChunk ch, int[] cmin, float cell, float uvScale, MaterialProfile prof) { int num = CI(cmin[0], cmin[1], cmin[2]); if (num < 0 || num >= _cellVert.Length) { return -1; } if (_cellVert[num] >= 0) { return _cellVert[num]; } if (!CellSurfacePoint(cmin, out var local)) { return -1; } V3 v = Gradient(local); V3 v2 = ch.Origin + new V3((float)cmin[0] + local.x, (float)cmin[1] + local.y, (float)cmin[2] + local.z) * cell; if (prof != null && prof.Rough > 0f) { float num2 = MathUtil.Clamp01((0f - VoxelWorld.SampleSdf0(v2, 0f) - 0.03f) / 0.049999997f); if (num2 > 0f) { v2 += v * (Fracture.Relief(v2, prof, V3.Zero) * num2); } } bool item = cmin[0] <= -1 || cmin[1] <= -1 || cmin[2] <= -1 || cmin[0] >= 15 || cmin[1] >= 15 || cmin[2] >= 15; AddVertex(md, v2, v, uvScale); _border.Add(item); int num3 = md.Pos.Count - 1; _cellVert[num] = num3; return num3; } private static int CellColVertex(Result res, VoxelChunk ch, int[] cmin, float cell) { int num = CI(cmin[0], cmin[1], cmin[2]); if (num < 0 || num >= _cellColVert.Length) { return -1; } if (_cellColVert[num] >= 0) { return _cellColVert[num]; } if (!CellSurfacePoint(cmin, out var local)) { return -1; } res.ColPos.Add(ch.Origin + new V3((float)cmin[0] + local.x, (float)cmin[1] + local.y, (float)cmin[2] + local.z) * cell); int num2 = res.ColPos.Count - 1; _cellColVert[num] = num2; return num2; } } public sealed class MeshData { public readonly List<V3> Pos = new List<V3>(); public readonly List<V3> Nrm; public readonly List<V4> Tan; public readonly List<V2> Uv0; public readonly List<V2> Uv1; public readonly List<C4> Col; public List<int>[] Sub; public bool HasNrm => Nrm != null; public bool HasTan => Tan != null; public bool HasUv0 => Uv0 != null; public bool HasUv1 => Uv1 != null; public bool HasCol => Col != null; public int VertexCount => Pos.Count; public int TriangleCount { get { int num = 0; List<int>[] sub = Sub; foreach (Lis