diff --git a/src/main/java/legend/core/RenderEngine.java b/src/main/java/legend/core/RenderEngine.java index bae0680a4..b6c7f5323 100644 --- a/src/main/java/legend/core/RenderEngine.java +++ b/src/main/java/legend/core/RenderEngine.java @@ -45,7 +45,9 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Comparator; +import java.util.Deque; import java.util.EnumMap; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -287,6 +289,8 @@ public class RenderEngine { private int frameSkipIndex; + private final Deque<Runnable> tasks = new LinkedList<>(); + public RenderEngine() { this.mainBatch = new RenderBatch(this, () -> this.vdfUniform, this.vdfBuffer, this.lightBuffer); this.scissorStack = new ScissorStack(this, this.mainBatch); @@ -437,6 +441,13 @@ public Texture getLastFrame() { return this.renderTextures[Math.floorMod(this.renderBufferIndex - 1, RENDER_BUFFER_COUNT)]; } + /** Submit a task to be run at the start of the next frame */ + public void addTask(final Runnable task) { + synchronized(this.tasks) { + this.tasks.push(task); + } + } + public void init() { this.camera2d = new BasicCamera(0.0f, 0.0f); this.camera3d = new QuaternionCamera(0.0f, 0.0f, 0.0f); @@ -553,6 +564,13 @@ public void init() { this.renderBufferQuad.persistent = true; this.window.events.onDraw(() -> { + synchronized(this.tasks) { + Runnable task; + while((task = this.tasks.poll()) != null) { + task.run(); + } + } + if(this.frameSkipIndex == 0) { this.pre(); } diff --git a/src/main/java/legend/game/combat/Battle.java b/src/main/java/legend/game/combat/Battle.java index 97eb6e543..99b8ac6af 100644 --- a/src/main/java/legend/game/combat/Battle.java +++ b/src/main/java/legend/game/combat/Battle.java @@ -1899,10 +1899,13 @@ public void FUN_800c8748() { @Method(0x800c8774L) public void loadStageTmdAndAnim(final String modelName, final List<FileData> files) { + LOGGER.info("Battle stage %s loaded", modelName); + this.setStageHasNoModel(); if(files.get(0).size() > 0 && files.get(1).size() > 0 && files.get(2).size() > 0) { final BattleStage stage = battlePreloadedEntities_1f8003f4.stage_963c; + stage.name = modelName; this.loadStageTmd(stage, new CContainer(modelName, files.get(0), 10), new TmdAnimationFile(files.get(1))); stage.coord2_558.coord.transfer.set(0, 0, 0); stage.param_5a8.rotate.set(0.0f, MathHelper.TWO_PI / 4.0f, 0.0f); @@ -1974,28 +1977,43 @@ public void renderSkybox() { @Method(0x800c8b20L) public void loadStage(final int stage) { + LOGGER.info("Loading battle stage %d", stage); + if(battlePreloadedEntities_1f8003f4.skyboxObj != null) { battlePreloadedEntities_1f8003f4.skyboxObj.delete(); battlePreloadedEntities_1f8003f4.skyboxObj = null; } - loadDrgnDir(0, 2497 + stage, files -> { - if(files.get(1).hasVirtualSize()) { - this.loadStageMcq(new McqHeader(files.get(1))); + // GH#1931 + // Disable texture animations so we don't corrupt the texture of the loading stage due to the old stage model still being loaded... + if(stage_800bda0c != null) { + for(int i = 0; i < 10; i++) { + stage_800bda0c._618[i] = 0; } + } - if(files.get(2).size() != 0) { - this.loadStageTim(files.get(2)); - } - }); + // ... and defer loading to the next frame so that any texture animations currently in the pipeline finish + RENDERER.addTask(() -> { + loadDrgnDir(0, 2497 + stage, files -> { + if(files.get(1).hasVirtualSize()) { + this.loadStageMcq(new McqHeader(files.get(1))); + } - loadDrgnDir(0, (2497 + stage) + "/0", files -> this.loadStageTmdAndAnim("DRGN0/" + (2497 + stage) + "/0", files)); + if(files.get(2).size() != 0) { + this.loadStageTim(files.get(2)); + } + }); + + loadDrgnDir(0, (2497 + stage) + "/0", files -> this.loadStageTmdAndAnim("DRGN0/" + (2497 + stage) + "/0", files)); + }); this.currentStage_800c66a4 = stage; } @Method(0x800c8c84L) public void loadStageTim(final FileData data) { + LOGGER.info("Battle stage texture loaded"); + final Tim tim = new Tim(data); GPU.uploadData15(tim.getImageRect(), tim.getImageData()); @@ -2025,6 +2043,8 @@ public void rotateAndRenderBattleStage() { @Method(0x800c8d64L) public void loadStageMcq(final McqHeader mcq) { + LOGGER.info("Battle stage skybox loaded"); + final int x; if((battleFlags_800bc960 & 0x80) != 0) { x = 320; diff --git a/src/main/java/legend/game/combat/environment/BattleStage.java b/src/main/java/legend/game/combat/environment/BattleStage.java index 81021db5d..83788568f 100644 --- a/src/main/java/legend/game/combat/environment/BattleStage.java +++ b/src/main/java/legend/game/combat/environment/BattleStage.java @@ -8,6 +8,8 @@ import legend.game.types.Keyframe0c; public class BattleStage { + public String name; + public ModelPart10[] dobj2s_00; // public final GsCOORDINATE2[] coord2s_a0 = new GsCOORDINATE2[10]; // Use coord2 on dobj2 // public final Transforms[] params_3c0 = new Transforms[10]; // Use dobj2.coord2.transforms