diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/CameraMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/CameraMixin.java index b00e2d37a8..6f272856b1 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/CameraMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/CameraMixin.java @@ -24,7 +24,6 @@ */ package net.runelite.mixins; -import net.runelite.api.Perspective; import net.runelite.api.mixins.FieldHook; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; @@ -40,9 +39,6 @@ public abstract class CameraMixin implements RSClient @Shadow("clientInstance") static RSClient client; - @Shadow("isDrawingScene") - static boolean isDrawingScene; - @Inject static boolean pitchRelaxEnabled = false; @@ -113,27 +109,4 @@ public abstract class CameraMixin implements RSClient } lastPitch = pitch; } - - // All of this is to bypass a check in Scene.drawScene - - @FieldHook("pitchSin") - @Inject - static void onPitchSinChanged(int idx) - { - if (pitchRelaxEnabled && isDrawingScene) - { - client.setPitchSin(Perspective.SINE[client.getCameraPitch()]); - } - } - - - @FieldHook("pitchCos") - @Inject - static void onPitchCosChanged(int idx) - { - if (pitchRelaxEnabled && isDrawingScene) - { - client.setPitchCos(Perspective.COSINE[client.getCameraPitch()]); - } - } } \ No newline at end of file diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneMixin.java index f55d81eaf7..7b65715fd1 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneMixin.java @@ -24,11 +24,11 @@ */ package net.runelite.mixins; +import net.runelite.api.Perspective; import net.runelite.api.Renderable; import net.runelite.api.SceneTilePaint; import net.runelite.api.Tile; import net.runelite.api.mixins.Copy; -import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; import net.runelite.api.mixins.Replace; import net.runelite.api.mixins.Shadow; @@ -37,33 +37,291 @@ import net.runelite.rs.api.RSDecorativeObject; import net.runelite.rs.api.RSGroundObject; import net.runelite.rs.api.RSItemLayer; import net.runelite.rs.api.RSScene; +import net.runelite.rs.api.RSTile; import net.runelite.rs.api.RSWallObject; @Mixin(RSScene.class) public abstract class RSSceneMixin implements RSScene { + private static final int MAX_DISTANCE = 25; + + private static final int PITCH_LOWER_LIMIT = 128; + private static final int PITCH_UPPER_LIMIT = 383; + @Shadow("clientInstance") private static RSClient client; - @Inject - static boolean isDrawingScene; - - @Copy("drawScene") - abstract void rs$drawScene(int cameraX, int cameraY, int cameraZ, int cameraPitch, int cameraYaw, int plane); + @Shadow("pitchRelaxEnabled") + private static boolean pitchRelaxEnabled; @Replace("drawScene") void rl$drawScene(int cameraX, int cameraY, int cameraZ, int cameraPitch, int cameraYaw, int plane) { - try + final int maxX = getMaxX(); + final int maxY = getMaxY(); + final int maxZ = getMaxZ(); + + final int minLevel = getMinLevel(); + + final RSTile[][][] tiles = getTiles(); + final int distance = MAX_DISTANCE; + + if (cameraX < 0) { - isDrawingScene = true; - rs$drawScene(cameraX, cameraY, cameraZ, cameraPitch, cameraYaw, plane); - client.getCallbacks().drawScene(); + cameraX = 0; } - finally + else if (cameraX >= maxX * Perspective.LOCAL_TILE_SIZE) { - isDrawingScene = false; + cameraX = maxX * Perspective.LOCAL_TILE_SIZE - 1; } + + if (cameraZ < 0) + { + cameraZ = 0; + } + else if (cameraZ >= maxZ * Perspective.LOCAL_TILE_SIZE) + { + cameraZ = maxZ * Perspective.LOCAL_TILE_SIZE - 1; + } + + // we store the uncapped pitch for setting camera angle for the pitch relaxer + // we still have to cap the pitch in order to access the visibility map, though + int realPitch = cameraPitch; + if (cameraPitch < PITCH_LOWER_LIMIT) + { + cameraPitch = PITCH_LOWER_LIMIT; + } + else if (cameraPitch > PITCH_UPPER_LIMIT) + { + cameraPitch = PITCH_UPPER_LIMIT; + } + if (!pitchRelaxEnabled) + { + realPitch = cameraPitch; + } + + client.setCycle(client.getCycle() + 1); + + client.setPitchSin(Perspective.SINE[realPitch]); + client.setPitchCos(Perspective.COSINE[realPitch]); + client.setYawSin(Perspective.SINE[cameraYaw]); + client.setYawCos(Perspective.COSINE[cameraYaw]); + + final int[][][] tileHeights = client.getTileHeights(); + boolean[][] renderArea = client.getVisibilityMaps()[(cameraPitch - 128) / 32][cameraYaw / 64]; + client.setRenderArea(renderArea); + + client.setCameraX2(cameraX); + client.setCameraY2(cameraY); + client.setCameraZ2(cameraZ); + + int screenCenterX = cameraX / Perspective.LOCAL_TILE_SIZE; + int screenCenterZ = cameraZ / Perspective.LOCAL_TILE_SIZE; + + client.setScreenCenterX(screenCenterX); + client.setScreenCenterZ(screenCenterZ); + client.setScenePlane(plane); + + int minTileX = screenCenterX - distance; + if (minTileX < 0) + { + minTileX = 0; + } + + int minTileZ = screenCenterZ - distance; + if (minTileZ < 0) + { + minTileZ = 0; + } + + int maxTileX = screenCenterX + distance; + if (maxTileX > maxX) + { + maxTileX = maxX; + } + + int maxTileZ = screenCenterZ + distance; + if (maxTileZ > maxZ) + { + maxTileZ = maxZ; + } + + client.setMinTileX(minTileX); + client.setMinTileZ(minTileZ); + client.setMaxTileX(maxTileX); + client.setMaxTileZ(maxTileZ); + + updateOccluders(); + + client.setTileUpdateCount(0); + + for (int z = minLevel; z < maxY; ++z) + { + RSTile[][] planeTiles = tiles[z]; + + for (int x = minTileX; x < maxTileX; ++x) + { + for (int y = minTileZ; y < maxTileZ; ++y) + { + RSTile tile = planeTiles[x][y]; + if (tile != null) + { + if (tile.getPhysicalLevel() <= plane + && (renderArea[x - screenCenterX + MAX_DISTANCE][y - screenCenterZ + MAX_DISTANCE] + || tileHeights[z][x][y] - cameraY >= 2000)) + { + tile.setDraw(true); + tile.setVisible(true); + tile.setDrawEntities(true); + client.setTileUpdateCount(client.getTileUpdateCount() + 1); + } + else + { + tile.setDraw(false); + tile.setVisible(false); + tile.setWallCullDirection(0); + } + } + } + } + } + + for (int z = minLevel; z < maxY; ++z) + { + RSTile[][] planeTiles = tiles[z]; + + for (int x = -distance; x <= 0; ++x) + { + int var10 = x + screenCenterX; + int var16 = screenCenterX - x; + if (var10 >= minTileX || var16 < maxTileX) + { + for (int y = -distance; y <= 0; ++y) + { + int var13 = y + screenCenterZ; + int var14 = screenCenterZ - y; + if (var10 >= minTileX) + { + if (var13 >= minTileZ) + { + RSTile tile = planeTiles[var10][var13]; + if (tile != null && tile.isDraw()) + { + draw(tile, true); + } + } + + if (var14 < maxTileZ) + { + RSTile tile = planeTiles[var10][var14]; + if (tile != null && tile.isDraw()) + { + draw(tile, true); + } + } + } + + if (var16 < maxTileX) + { + if (var13 >= minTileZ) + { + RSTile tile = planeTiles[var16][var13]; + if (tile != null && tile.isDraw()) + { + draw(tile, true); + } + } + + if (var14 < maxTileZ) + { + RSTile tile = planeTiles[var16][var14]; + if (tile != null && tile.isDraw()) + { + draw(tile, true); + } + } + } + + if (client.getTileUpdateCount() == 0) + { + client.setCheckClick(false); + client.getCallbacks().drawScene(); + return; + } + } + } + } + } + + for (int z = minLevel; z < maxY; ++z) + { + RSTile[][] planeTiles = tiles[z]; + + for (int x = -distance; x <= 0; ++x) + { + int var10 = x + screenCenterX; + int var16 = screenCenterX - x; + if (var10 >= minTileX || var16 < maxTileX) + { + for (int y = -distance; y <= 0; ++y) + { + int var13 = y + screenCenterZ; + int var14 = screenCenterZ - y; + if (var10 >= minTileX) + { + if (var13 >= minTileZ) + { + RSTile tile = planeTiles[var10][var13]; + if (tile != null && tile.isDraw()) + { + draw(tile, false); + } + } + + if (var14 < maxTileZ) + { + RSTile tile = planeTiles[var10][var14]; + if (tile != null && tile.isDraw()) + { + draw(tile, false); + } + } + } + + if (var16 < maxTileX) + { + if (var13 >= minTileZ) + { + RSTile tile = planeTiles[var16][var13]; + if (tile != null && tile.isDraw()) + { + draw(tile, false); + } + } + + if (var14 < maxTileZ) + { + RSTile tile = planeTiles[var16][var14]; + if (tile != null && tile.isDraw()) + { + draw(tile, false); + } + } + } + + if (client.getTileUpdateCount() == 0) + { + client.setCheckClick(false); + client.getCallbacks().drawScene(); + return; + } + } + } + } + } + + client.setCheckClick(false); + client.getCallbacks().drawScene(); } @Copy("addBoundaryDecoration") diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java index cdf6cf6d53..c4bacaf984 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java @@ -558,6 +558,12 @@ public interface RSClient extends RSGameEngine, Client @Import("pitchCos") void setPitchCos(int v); + @Import("yawSin") + void setYawSin(int v); + + @Import("yawCos") + void setYawCos(int v); + @Import("Rasterizer3D_zoom") int get3dZoom(); @@ -685,4 +691,52 @@ public interface RSClient extends RSGameEngine, Client @Import("occupiedTilesTick") int[][] getOccupiedTilesTick(); + + @Import("cycle") + int getCycle(); + + @Import("cycle") + void setCycle(int cycle); + + @Import("visibilityMaps") + boolean[][][][] getVisibilityMaps(); + + @Import("renderArea") + void setRenderArea(boolean[][] renderArea); + + @Import("cameraX2") + void setCameraX2(int cameraX2); + + @Import("cameraY2") + void setCameraY2(int cameraY2); + + @Import("cameraZ2") + void setCameraZ2(int cameraZ2); + + @Import("screenCenterX") + void setScreenCenterX(int screenCenterX); + + @Import("screenCenterZ") + void setScreenCenterZ(int screenCenterZ); + + @Import("Scene_plane") + void setScenePlane(int scenePlane); + + @Import("minTileX") + void setMinTileX(int i); + + @Import("minTileZ") + void setMinTileZ(int i); + + @Import("maxTileX") + void setMaxTileX(int i); + + @Import("maxTileZ") + void setMaxTileZ(int i); + + @Import("tileUpdateCount") + int getTileUpdateCount(); + + @Import("tileUpdateCount") + void setTileUpdateCount(int tileUpdateCount); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSScene.java b/runescape-api/src/main/java/net/runelite/rs/api/RSScene.java index 7b75ed9f24..312b12f557 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSScene.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSScene.java @@ -35,8 +35,26 @@ public interface RSScene extends Scene @Import("tiles") @Override - Tile[][][] getTiles(); + RSTile[][][] getTiles(); + + @Import("draw") + void draw(Tile tile, boolean var2); @Import("drawTile") void drawTile(int[] pixels, int pixelOffset, int width, int z, int x, int y); + + @Import("updateOccluders") + void updateOccluders(); + + @Import("maxX") + int getMaxX(); + + @Import("maxY") + int getMaxY(); + + @Import("maxZ") + int getMaxZ(); + + @Import("minLevel") + int getMinLevel(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java b/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java index b7c9feb58e..bd53aa3060 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java @@ -73,4 +73,28 @@ public interface RSTile extends Tile @Import("plane") @Override int getPlane(); + + @Import("physicalLevel") + int getPhysicalLevel(); + + @Import("draw") + boolean isDraw(); + + @Import("draw") + void setDraw(boolean draw); + + @Import("visible") + boolean isVisible(); + + @Import("visible") + void setVisible(boolean visible); + + @Import("drawEntities") + boolean isDrawEntities(); + + @Import("drawEntities") + void setDrawEntities(boolean drawEntities); + + @Import("wallCullDirection") + void setWallCullDirection(int wallCullDirection); }