public new void DisposeBillboardBuffers() { if (!MainThreadHelper.IsMainThread && ( (billboardVertices != null && !billboardVertices.IsDisposed) || (billboardIndices != null && !billboardIndices.IsDisposed) )) { // This is a disposal, there's no need to wait for this to be disposed, // let following reset calls realloc even before the olds get disposed. VertexBuffer billboardVerticesOld = billboardVertices; IndexBuffer billboardIndicesOld = billboardIndices; billboardVertices = null; billboardIndices = null; MainThreadHelper.Do(() => { if (billboardVerticesOld != null && !billboardVerticesOld.IsDisposed) { billboardVerticesOld.Dispose(); } if (billboardIndicesOld != null && !billboardIndicesOld.IsDisposed) { billboardIndicesOld.Dispose(); } }); return; } if (billboardVertices != null && !billboardVertices.IsDisposed) { billboardVertices.Dispose(); } if (billboardIndices != null && !billboardIndices.IsDisposed) { billboardIndices.Dispose(); } }
public void Exit() { task = null; Lines.Clear(); MainThreadHelper.Do(() => ((patch_OuiMainMenu)Overworld.GetUI <OuiMainMenu>())?.RebuildMainAndTitle()); Audio.Play(SFX.ui_main_button_back); Overworld.Goto <OuiModOptions>(); }
internal bool Load(bool wait, Func <Texture2D> load) { if (LoadImmediately) { Texture_Unsafe?.Dispose(); Texture_Unsafe = load(); FreeFastTextureLoad(); return(true); } // Let's queue a reload onto the main thread and call it a day. // Make sure to read the texture size immediately though! object queuedLoadLock; lock (queuedLoadLock = new object()) { _Texture_QueuedLoadLock = queuedLoadLock; Func <Texture2D> _load = load; load = () => { Texture2D tex; lock (queuedLoadLock) { if (_Texture_QueuedLoadLock != queuedLoadLock) { _load = null; FreeFastTextureLoad(); return(Texture_Unsafe); } // NOTE: If something dares to change texture info on the fly, GOOD LUCK. Texture_Unsafe?.Dispose(); Texture_Unsafe = tex = _load(); FreeFastTextureLoad(); _Texture_QueuedLoadLock = null; } if (_Texture_UnloadAfterReload) { tex?.Dispose(); tex = Texture_Unsafe; // ... can anything even swap the texture here? Texture_Unsafe = null; tex?.Dispose(); _Texture_UnloadAfterReload = false; } return(tex); }; _Texture_QueuedLoad = !_Texture_FTLLoading? MainThreadHelper.GetForceQueue(load) : MainThreadHelper.Get(load); } if (wait || _Texture_Requesting) { _Texture_QueuedLoad.GetResult(); } return(false); }
public static void Init() { var obj = GameObject.Find("MainThreadHelper"); if (obj == null) { helper = (new GameObject("MainThreadHelper")).AddComponent <MainThreadHelper>(); } }
internal override void Reload() { _Texture_Reloading = true; if (Metadata == null) { orig_Reload(); _Texture_Reloading = false; return; } Unload(); Texture = null; Stream stream = Metadata.Stream; if (stream != null) { bool premul = false; // Assume unpremultiplied by default. if (Metadata.TryGetMeta(out TextureMeta meta)) { premul = meta.Premultiplied; } using (stream) { if (premul) { Texture = MainThreadHelper.Get(() => Texture2D.FromStream(Celeste.Celeste.Instance.GraphicsDevice, stream)).GetResult(); } else { ContentExtensions.LoadTextureLazyPremultiply(Celeste.Celeste.Instance.GraphicsDevice, stream, out int w, out int h, out byte[] data); Texture = MainThreadHelper.Get(() => { Texture2D tex = new Texture2D(Celeste.Celeste.Instance.GraphicsDevice, w, h, false, SurfaceFormat.Color); tex.SetData(data); return(tex); }).GetResult(); } } } else if (Fallback != null) { ((patch_VirtualTexture)(object)Fallback).Reload(); Texture = Fallback.Texture; } if (Texture != null) { Width = Texture.Width; Height = Texture.Height; } _Texture_Reloading = false; }
public MainThreadHelper() { if (_instance == null) { _instance = this; } else { Debug.LogError("There should be only one instance"); } }
private void LoadThread() { MInput.Disabled = true; MainThreadHelper.Boost = 25; Stopwatch timer = Stopwatch.StartNew(); Audio.Init(); // Original code loads audio banks here. Settings.Instance.ApplyVolumes(); audioLoaded = true; Console.WriteLine(" - AUDIO LOAD: " + timer.ElapsedMilliseconds + "ms"); timer.Stop(); GFX.Load(); MTN.Load(); GFX.LoadData(); MTN.LoadData(); timer = Stopwatch.StartNew(); Fonts.Prepare(); Dialog.Load(); Fonts.Load(Dialog.Languages["english"].FontFace); Fonts.Load(Dialog.Languages[Settings.Instance.Language].FontFace); dialogLoaded = true; Console.WriteLine(" - DIA/FONT LOAD: " + timer.ElapsedMilliseconds + "ms"); timer.Stop(); MInput.Disabled = false; timer = Stopwatch.StartNew(); AreaData.Load(); Console.WriteLine(" - LEVELS LOAD: " + timer.ElapsedMilliseconds + "ms"); timer.Stop(); timer = Stopwatch.StartNew(); MainThreadHelper.Boost = 50; patch_VirtualTexture.WaitFinishFastTextureLoading(); MainThreadHelper.Get(() => MainThreadHelper.Boost = 0).GetResult(); // FIXME: There could be ongoing tasks which add to the main thread queue right here. Console.WriteLine(" - FASTTEXTURELOADING LOAD: " + timer.ElapsedMilliseconds + "ms"); timer.Stop(); Console.WriteLine("DONE LOADING (in " + Celeste.LoadTimer.ElapsedMilliseconds + "ms)"); Celeste.LoadTimer.Stop(); Celeste.LoadTimer = null; loaded = true; }
private void Awake() { Directory.CreateDirectory(performanceDataDir); if (finished) { return; } int numTests = Enum.GetNames(typeof(Tests)).Length; perfData = new PerfData[numTests]; shader = FindObjectOfType <PMBShader>().shaderRef; pmb = new PMB(shader); globalStopwatch = Stopwatch.StartNew(); helper = FindObjectOfType <MainThreadHelper>(); Noise n = new PerlinNoise(42, 5f); UnityEngine.Debug.Log("Starting Task"); Task.Factory.StartNew(() => { if (finished) { return; } var checkerboard = genericTest(Tests.Checkerboard, "checkerboard", 2, 5, (s, x, y, z) => (x + y + z) % 2); while (checkerboard.MoveNext()) { ; } var empty = genericTest(Tests.Empty, "empty", 5, 6, (s, x, y, z) => 0); while (empty.MoveNext()) { ; } var simpleNoise = genericTest(Tests.SimpleNoise, "simple-noise", 5, 5, (s, x, y, z) => n.Sample3D(x, y, z)); while (simpleNoise.MoveNext()) { ; } UnityEngine.Debug.Log("finish testing"); finishTesting(); }); }
internal override void Unload() { Texture2D tex = Texture_Unsafe; // Handle already queued loads appropriately. object queuedLoadLock = _Texture_QueuedLoadLock; if (queuedLoadLock != null) { bool gotLock = false; try { Monitor.TryEnter(queuedLoadLock, ref gotLock); if (gotLock) { if (_Texture_QueuedLoadLock != null) { // If we still can, cancel the queued load. _Texture_QueuedLoadLock = null; } } else { // Welp. _Texture_UnloadAfterReload = true; Monitor.TryEnter(queuedLoadLock, ref gotLock); if (gotLock) { // It might be too late - let's unload ourselves. _Texture_UnloadAfterReload = false; } else { // The loader will still handle the request. return; } } } finally { if (gotLock) { Monitor.Exit(queuedLoadLock); } } if (!MainThreadHelper.IsMainThread) { // Otherwise wait for it to get loaded. (Don't wait locked!) while (!_Texture_QueuedLoad.IsValid) { Thread.Yield(); } tex = _Texture_QueuedLoad.GetResult(); } } Texture_Unsafe = null; if (tex == null || tex.IsDisposed) { return; } if (!(CoreModule.Settings.ThreadedGL ?? Everest.Flags.PreferThreadedGL) && !MainThreadHelper.IsMainThread) { MainThreadHelper.Do(() => tex.Dispose()); } else { tex.Dispose(); } }
protected override void addOptionsToMenu(TextMenu menu) { // for now, display a "loading" message. TextMenu.Button loading = new TextMenu.Button(Dialog.Clean("MODOPTIONS_MODTOGGLE_LOADING")) { Disabled = true }; menu.Add(loading); modLoadingTask = new Task(() => { // load all the mod yamls (that can take some time), update the progress every 500ms so that the text doesn't go crazy since it is centered. Stopwatch updateTimer = Stopwatch.StartNew(); modYamls = LoadAllModYamls(progress => { if (updateTimer.ElapsedMilliseconds > 500) { updateTimer.Restart(); loading.Label = $"{Dialog.Clean("MODOPTIONS_MODTOGGLE_LOADING")} ({(int) (progress * 100)}%)"; } }); updateTimer.Stop(); MainThreadHelper.Do(() => { modToggles = new Dictionary <string, TextMenu.OnOff>(); // remove the "loading..." message menu.Remove(loading); // if there is a whitelist, warn the user that it will break those settings. if (Everest.Loader.Whitelist != null) { menu.Add(restartMessage1 = new TextMenuExt.SubHeaderExt(Dialog.Clean("MODOPTIONS_MODTOGGLE_WHITELISTWARN")) { TextColor = Color.OrangeRed }); } // display the warning about blacklist.txt + restarting menu.Add(restartMessage1 = new TextMenuExt.SubHeaderExt(Dialog.Clean("MODOPTIONS_MODTOGGLE_MESSAGE_1"))); menu.Add(restartMessage2 = new TextMenuExt.SubHeaderExt(Dialog.Clean("MODOPTIONS_MODTOGGLE_MESSAGE_2")) { HeightExtra = 0f }); menu.Add(new TextMenuExt.SubHeaderExt(Dialog.Clean("MODOPTIONS_MODTOGGLE_MESSAGE_3")) { HeightExtra = 20f, TextColor = Color.Goldenrod }); // reduce spacing between the whitelist warning and the blacklist overwrite warning if (Everest.Loader.Whitelist != null) { restartMessage1.HeightExtra = 30f; } // "enable all" and "disable all" buttons menu.Add(new TextMenu.Button(Dialog.Clean("MODOPTIONS_MODTOGGLE_ENABLEALL")).Pressed(() => { foreach (TextMenu.OnOff toggle in modToggles.Values) { toggle.Index = 1; } blacklistedMods.Clear(); updateHighlightedMods(); })); menu.Add(new TextMenu.Button(Dialog.Clean("MODOPTIONS_MODTOGGLE_DISABLEALL")).Pressed(() => { blacklistedMods.Clear(); foreach (KeyValuePair <string, TextMenu.OnOff> toggle in modToggles) { toggle.Value.Index = 0; blacklistedMods.Add(toggle.Key); } updateHighlightedMods(); })); // "toggle dependencies automatically" button TextMenu.Item toggleDependenciesButton; menu.Add(toggleDependenciesButton = new TextMenu.OnOff(Dialog.Clean("MODOPTIONS_MODTOGGLE_TOGGLEDEPS"), true) .Change(value => toggleDependencies = value)); toggleDependenciesButton.AddDescription(menu, Dialog.Clean("MODOPTIONS_MODTOGGLE_TOGGLEDEPS_MESSAGE2")); toggleDependenciesButton.AddDescription(menu, Dialog.Clean("MODOPTIONS_MODTOGGLE_TOGGLEDEPS_MESSAGE1")); // "cancel" button to leave the screen without saving menu.Add(new TextMenu.Button(Dialog.Clean("MODOPTIONS_MODTOGGLE_CANCEL")).Pressed(() => { blacklistedMods = blacklistedModsOriginal; onBackPressed(Overworld); })); // reset the mods list allMods = new List <string>(); blacklistedMods = new HashSet <string>(); string[] files; bool headerInserted; // crawl directories files = Directory.GetDirectories(Everest.Loader.PathMods); Array.Sort(files, (a, b) => a.ToLowerInvariant().CompareTo(b.ToLowerInvariant())); headerInserted = false; for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (file != "Cache") { if (!headerInserted) { menu.Add(new patch_TextMenu.patch_SubHeader(Dialog.Clean("MODOPTIONS_MODTOGGLE_DIRECTORIES"))); headerInserted = true; } addFileToMenu(menu, file); } } // crawl zips files = Directory.GetFiles(Everest.Loader.PathMods); Array.Sort(files, (a, b) => a.ToLowerInvariant().CompareTo(b.ToLowerInvariant())); headerInserted = false; for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (file.EndsWith(".zip")) { if (!headerInserted) { menu.Add(new patch_TextMenu.patch_SubHeader(Dialog.Clean("MODOPTIONS_MODTOGGLE_ZIPS"))); headerInserted = true; } addFileToMenu(menu, file); } } // crawl map bins files = Directory.GetFiles(Everest.Loader.PathMods); Array.Sort(files, (a, b) => a.ToLowerInvariant().CompareTo(b.ToLowerInvariant())); headerInserted = false; for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (file.EndsWith(".bin")) { if (!headerInserted) { menu.Add(new patch_TextMenu.patch_SubHeader(Dialog.Clean("MODOPTIONS_MODTOGGLE_BINS"))); headerInserted = true; } addFileToMenu(menu, file); } } // sort the mods list alphabetically, for output in the blacklist.txt file later. allMods.Sort((a, b) => a.ToLowerInvariant().CompareTo(b.ToLowerInvariant())); // adjust the mods' color if they are required dependencies for other mods foreach (KeyValuePair <string, TextMenu.OnOff> toggle in modToggles) { if (modHasDependencies(toggle.Key)) { ((patch_TextMenu.patch_Option <bool>)(object) toggle.Value).UnselectedColor = Color.Goldenrod; } } // snap the menu so that it doesn't show a scroll up. menu.Y = menu.ScrollTargetY; // clone the list to be able to check if the list changed when leaving the menu. blacklistedModsOriginal = new HashSet <string>(blacklistedMods); // loading is done! modLoadingTask = null; }); }); modLoadingTask.Start(); }
private bool ResetVertexBuffer() { // Checking for IsDisposed on other threads should be fine... if (Vertices != null && !Vertices.IsDisposed && !Vertices.GraphicsDevice.IsDisposed) { return(false); } // Handle already queued loads appropriately. object queuedLoadLock = _Vertices_QueuedLoadLock; if (queuedLoadLock != null) { lock (queuedLoadLock) { // Queued task finished just in time. if (_Vertices_QueuedLoadLock == null) { return(true); } // If we still can, cancel the queued load, then proceed with lazy-loading. if (MainThreadHelper.IsMainThread) { _Vertices_QueuedLoadLock = null; } } if (!MainThreadHelper.IsMainThread) { // Otherwise wait for it to get loaded, don't reload twice. (Don't wait locked!) while (!_Vertices_QueuedLoad.IsValid) { Thread.Yield(); } _Vertices_QueuedLoad.GetResult(); return(true); } } if (!(CoreModule.Settings.ThreadedGL ?? Everest.Flags.PreferThreadedGL) && !MainThreadHelper.IsMainThread && queuedLoadLock == null) { // Let's queue a reload onto the main thread and call it a day. lock (queuedLoadLock = new object()) { _Vertices_QueuedLoadLock = queuedLoadLock; _Vertices_QueuedLoad = MainThreadHelper.Get(() => { lock (queuedLoadLock) { if (_Vertices_QueuedLoadLock == null) { return(Vertices); } // Force-reload as we already returned true on the other thread. Vertices?.Dispose(); // NOTE: If something dares to change verts on the fly, make it wait on any existing tasks, then make it force-reload. Vertices = new VertexBuffer(Engine.Graphics.GraphicsDevice, typeof(VertexPositionTexture), verts.Length, BufferUsage.None); Vertices.SetData(verts); _Vertices_QueuedLoadLock = null; return(Vertices); } }); } return(true); } return(orig_ResetVertexBuffer()); }
public void ctor(Session session, Vector2?startPosition = default(Vector2?)) { if (LastLoadingThread != null && LastLoadingThread.TryGetTarget(out Thread lastThread) && (lastThread?.IsAlive ?? false)) { lastThread?.Abort(); } if (CoreModule.Settings.LazyLoading) { MainThreadHelper.Do(() => VirtualContentExt.UnloadOverworld()); } // Vanilla TileToIndex mappings. SurfaceIndex.TileToIndex = new Dictionary <char, int> { { '1', 3 }, { '3', 4 }, { '4', 7 }, { '5', 8 }, { '6', 8 }, { '7', 8 }, { '8', 8 }, { '9', 13 }, { 'a', 8 }, { 'b', 23 }, { 'c', 8 }, { 'd', 8 }, { 'e', 8 }, { 'f', 8 }, { 'g', 8 }, { 'G', 8 }, // Reflection alt - unassigned in vanilla. { 'h', 33 }, { 'i', 4 }, { 'j', 8 }, { 'k', 3 }, { 'l', 25 }, { 'm', 44 }, { 'n', 40 }, { 'o', 43 } }; AreaData area = AreaData.Get(session); MapMeta meta = area.GetMeta(); string path; path = meta?.BackgroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "BackgroundTiles.xml"); } GFX.BGAutotiler = new Autotiler(path); path = meta?.ForegroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "ForegroundTiles.xml"); } GFX.FGAutotiler = new Autotiler(path); path = meta?.AnimatedTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "AnimatedTiles.xml"); } GFX.AnimatedTilesBank = new AnimatedTilesBank(); XmlElement animatedData = Calc.LoadContentXML(path)["Data"]; foreach (XmlElement el in animatedData) { if (el != null) { GFX.AnimatedTilesBank.Add( el.Attr("name"), el.AttrFloat("delay", 0f), el.AttrVector2("posX", "posY", Vector2.Zero), el.AttrVector2("origX", "origY", Vector2.Zero), GFX.Game.GetAtlasSubtextures(el.Attr("path")) ); } } GFX.SpriteBank = new SpriteBank(GFX.Game, Path.Combine("Graphics", "Sprites.xml")); path = meta?.Sprites; if (!string.IsNullOrEmpty(path)) { SpriteBank bankOrig = GFX.SpriteBank; SpriteBank bankMod = new SpriteBank(GFX.Game, path); foreach (KeyValuePair <string, SpriteData> kvpBank in bankMod.SpriteData) { string key = kvpBank.Key; SpriteData valueMod = kvpBank.Value; if (bankOrig.SpriteData.TryGetValue(key, out SpriteData valueOrig)) { IDictionary animsOrig = valueOrig.Sprite.GetAnimations(); IDictionary animsMod = valueMod.Sprite.GetAnimations(); foreach (DictionaryEntry kvpAnim in animsMod) { animsOrig[kvpAnim.Key] = kvpAnim.Value; } valueOrig.Sources.AddRange(valueMod.Sources); // replay the starting animation to be sure it is referring to the new sprite. valueOrig.Sprite.Stop(); if (valueMod.Sprite.CurrentAnimationID != "") { valueOrig.Sprite.Play(valueMod.Sprite.CurrentAnimationID); } } else { bankOrig.SpriteData[key] = valueMod; } } } // This is done exactly once in the vanilla GFX.LoadData method. PlayerSprite.ClearFramesMetadata(); PlayerSprite.CreateFramesMetadata("player"); PlayerSprite.CreateFramesMetadata("player_no_backpack"); PlayerSprite.CreateFramesMetadata("badeline"); PlayerSprite.CreateFramesMetadata("player_badeline"); PlayerSprite.CreateFramesMetadata("player_playback"); path = meta?.Portraits; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "Portraits.xml"); } GFX.PortraitsSpriteBank = new SpriteBank(GFX.Portraits, path); orig_ctor(session, startPosition); LastLoadingThread = patch_RunThread.Current; }
public new void ResetBillboardBuffers() { // Checking for IsDisposed on other threads should be fine... if (billboardVertices != null && !billboardIndices.IsDisposed && !billboardIndices.GraphicsDevice.IsDisposed && billboardVertices != null && !billboardVertices.IsDisposed && !billboardVertices.GraphicsDevice.IsDisposed && billboardInfo.Length <= billboardVertices.VertexCount) { return; } // Handle already queued loads appropriately. object queuedLoadLock = _Billboard_QueuedLoadLock; if (queuedLoadLock != null) { lock (queuedLoadLock) { // Queued task finished just in time. if (_Billboard_QueuedLoadLock == null) { return; } // If we still can, cancel the queued load, then proceed with lazy-loading. if (MainThreadHelper.IsMainThread) { _Billboard_QueuedLoadLock = null; } } if (!MainThreadHelper.IsMainThread) { // Otherwise wait for it to get loaded, don't reload twice. (Don't wait locked!) while (!_Billboard_QueuedLoad.IsValid) { Thread.Yield(); } _Billboard_QueuedLoad.GetResult(); return; } } if (!(CoreModule.Settings.ThreadedGL ?? Everest.Flags.PreferThreadedGL) && !MainThreadHelper.IsMainThread && queuedLoadLock == null) { // Let's queue a reload onto the main thread and call it a day. lock (queuedLoadLock = new object()) { _Billboard_QueuedLoadLock = queuedLoadLock; _Billboard_QueuedLoad = MainThreadHelper.Get(() => { lock (queuedLoadLock) { if (_Billboard_QueuedLoadLock == null) { return(billboardVertices); } // Force-reload as we already returned true on the other thread. if (billboardVertices != null && !billboardVertices.IsDisposed) { billboardVertices.Dispose(); } if (billboardIndices != null && !billboardIndices.IsDisposed) { billboardIndices.Dispose(); } // NOTE: If something dares to change verts on the fly, make it wait on any existing tasks, then make it force-reload. // Let's rely on the original code for now. orig_ResetBillboardBuffers(); _Billboard_QueuedLoadLock = null; return(billboardVertices); } }); } return; } orig_ResetBillboardBuffers(); }
public void ctor(Session session, Vector2?startPosition = default(Vector2?)) { if (CoreModule.Settings.LazyLoading) { MainThreadHelper.Do(() => VirtualContentExt.UnloadOverworld()); } // Vanilla TileToIndex mappings. SurfaceIndex.TileToIndex = new Dictionary <char, int> { { '1', 3 }, { '3', 4 }, { '4', 7 }, { '5', 8 }, { '6', 8 }, { '7', 8 }, { '8', 8 }, { '9', 13 }, { 'a', 8 }, { 'b', 23 }, { 'c', 8 }, { 'd', 8 }, { 'e', 8 }, { 'f', 8 }, { 'g', 8 }, { 'h', 33 }, { 'i', 4 }, { 'j', 8 }, { 'k', 3 }, { 'l', 25 }, { 'm', 44 }, { 'n', 40 }, { 'o', 43 } }; AreaData area = AreaData.Get(session); MapMeta meta = area.GetMeta(); string path; path = meta?.BackgroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "BackgroundTiles.xml"); } GFX.BGAutotiler = new Autotiler(path); path = meta?.ForegroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "ForegroundTiles.xml"); } GFX.FGAutotiler = new Autotiler(path); path = meta?.AnimatedTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "AnimatedTiles.xml"); } GFX.AnimatedTilesBank = new AnimatedTilesBank(); XmlElement animatedData = Calc.LoadContentXML(path)["Data"]; foreach (XmlElement el in animatedData) { if (el != null) { GFX.AnimatedTilesBank.Add( el.Attr("name"), el.AttrFloat("delay", 0f), el.AttrVector2("posX", "posY", Vector2.Zero), el.AttrVector2("origX", "origY", Vector2.Zero), GFX.Game.GetAtlasSubtextures(el.Attr("path")) ); } } GFX.SpriteBank = new SpriteBank(GFX.Game, Path.Combine("Graphics", "Sprites.xml")); path = meta?.Sprites; if (!string.IsNullOrEmpty(path)) { SpriteBank bankOrig = GFX.SpriteBank; SpriteBank bankMod = new SpriteBank(GFX.Game, path); foreach (KeyValuePair <string, SpriteData> kvpBank in bankMod.SpriteData) { string key = kvpBank.Key; SpriteData valueMod = kvpBank.Value; if (bankOrig.SpriteData.TryGetValue(key, out SpriteData valueOrig)) { IDictionary animsOrig = valueOrig.Sprite.GetAnimations(); IDictionary animsMod = valueMod.Sprite.GetAnimations(); foreach (DictionaryEntry kvpAnim in animsMod) { animsOrig[kvpAnim.Key] = kvpAnim.Value; } } else { bankOrig.SpriteData[key] = valueMod; } } } path = meta?.Portraits; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "Portraits.xml"); } GFX.PortraitsSpriteBank = new SpriteBank(GFX.Portraits, path); orig_ctor(session, startPosition); }
public override bool Calculate() { bool isPMB = false; if (!input.connected()) { return(false); } VoxelBlock <Voxel> block = input.GetValue <VoxelBlock <Voxel> >(); if (surfaceConnection.connected()) { surface = surfaceConnection.GetValue <float>(); } Marching marching = null; switch (mode) { case VerteGenerationMode.Tetrahedron: marching = new MarchingTertrahedron(); break; case VerteGenerationMode.Cubes: marching = new MarchingCubes(); break; case VerteGenerationMode.Voxel: marching = new VoxelGeneration(); break; case VerteGenerationMode.PMB: isPMB = true; break; } //The size of voxel array. Vector3Int count = block.VoxelCount; int width = count.x; int height = count.y; int length = count.z; float[] voxels = new float[width * height * length]; for (int y = 0; y < height; y++) { Voxel[,] voxelLayer = block.Layers[y].Layer; for (int x = 0; x < width; x++) { for (int z = 0; z < length; z++) { int idx = x + y * width + z * width * height; voxels[idx] = voxelLayer[x, z].GetValue(); } } } if (isPMB) { var pmbTask = MainThreadHelper.instance().scheduleOnMainThread(() => { pmb.ReInit(block); Stopwatch pmbWatch = Stopwatch.StartNew(); buffers = pmb.calculate(voxels, width, height, length, surface); pmbWatch.Stop(); UnityEngine.Debug.LogFormat("PMB took {0}ms\n\t{1} voxels\n\t{2} triangles", pmbWatch.ElapsedMilliseconds, voxels.Count(), buffers.indexBuffer.count / 3); }); pmbTask.wait(); if (!pmbTask.completed) { return(false); } Block = block; return(true); } //Surface is the value that represents the surface of mesh //For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through. //The target value does not have to be the mid point it can be any value with in the range. // //This should be accesible by an input marching.Surface = surface; List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); List <Vector3> normals = new List <Vector3>(); Stopwatch sw = Stopwatch.StartNew(); marching.Generate(voxels, width, height, length, verts, indices, normals); sw.Stop(); UnityEngine.Debug.LogFormat("Marching took {0}ms\n\t{1} vertices; {2} triangles", sw.ElapsedMilliseconds, verts.Count(), indices.Count() / 3); sw.Restart(); weldVertices(verts, indices, normals); sw.Stop(); UnityEngine.Debug.LogFormat("Vertex welding took {0}ms\n\t {1} vertices left", sw.ElapsedMilliseconds, verts.Count()); var task = MainThreadHelper.instance().scheduleOnMainThread(() => { buffers = new RenderBuffers { vertexBuffer = new ComputeBuffer(verts.Count, sizeof(float) * 3), indexBuffer = new ComputeBuffer(indices.Count, sizeof(int)), normalBuffer = new ComputeBuffer(normals.Count, sizeof(float) * 3), argsBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments), }; buffers.vertexBuffer.SetData(verts); buffers.indexBuffer.SetData(indices); buffers.normalBuffer.SetData(normals); buffers.argsBuffer.SetData(new int[] { indices.Count, 1, 0, 0 }); }); task.wait(); if (!task.completed) { return(false); } Block = block; return(true); }