From 3f5e2733495cd53c7881ea8579fa0e336ad72b8a Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 15 Nov 2018 09:19:37 -0500 Subject: [PATCH] Add GPU renderer --- .../java/net/runelite/api/BufferProvider.java | 5 + .../main/java/net/runelite/api/Client.java | 49 + .../main/java/net/runelite/api/Constants.java | 2 + .../net/runelite/api/DecorativeObject.java | 3 + .../java/net/runelite/api/GameEngine.java | 2 + .../java/net/runelite/api/GameObject.java | 2 + .../java/net/runelite/api/GroundObject.java | 1 + .../src/main/java/net/runelite/api/Model.java | 57 + .../main/java/net/runelite/api/NodeCache.java | 4 + .../java/net/runelite/api/Perspective.java | 3 +- .../java/net/runelite/api/Renderable.java | 4 + .../src/main/java/net/runelite/api/Scene.java | 3 + .../java/net/runelite/api/SceneTileModel.java | 29 + .../java/net/runelite/api/SceneTilePaint.java | 19 + .../main/java/net/runelite/api/Texture.java | 42 + .../net/runelite/api/TextureProvider.java | 22 +- .../src/main/java/net/runelite/api/Tile.java | 14 + .../java/net/runelite/api/WallObject.java | 3 + .../net/runelite/api/hooks/DrawCallbacks.java | 50 + runelite-client/pom.xml | 52 + .../net/runelite/client/callback/Hooks.java | 44 + .../runelite/client/plugins/gpu/GLUtil.java | 180 +++ .../client/plugins/gpu/GpuFloatBuffer.java | 72 ++ .../client/plugins/gpu/GpuIntBuffer.java | 77 ++ .../client/plugins/gpu/GpuPlugin.java | 1144 +++++++++++++++++ .../client/plugins/gpu/GpuPluginConfig.java | 43 + .../client/plugins/gpu/SceneUploader.java | 549 ++++++++ .../client/plugins/gpu/ShaderException.java | 33 + .../client/plugins/gpu/TextureManager.java | 235 ++++ .../client/plugins/gpu/template/Template.java | 56 + .../runelite/client/plugins/gpu/common.glsl | 166 +++ .../net/runelite/client/plugins/gpu/comp.glsl | 150 +++ .../client/plugins/gpu/comp_small.glsl | 128 ++ .../net/runelite/client/plugins/gpu/frag.glsl | 53 + .../runelite/client/plugins/gpu/fragui.glsl | 36 + .../net/runelite/client/plugins/gpu/geom.glsl | 82 ++ .../client/plugins/gpu/priority_render.glsl | 205 +++ .../client/plugins/gpu/to_screen.glsl | 47 + .../net/runelite/client/plugins/gpu/vert.glsl | 125 ++ .../runelite/client/plugins/gpu/vertui.glsl | 36 + .../client/plugins/gpu/ShaderTest.java | 172 +++ .../plugins/gpu/template/TemplateTest.java | 64 + runelite-mixins/pom.xml | 13 + .../net/runelite/mixins/ClickboxMixin.java | 314 +++++ .../net/runelite/mixins/RSClientMixin.java | 35 + .../runelite/mixins/RSGameEngineMixin.java | 25 + .../mixins/RSMainBufferProviderMixin.java | 34 + .../net/runelite/mixins/RSModelDataMixin.java | 202 +++ .../net/runelite/mixins/RSModelMixin.java | 158 +++ .../net/runelite/mixins/RSSceneMixin.java | 295 ++++- .../mixins/RSSceneTileModelMixin.java | 84 ++ .../mixins/RSSceneTilePaintMixin.java | 84 ++ .../net/runelite/mixins/RSTextureMixin.java | 92 ++ .../mixins/RSTextureProviderMixin.java | 14 +- .../runelite/mixins/Rasterizer2DMixin.java | 272 ++++ .../net/runelite/mixins/RSSceneMixinTest.java | 53 + .../java/net/runelite/rs/api/RSBuffer.java | 2 +- .../net/runelite/rs/api/RSBufferProvider.java | 12 + .../java/net/runelite/rs/api/RSClient.java | 113 ++ .../runelite/rs/api/RSDecorativeObject.java | 5 + .../net/runelite/rs/api/RSGameEngine.java | 18 +- .../net/runelite/rs/api/RSGroundObject.java | 4 +- .../runelite/rs/api/RSMainBufferProvider.java | 7 + .../java/net/runelite/rs/api/RSModel.java | 85 ++ .../java/net/runelite/rs/api/RSModelData.java | 69 + .../java/net/runelite/rs/api/RSNodeCache.java | 9 + .../net/runelite/rs/api/RSRenderable.java | 8 + .../java/net/runelite/rs/api/RSScene.java | 3 + .../net/runelite/rs/api/RSSceneTileModel.java | 40 + .../net/runelite/rs/api/RSSceneTilePaint.java | 20 + .../java/net/runelite/rs/api/RSTexture.java | 47 + .../runelite/rs/api/RSTextureProvider.java | 22 +- .../main/java/net/runelite/rs/api/RSTile.java | 14 +- .../net/runelite/rs/api/RSWallObject.java | 7 +- 74 files changed, 6201 insertions(+), 22 deletions(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/Texture.java create mode 100644 runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_small.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java create mode 100644 runelite-mixins/src/main/java/net/runelite/mixins/ClickboxMixin.java create mode 100644 runelite-mixins/src/main/java/net/runelite/mixins/RSModelDataMixin.java create mode 100644 runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTileModelMixin.java create mode 100644 runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTilePaintMixin.java create mode 100644 runelite-mixins/src/main/java/net/runelite/mixins/RSTextureMixin.java create mode 100644 runelite-mixins/src/main/java/net/runelite/mixins/Rasterizer2DMixin.java create mode 100644 runelite-mixins/src/test/java/net/runelite/mixins/RSSceneMixinTest.java create mode 100644 runescape-api/src/main/java/net/runelite/rs/api/RSModelData.java create mode 100644 runescape-api/src/main/java/net/runelite/rs/api/RSTexture.java diff --git a/runelite-api/src/main/java/net/runelite/api/BufferProvider.java b/runelite-api/src/main/java/net/runelite/api/BufferProvider.java index 8a7d9f2d66d..1caf77aa964 100644 --- a/runelite-api/src/main/java/net/runelite/api/BufferProvider.java +++ b/runelite-api/src/main/java/net/runelite/api/BufferProvider.java @@ -29,4 +29,9 @@ */ public interface BufferProvider { + int[] getPixels(); + + int getWidth(); + + int getHeight(); } diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index d6fe5dbbef1..84a56fd7028 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -34,6 +34,7 @@ import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.api.hooks.Callbacks; +import net.runelite.api.hooks.DrawCallbacks; import net.runelite.api.vars.AccountType; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; @@ -50,6 +51,10 @@ public interface Client extends GameEngine */ Callbacks getCallbacks(); + DrawCallbacks getDrawCallbacks(); + + void setDrawCallbacks(DrawCallbacks drawCallbacks); + /** * Retrieve a global logger for the client. * This is most useful for mixins which can't have their own. @@ -210,6 +215,18 @@ public interface Client extends GameEngine */ int getWorld(); + /** + * Gets the canvas height + * @return + */ + int getCanvasHeight(); + + /** + * Gets the canvas width + * @return + */ + int getCanvasWidth(); + /** * Gets the height of the viewport. * @@ -1485,6 +1502,13 @@ public interface Client extends GameEngine */ EnumSet getWorldType(); + /** + * Gets the enabled state for the Oculus orb mode + * + * @return + */ + int getOculusOrbState(); + /** * Sets the enabled state for the Oculus orb state * @@ -1509,4 +1533,29 @@ public interface Client extends GameEngine * @param world target world to hop to */ void hopToWorld(World world); + + boolean isGpu(); + + void setGpu(boolean gpu); + + int get3dZoom(); + int getCenterX(); + int getCenterY(); + + int getCameraX2(); + int getCameraY2(); + int getCameraZ2(); + + TextureProvider getTextureProvider(); + + NodeCache getCachedModels2(); + + void setRenderArea(boolean[][] renderArea); + + int getRasterizer3D_clipMidX2(); + int getRasterizer3D_clipNegativeMidX(); + int getRasterizer3D_clipNegativeMidY(); + int getRasterizer3D_clipMidY2(); + + void checkClickbox(Model model, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash); } diff --git a/runelite-api/src/main/java/net/runelite/api/Constants.java b/runelite-api/src/main/java/net/runelite/api/Constants.java index c8a567c4a2a..f13dbf86c6d 100644 --- a/runelite-api/src/main/java/net/runelite/api/Constants.java +++ b/runelite-api/src/main/java/net/runelite/api/Constants.java @@ -67,4 +67,6 @@ public class Constants * the plane value to 0-3. */ public static final int MAX_Z = 4; + + public static final int TILE_FLAG_BRIDGE = 2; } diff --git a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java index 14d8da9fb99..c2bbd14cf3b 100644 --- a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java +++ b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java @@ -38,4 +38,7 @@ public interface DecorativeObject extends TileObject * @see net.runelite.api.model.Jarvis */ Polygon getConvexHull(); + + Renderable getRenderable(); + Renderable getRenderable2(); } diff --git a/runelite-api/src/main/java/net/runelite/api/GameEngine.java b/runelite-api/src/main/java/net/runelite/api/GameEngine.java index 7a094068756..30faa9691d8 100644 --- a/runelite-api/src/main/java/net/runelite/api/GameEngine.java +++ b/runelite-api/src/main/java/net/runelite/api/GameEngine.java @@ -51,4 +51,6 @@ public interface GameEngine * @return true if on the main thread, false otherwise */ boolean isClientThread(); + + void resizeCanvas(); } diff --git a/runelite-api/src/main/java/net/runelite/api/GameObject.java b/runelite-api/src/main/java/net/runelite/api/GameObject.java index e8e7b692e77..c721f350670 100644 --- a/runelite-api/src/main/java/net/runelite/api/GameObject.java +++ b/runelite-api/src/main/java/net/runelite/api/GameObject.java @@ -67,4 +67,6 @@ public interface GameObject extends TileObject * @return the orientation */ Angle getOrientation(); + + Renderable getRenderable(); } diff --git a/runelite-api/src/main/java/net/runelite/api/GroundObject.java b/runelite-api/src/main/java/net/runelite/api/GroundObject.java index c463c2429ec..8a974cd752c 100644 --- a/runelite-api/src/main/java/net/runelite/api/GroundObject.java +++ b/runelite-api/src/main/java/net/runelite/api/GroundObject.java @@ -29,4 +29,5 @@ */ public interface GroundObject extends TileObject { + Renderable getRenderable(); } diff --git a/runelite-api/src/main/java/net/runelite/api/Model.java b/runelite-api/src/main/java/net/runelite/api/Model.java index 172232e9ded..c287b2afd15 100644 --- a/runelite-api/src/main/java/net/runelite/api/Model.java +++ b/runelite-api/src/main/java/net/runelite/api/Model.java @@ -46,4 +46,61 @@ public interface Model extends Renderable * @return the triangle */ List getTriangles(); + + int getVerticesCount(); + + int[] getVerticesX(); + + int[] getVerticesY(); + + int[] getVerticesZ(); + + int getTrianglesCount(); + + int[] getTrianglesX(); + + int[] getTrianglesY(); + + int[] getTrianglesZ(); + + int[] getFaceColors1(); + + int[] getFaceColors2(); + + int[] getFaceColors3(); + + byte[] getTriangleTransparencies(); + + int getSceneId(); + void setSceneId(int sceneId); + + int getBufferOffset(); + void setBufferOffset(int bufferOffset); + + int getUvBufferOffset(); + void setUvBufferOffset(int bufferOffset); + + int getModelHeight(); + + void calculateBoundsCylinder(); + + byte[] getFaceRenderPriorities(); + + int getRadius(); + + short[] getFaceTextures(); + + float[][] getFaceTextureUCoordinates(); + float[][] getFaceTextureVCoordinates(); + + void calculateExtreme(int orientation); + + int getCenterX(); + int getCenterY(); + int getCenterZ(); + int getExtremeX(); + int getExtremeY(); + int getExtremeZ(); + + int getXYZMag(); } diff --git a/runelite-api/src/main/java/net/runelite/api/NodeCache.java b/runelite-api/src/main/java/net/runelite/api/NodeCache.java index c6337fd735b..e6d8f8208ba 100644 --- a/runelite-api/src/main/java/net/runelite/api/NodeCache.java +++ b/runelite-api/src/main/java/net/runelite/api/NodeCache.java @@ -33,4 +33,8 @@ public interface NodeCache * Resets cache. */ void reset(); + + void setCapacity(int capacity); + + void setRemainingCapacity(int remainingCapacity); } diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index bfbc2f070e0..0d66d5c3964 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -36,6 +36,7 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import static net.runelite.api.Constants.TILE_FLAG_BRIDGE; import net.runelite.api.coords.LocalPoint; import net.runelite.api.model.Jarvis; import net.runelite.api.model.Triangle; @@ -56,8 +57,6 @@ public class Perspective public static final int SCENE_SIZE = Constants.SCENE_SIZE; // in tiles - private static final int TILE_FLAG_BRIDGE = 2; - public static final int[] SINE = new int[2048]; // sine angles for each of the 2048 units, * 65536 and stored as an int public static final int[] COSINE = new int[2048]; // cosine diff --git a/runelite-api/src/main/java/net/runelite/api/Renderable.java b/runelite-api/src/main/java/net/runelite/api/Renderable.java index af7e7dfc54f..15569316a39 100644 --- a/runelite-api/src/main/java/net/runelite/api/Renderable.java +++ b/runelite-api/src/main/java/net/runelite/api/Renderable.java @@ -35,4 +35,8 @@ public interface Renderable extends Node * @return the model */ Model getModel(); + + void setModelHeight(int modelHeight); + + void draw(int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash); } diff --git a/runelite-api/src/main/java/net/runelite/api/Scene.java b/runelite-api/src/main/java/net/runelite/api/Scene.java index 10a34c02b84..53cc8fcb150 100644 --- a/runelite-api/src/main/java/net/runelite/api/Scene.java +++ b/runelite-api/src/main/java/net/runelite/api/Scene.java @@ -35,4 +35,7 @@ public interface Scene * @return the tiles in [plane][x][y] */ Tile[][][] getTiles(); + + int getDrawDistance(); + void setDrawDistance(int drawDistance); } diff --git a/runelite-api/src/main/java/net/runelite/api/SceneTileModel.java b/runelite-api/src/main/java/net/runelite/api/SceneTileModel.java index 0f8a9e6bc40..b7abcab08fd 100644 --- a/runelite-api/src/main/java/net/runelite/api/SceneTileModel.java +++ b/runelite-api/src/main/java/net/runelite/api/SceneTileModel.java @@ -56,4 +56,33 @@ public interface SceneTileModel * @return the rotation */ int getRotation(); + + int[] getFaceX(); + + int[] getFaceY(); + + int[] getFaceZ(); + + int[] getVertexX(); + + int[] getVertexY(); + + int[] getVertexZ(); + + int[] getTriangleColorA(); + + int[] getTriangleColorB(); + + int[] getTriangleColorC(); + + int[] getTriangleTextureId(); + + int getBufferOffset(); + void setBufferOffset(int bufferOffset); + + int getUvBufferOffset(); + void setUvBufferOffset(int bufferOffset); + + int getBufferLen(); + void setBufferLen(int bufferLen); } diff --git a/runelite-api/src/main/java/net/runelite/api/SceneTilePaint.java b/runelite-api/src/main/java/net/runelite/api/SceneTilePaint.java index 61771e31b99..0247c17e8f1 100644 --- a/runelite-api/src/main/java/net/runelite/api/SceneTilePaint.java +++ b/runelite-api/src/main/java/net/runelite/api/SceneTilePaint.java @@ -35,4 +35,23 @@ public interface SceneTilePaint * @return the paint RGB */ int getRBG(); + + int getSwColor(); + + int getSeColor(); + + int getNwColor(); + + int getNeColor(); + + int getTexture(); + + int getBufferOffset(); + void setBufferOffset(int bufferOffset); + + int getUvBufferOffset(); + void setUvBufferOffset(int bufferOffset); + + int getBufferLen(); + void setBufferLen(int bufferLen); } diff --git a/runelite-api/src/main/java/net/runelite/api/Texture.java b/runelite-api/src/main/java/net/runelite/api/Texture.java new file mode 100644 index 00000000000..80a4d1a45a5 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/Texture.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +public interface Texture extends Node +{ + int[] getPixels(); + + int getAnimationDirection(); + + int getAnimationSpeed(); + + boolean isLoaded(); + + float getU(); + void setU(float u); + + float getV(); + void setV(float v); +} diff --git a/runelite-api/src/main/java/net/runelite/api/TextureProvider.java b/runelite-api/src/main/java/net/runelite/api/TextureProvider.java index 1123fcde3cb..b7df7334384 100644 --- a/runelite-api/src/main/java/net/runelite/api/TextureProvider.java +++ b/runelite-api/src/main/java/net/runelite/api/TextureProvider.java @@ -26,5 +26,25 @@ public interface TextureProvider { - void checkTextures(int var1); + double getBrightness(); + + /** + * Set the brightness for textures, clearing the texture cache. + * @param brightness + */ + void setBrightness(double brightness); + + /** + * Get all textures + * + * @return + */ + Texture[] getTextures(); + + /** + * Get the pixels for a texture + * @param textureId + * @return + */ + int[] load(int textureId); } diff --git a/runelite-api/src/main/java/net/runelite/api/Tile.java b/runelite-api/src/main/java/net/runelite/api/Tile.java index 2f14978ec18..0911c17de10 100644 --- a/runelite-api/src/main/java/net/runelite/api/Tile.java +++ b/runelite-api/src/main/java/net/runelite/api/Tile.java @@ -110,6 +110,13 @@ public interface Tile */ int getPlane(); + /** + * Get the plane this tile is rendered on, which is where the tile heights are from. + * + * @return + */ + int getRenderLevel(); + /** * Computes and returns whether this tile has line of sight to another. * @@ -124,4 +131,11 @@ public interface Tile * @return the ground items */ List getGroundItems(); + + /** + * Return the tile under this one, if this tile is a bridge + * + * @return + */ + Tile getBridge(); } diff --git a/runelite-api/src/main/java/net/runelite/api/WallObject.java b/runelite-api/src/main/java/net/runelite/api/WallObject.java index 0b27c1a7bb4..9a7156b6543 100644 --- a/runelite-api/src/main/java/net/runelite/api/WallObject.java +++ b/runelite-api/src/main/java/net/runelite/api/WallObject.java @@ -49,4 +49,7 @@ public interface WallObject extends TileObject * @return the boundary configuration */ int getConfig(); + + Renderable getRenderable1(); + Renderable getRenderable2(); } diff --git a/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java b/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java new file mode 100644 index 00000000000..b76caecd44f --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.hooks; + +import net.runelite.api.Renderable; +import net.runelite.api.SceneTileModel; +import net.runelite.api.SceneTilePaint; +import net.runelite.api.Texture; + +public interface DrawCallbacks +{ + void draw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash); + + void drawScenePaint(int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, + SceneTilePaint paint, int tileZ, int tileX, int tileY, + int zoom, int centerX, int centerY); + + + void drawSceneModel(int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, + SceneTileModel model, int tileZ, int tileX, int tileY, + int zoom, int centerX, int centerY); + + void draw(); + + void drawScene(int cameraX, int cameraY, int cameraZ, int cameraPitch, int cameraYaw, int plane); + + void animate(Texture texture, int diff); +} diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 857ce60fab8..956dd60bf74 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -37,6 +37,7 @@ 4.1.0 + 2.3.2 true @@ -90,6 +91,45 @@ commons-text 1.2 + + org.jogamp.jogl + jogl-all + ${jogl.version} + + + org.jogamp.jogl + jogl-all + ${jogl.version} + natives-windows-amd64 + runtime + + + org.jogamp.jogl + jogl-all + ${jogl.version} + natives-windows-i586 + runtime + + + org.jogamp.gluegen + gluegen-rt + ${jogl.version} + runtime + + + org.jogamp.gluegen + gluegen-rt + ${jogl.version} + natives-windows-amd64 + runtime + + + org.jogamp.gluegen + gluegen-rt + ${jogl.version} + natives-windows-i586 + runtime + net.runelite @@ -230,6 +270,18 @@ ** + + org.jogamp.jogl:* + + ** + + + + org.jogamp.gluegen:* + + ** + + diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index ebb041d5cca..766e6acd247 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -41,12 +41,15 @@ import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.BufferProvider; import net.runelite.api.Client; import net.runelite.api.MainBufferProvider; import net.runelite.api.RenderOverview; +import net.runelite.api.Renderable; import net.runelite.api.WorldMapManager; import net.runelite.api.events.GameTick; import net.runelite.api.hooks.Callbacks; +import net.runelite.api.hooks.DrawCallbacks; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetInfo.WORLD_MAP_VIEW; import net.runelite.client.Notifier; @@ -300,6 +303,15 @@ public void draw(MainBufferProvider mainBufferProvider, Graphics graphics, int x // Draw clientUI overlays clientUi.paintOverlays(graphics2d); + graphics2d.dispose(); + + if (client.isGpu()) + { + // processDrawComplete gets called on GPU by the gpu plugin at the end of its + // drawing cycle, which is later on. + return; + } + // Stretch the game image if the user has that enabled if (client.isStretchedEnabled()) { @@ -418,4 +430,36 @@ public void updateNpcs() // have been processed is typically more useful. shouldProcessGameTick = true; } + + public static void renderDraw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash) + { + DrawCallbacks drawCallbacks = client.getDrawCallbacks(); + if (drawCallbacks != null) + { + drawCallbacks.draw(renderable, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash); + } + else + { + renderable.draw(orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash); + } + } + + public static void clearColorBuffer(int x, int y, int width, int height, int color) + { + BufferProvider bp = client.getBufferProvider(); + int canvasWidth = bp.getWidth(); + int[] pixels = bp.getPixels(); + + int pixelPos = y * canvasWidth + x; + int pixelJump = canvasWidth - width; + + for (int cy = y; cy < y + height; cy++) + { + for (int cx = x; cx < x + width; cx++) + { + pixels[pixelPos++] = 0; + } + pixelPos += pixelJump; + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java new file mode 100644 index 00000000000..108e99d3554 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import com.jogamp.opengl.GL4; +import java.io.InputStream; +import java.util.Scanner; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +class GLUtil +{ + private static final int ERR_LEN = 1024; + + private static final int[] buf = new int[1]; + + static int glGetInteger(GL4 gl, int pname) + { + gl.glGetIntegerv(pname, buf, 0); + return buf[0]; + } + + static int glGetShader(GL4 gl, int shader, int pname) + { + gl.glGetShaderiv(shader, pname, buf, 0); + assert buf[0] > -1; + return buf[0]; + } + + static int glGetProgram(GL4 gl, int program, int pname) + { + gl.glGetProgramiv(program, pname, buf, 0); + assert buf[0] > -1; + return buf[0]; + } + + static String glGetShaderInfoLog(GL4 gl, int shader) + { + byte[] err = new byte[ERR_LEN]; + gl.glGetShaderInfoLog(shader, ERR_LEN, buf, 0, err, 0); + return new String(err); + } + + static String glGetProgramInfoLog(GL4 gl, int program) + { + byte[] err = new byte[ERR_LEN]; + gl.glGetProgramInfoLog(program, ERR_LEN, buf, 0, err, 0); + return new String(err); + } + + static int glGenVertexArrays(GL4 gl) + { + gl.glGenVertexArrays(1, buf, 0); + return buf[0]; + } + + static void glDeleteVertexArrays(GL4 gl, int vertexArray) + { + buf[0] = vertexArray; + gl.glDeleteVertexArrays(1, buf, 0); + } + + static int glGenBuffers(GL4 gl) + { + gl.glGenBuffers(1, buf, 0); + return buf[0]; + } + + static void glDeleteBuffer(GL4 gl, int buffer) + { + buf[0] = buffer; + gl.glDeleteBuffers(1, buf, 0); + } + + static int glGenTexture(GL4 gl) + { + gl.glGenTextures(1, buf, 0); + return buf[0]; + } + + static void glDeleteTexture(GL4 gl, int texture) + { + buf[0] = texture; + gl.glDeleteTextures(1, buf, 0); + } + + static void loadShaders(GL4 gl, int glProgram, int glVertexShader, int glGeometryShader, int glFragmentShader, + String vertexShaderStr, String geomShaderStr, String fragShaderStr) throws ShaderException + { + compileAndAttach(gl, glProgram, glVertexShader, vertexShaderStr); + + if (glGeometryShader != -1) + { + compileAndAttach(gl, glProgram, glGeometryShader, geomShaderStr); + } + + compileAndAttach(gl, glProgram, glFragmentShader, fragShaderStr); + + gl.glLinkProgram(glProgram); + + if (glGetProgram(gl, glProgram, gl.GL_LINK_STATUS) == gl.GL_FALSE) + { + String err = glGetProgramInfoLog(gl, glProgram); + throw new ShaderException(err); + } + + gl.glValidateProgram(glProgram); + + if (glGetProgram(gl, glProgram, gl.GL_VALIDATE_STATUS) == gl.GL_FALSE) + { + String err = glGetProgramInfoLog(gl, glProgram); + throw new ShaderException(err); + } + } + + static void loadComputeShader(GL4 gl, int glProgram, int glComputeShader, String str) throws ShaderException + { + compileAndAttach(gl, glProgram, glComputeShader, str); + + gl.glLinkProgram(glProgram); + + if (glGetProgram(gl, glProgram, gl.GL_LINK_STATUS) == gl.GL_FALSE) + { + String err = glGetProgramInfoLog(gl, glProgram); + throw new ShaderException(err); + } + + gl.glValidateProgram(glProgram); + + if (glGetProgram(gl, glProgram, gl.GL_VALIDATE_STATUS) == gl.GL_FALSE) + { + String err = glGetProgramInfoLog(gl, glProgram); + throw new ShaderException(err); + } + } + + private static void compileAndAttach(GL4 gl, int program, int shader, String source) throws ShaderException + { + gl.glShaderSource(shader, 1, new String[]{source}, null); + gl.glCompileShader(shader); + + if (glGetShader(gl, shader, gl.GL_COMPILE_STATUS) == gl.GL_TRUE) + { + gl.glAttachShader(program, shader); + } + else + { + String err = glGetShaderInfoLog(gl, shader); + throw new ShaderException(err); + } + } + + static String inputStreamToString(InputStream in) + { + Scanner scanner = new Scanner(in).useDelimiter("\\A"); + return scanner.next(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java new file mode 100644 index 00000000000..a90d85a16d2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +class GpuFloatBuffer +{ + private FloatBuffer buffer = allocateDirect(65536); + + void put(float texture, float u, float v, float pad) + { + buffer.put(texture).put(u).put(v).put(pad); + } + + void flip() + { + buffer.flip(); + } + + void clear() + { + buffer.clear(); + } + + void ensureCapacity(int size) + { + while (buffer.remaining() < size) + { + FloatBuffer newB = allocateDirect(buffer.capacity() * 2); + buffer.flip(); + newB.put(buffer); + buffer = newB; + } + } + + FloatBuffer getBuffer() + { + return buffer; + } + + static FloatBuffer allocateDirect(int size) + { + return ByteBuffer.allocateDirect(size * Float.BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java new file mode 100644 index 00000000000..15ea5461ade --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; + +class GpuIntBuffer +{ + private IntBuffer buffer = allocateDirect(65536); + + void put(int x, int y, int z) + { + buffer.put(x).put(y).put(z); + } + + void put(int x, int y, int z, int c) + { + buffer.put(x).put(y).put(z).put(c); + } + + void flip() + { + buffer.flip(); + } + + void clear() + { + buffer.clear(); + } + + void ensureCapacity(int size) + { + while (buffer.remaining() < size) + { + IntBuffer newB = allocateDirect(buffer.capacity() * 2); + buffer.flip(); + newB.put(buffer); + buffer = newB; + } + } + + IntBuffer getBuffer() + { + return buffer; + } + + static IntBuffer allocateDirect(int size) + { + return ByteBuffer.allocateDirect(size * Integer.BYTES) + .order(ByteOrder.nativeOrder()) + .asIntBuffer(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java new file mode 100644 index 00000000000..94153a5b599 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -0,0 +1,1144 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; +import com.jogamp.nativewindow.awt.JAWTWindow; +import com.jogamp.opengl.GL; +import static com.jogamp.opengl.GL2ES2.GL_DEBUG_OUTPUT; +import static com.jogamp.opengl.GL3ES3.GL_SHADER_STORAGE_BARRIER_BIT; +import com.jogamp.opengl.GL4; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.GLDrawableFactory; +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.GLProfile; +import java.awt.Canvas; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.function.Function; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.BufferProvider; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.GameState; +import net.runelite.api.Model; +import net.runelite.api.NodeCache; +import net.runelite.api.Perspective; +import net.runelite.api.Renderable; +import net.runelite.api.Scene; +import net.runelite.api.SceneTileModel; +import net.runelite.api.SceneTilePaint; +import net.runelite.api.Texture; +import net.runelite.api.TextureProvider; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.hooks.DrawCallbacks; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginInstantiationException; +import net.runelite.client.plugins.PluginManager; +import static net.runelite.client.plugins.gpu.GLUtil.glDeleteBuffer; +import static net.runelite.client.plugins.gpu.GLUtil.glDeleteTexture; +import static net.runelite.client.plugins.gpu.GLUtil.glDeleteVertexArrays; +import static net.runelite.client.plugins.gpu.GLUtil.glGenBuffers; +import static net.runelite.client.plugins.gpu.GLUtil.glGenTexture; +import static net.runelite.client.plugins.gpu.GLUtil.glGenVertexArrays; +import static net.runelite.client.plugins.gpu.GLUtil.inputStreamToString; +import net.runelite.client.plugins.gpu.template.Template; +import net.runelite.client.ui.DrawManager; + +@PluginDescriptor( + name = "GPU", + description = "Utilizes the GPU", + enabledByDefault = false +) +@Slf4j +public class GpuPlugin extends Plugin implements DrawCallbacks +{ + // This is the maximum number of triangles the compute shaders support + private static final int MAX_TRIANGLE = 4096; + private static final int SMALL_TRIANGLE_COUNT = 512; + private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE; + private static final int MAX_DISTANCE = 90; + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private GpuPluginConfig config; + + @Inject + private TextureManager textureManager; + + @Inject + private SceneUploader sceneUploader; + + @Inject + private DrawManager drawManager; + + @Inject + private PluginManager pluginManager; + + private Canvas canvas; + private JAWTWindow jawtWindow; + private GL4 gl; + private GLDrawable glDrawable; + + private int glProgram; + private int glVertexShader; + private int glGeomShader; + private int glFragmentShader; + + private int glComputeProgram; + private int glComputeShader; + + private int glSmallComputeProgram; + private int glSmallComputeShader; + + private int vaoHandle; + + private int interfaceTexture; + + private int glUiProgram; + private int glUiVertexShader; + private int glUiFragmentShader; + + private int vaoUiHandle; + private int vboUiHandle; + + // scene vertex buffer id + private int bufferId; + // scene uv buffer id + private int uvBufferId; + + private int textureArrayId; + + private final IntBuffer uniformBuffer = GpuIntBuffer.allocateDirect(5); + private final float[] textureOffsets = new float[128]; + + private GpuIntBuffer vertexBuffer; + private GpuFloatBuffer uvBuffer; + + private GpuIntBuffer modelBufferSmall; + private GpuIntBuffer modelBuffer; + + /** + * number of models in small buffer + */ + private int smallModels; + + /** + * number of models in large buffer + */ + private int largeModels; + + /** + * offset in the target buffer for model + */ + private int targetBufferOffset; + + /** + * offset into the temporary scene vertex buffer + */ + private int tempOffset; + + /** + * offset into the temporary scene uv buffer + */ + private int tempUvOffset; + + private int lastViewportWidth; + private int lastViewportHeight; + private int lastCanvasWidth; + private int lastCanvasHeight; + + private int centerX; + private int centerY; + + // Uniforms + private int uniProjectionMatrix; + private int uniBrightness; + private int uniTex; + private int uniTextures; + private int uniTextureOffsets; + private int uniBlockSmall; + private int uniBlockLarge; + private int uniBlockMain; + + @Override + protected void startUp() + { + clientThread.invoke(() -> + { + try + { + bufferId = uvBufferId = -1; + + vertexBuffer = new GpuIntBuffer(); + uvBuffer = new GpuFloatBuffer(); + modelBufferSmall = new GpuIntBuffer(); + modelBuffer = new GpuIntBuffer(); + + canvas = client.getCanvas(); + canvas.setIgnoreRepaint(true); + + if (log.isDebugEnabled()) + { + System.setProperty("jogl.debug", "true"); + } + + GLProfile.initSingleton(); + + GLProfile glProfile = GLProfile.get(GLProfile.GL4bc); + + GLCapabilities glCaps = new GLCapabilities(glProfile); + AbstractGraphicsConfiguration config = AWTGraphicsConfiguration.create(canvas.getGraphicsConfiguration(), + glCaps, glCaps); + + jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(canvas, config); + + GLDrawableFactory glDrawableFactory = GLDrawableFactory.getFactory(glProfile); + + glDrawable = glDrawableFactory.createGLDrawable(jawtWindow); + glDrawable.setRealized(true); + + GLContext glContext = glDrawable.createContext(null); + int res = glContext.makeCurrent(); + if (res == GLContext.CONTEXT_NOT_CURRENT) + { + throw new GLException("Unable to make context current"); + } + + this.gl = glContext.getGL().getGL4(); + gl.setSwapInterval(0); + + if (log.isDebugEnabled()) + { + glContext.enableGLDebugMessage(true); + gl.glEnable(GL_DEBUG_OUTPUT); + } + + initVao(); + initProgram(); + initInterfaceTexture(); + + client.setDrawCallbacks(this); + client.setGpu(true); + + // force rebuild of main buffer provider to enable alpha channel + client.resizeCanvas(); + + lastViewportWidth = lastViewportHeight = lastCanvasWidth = lastCanvasHeight = -1; + + textureArrayId = -1; + + // increase size of model cache for dynamic objects since we are extending scene size + NodeCache cachedModels2 = client.getCachedModels2(); + cachedModels2.setCapacity(256); + cachedModels2.setRemainingCapacity(256); + cachedModels2.reset(); + + if (client.getGameState() == GameState.LOGGED_IN) + { + uploadScene(); + } + } + catch (Throwable e) + { + log.error("Error starting GPU plugin", e); + + try + { + pluginManager.setPluginEnabled(this, false); + pluginManager.stopPlugin(this); + } + catch (PluginInstantiationException ex) + { + log.error("error stopping plugin", ex); + } + + shutDown(); + } + + }); + } + + @Override + protected void shutDown() + { + clientThread.invoke(() -> + { + if (textureArrayId != -1) + { + textureManager.freeTextureArray(gl, textureArrayId); + textureArrayId = -1; + } + + client.setGpu(false); + client.setDrawCallbacks(null); + + if (bufferId != -1) + { + GLUtil.glDeleteBuffer(gl, bufferId); + bufferId = -1; + } + + if (uvBufferId != -1) + { + GLUtil.glDeleteBuffer(gl, uvBufferId); + uvBufferId = -1; + } + + shutdownInterfaceTexture(); + shutdownProgram(); + shutdownVao(); + + vertexBuffer = null; + uvBuffer = null; + modelBufferSmall = null; + modelBuffer = null; + + // force main buffer provider rebuild to turn off alpha channel + client.resizeCanvas(); + }); + } + + @Provides + GpuPluginConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(GpuPluginConfig.class); + } + + private void initProgram() throws ShaderException + { + glProgram = gl.glCreateProgram(); + glVertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER); + glGeomShader = gl.glCreateShader(gl.GL_GEOMETRY_SHADER); + glFragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER); + + Function resourceLoader = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); + Template template = new Template(resourceLoader); + String source = template.process(resourceLoader.apply("geom.glsl")); + + GLUtil.loadShaders(gl, glProgram, glVertexShader, glGeomShader, glFragmentShader, + inputStreamToString(getClass().getResourceAsStream("vert.glsl")), + source, + inputStreamToString(getClass().getResourceAsStream("frag.glsl"))); + + glComputeProgram = gl.glCreateProgram(); + glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); + template = new Template(resourceLoader); + source = template.process(resourceLoader.apply("comp.glsl")); + GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); + + glSmallComputeProgram = gl.glCreateProgram(); + glSmallComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); + template = new Template(resourceLoader); + source = template.process(resourceLoader.apply("comp_small.glsl")); + GLUtil.loadComputeShader(gl, glSmallComputeProgram, glSmallComputeShader, source); + + glUiProgram = gl.glCreateProgram(); + glUiVertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER); + glUiFragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER); + GLUtil.loadShaders(gl, glUiProgram, glUiVertexShader, -1, glUiFragmentShader, + inputStreamToString(getClass().getResourceAsStream("vertui.glsl")), + null, + inputStreamToString(getClass().getResourceAsStream("fragui.glsl"))); + + initUniforms(); + } + + private void initUniforms() + { + uniProjectionMatrix = gl.glGetUniformLocation(glProgram, "projectionMatrix"); + uniBrightness = gl.glGetUniformLocation(glProgram, "brightness"); + + uniTex = gl.glGetUniformLocation(glUiProgram, "tex"); + uniTextures = gl.glGetUniformLocation(glProgram, "textures"); + uniTextureOffsets = gl.glGetUniformLocation(glProgram, "textureOffsets"); + + uniBlockSmall = gl.glGetUniformBlockIndex(glSmallComputeProgram, "uniforms"); + uniBlockLarge = gl.glGetUniformBlockIndex(glComputeProgram, "uniforms"); + uniBlockMain = gl.glGetUniformBlockIndex(glProgram, "uniforms"); + } + + private void shutdownProgram() + { + gl.glDeleteShader(glVertexShader); + glVertexShader = -1; + + gl.glDeleteShader(glGeomShader); + glGeomShader = -1; + + gl.glDeleteShader(glFragmentShader); + glFragmentShader = -1; + + gl.glDeleteProgram(glProgram); + glProgram = -1; + + /// + + gl.glDeleteShader(glComputeShader); + glComputeShader = -1; + + gl.glDeleteProgram(glComputeProgram); + glComputeProgram = -1; + + gl.glDeleteShader(glSmallComputeShader); + glSmallComputeShader = -1; + + gl.glDeleteProgram(glSmallComputeProgram); + glSmallComputeProgram = -1; + + /// + + gl.glDeleteShader(glUiVertexShader); + glUiVertexShader = -1; + + gl.glDeleteShader(glUiFragmentShader); + glUiFragmentShader = -1; + + gl.glDeleteProgram(glUiProgram); + glUiProgram = -1; + } + + private void initVao() + { + // Create VAO + vaoHandle = glGenVertexArrays(gl); + + // Create UI VAO + vaoUiHandle = glGenVertexArrays(gl); + // Create UI buffer + vboUiHandle = glGenBuffers(gl); + gl.glBindVertexArray(vaoUiHandle); + + FloatBuffer vboUiBuf = GpuFloatBuffer.allocateDirect(5 * 4); + vboUiBuf.put(new float[]{ + // positions // texture coords + 1f, 1f, 0.0f, 1.0f, 0f, // top right + 1f, -1f, 0.0f, 1.0f, 1f, // bottom right + -1f, -1f, 0.0f, 0.0f, 1f, // bottom left + -1f, 1f, 0.0f, 0.0f, 0f // top left + }); + vboUiBuf.rewind(); + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vboUiHandle); + gl.glBufferData(gl.GL_ARRAY_BUFFER, vboUiBuf.capacity() * Float.BYTES, vboUiBuf, gl.GL_STATIC_DRAW); + + // position attribute + gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, false, 5 * Float.BYTES, 0); + gl.glEnableVertexAttribArray(0); + + // texture coord attribute + gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES); + gl.glEnableVertexAttribArray(1); + + // unbind VBO + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0); + } + + private void shutdownVao() + { + glDeleteVertexArrays(gl, vaoHandle); + vaoHandle = -1; + + glDeleteBuffer(gl, vboUiHandle); + vboUiHandle = -1; + + glDeleteVertexArrays(gl, vaoUiHandle); + vaoUiHandle = -1; + } + + private void initInterfaceTexture() + { + interfaceTexture = glGenTexture(gl); + gl.glBindTexture(gl.GL_TEXTURE_2D, interfaceTexture); + gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT); + gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT); + gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR); + gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR); + gl.glBindTexture(gl.GL_TEXTURE_2D, 0); + } + + private void shutdownInterfaceTexture() + { + glDeleteTexture(gl, interfaceTexture); + interfaceTexture = -1; + } + + private void createProjectionMatrix(float left, float right, float bottom, float top, float near, float far) + { + // create a standard orthographic projection + float tx = -((right + left) / (right - left)); + float ty = -((top + bottom) / (top - bottom)); + float tz = -((far + near) / (far - near)); + + gl.glUseProgram(glProgram); + + float[] matrix = new float[]{ + 2 / (right - left), 0, 0, 0, + 0, 2 / (top - bottom), 0, 0, + 0, 0, -2 / (far - near), 0, + tx, ty, tz, 1 + }; + gl.glUniformMatrix4fv(uniProjectionMatrix, 1, false, matrix, 0); + + gl.glUseProgram(0); + } + + @Override + public void drawScene(int cameraX, int cameraY, int cameraZ, int cameraPitch, int cameraYaw, int plane) + { + centerX = client.getCenterX(); + centerY = client.getCenterY(); + + final Scene scene = client.getScene(); + final int drawDistance = Math.max(0, Math.min(MAX_DISTANCE, config.drawDistance())); + scene.setDrawDistance(drawDistance); + } + + + public void drawScenePaint(int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, + SceneTilePaint paint, int tileZ, int tileX, int tileY, + int zoom, int centerX, int centerY) + { + if (paint.getBufferLen() > 0) + { + x = tileX * Perspective.LOCAL_TILE_SIZE; + y = 0; + z = tileY * Perspective.LOCAL_TILE_SIZE; + + x -= client.getCameraX2(); + y -= client.getCameraY2(); + z -= client.getCameraZ2(); + + GpuIntBuffer b = bufferForTriangles(2); + + b.ensureCapacity(8); + IntBuffer buffer = b.getBuffer(); + buffer.put(paint.getBufferOffset()); + buffer.put(paint.getUvBufferOffset()); + buffer.put(2); + buffer.put(targetBufferOffset); + buffer.put(FLAG_SCENE_BUFFER); + buffer.put(x).put(y).put(z); + + targetBufferOffset += 2 * 3; + } + } + + public void drawSceneModel(int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, + SceneTileModel model, int tileZ, int tileX, int tileY, + int zoom, int centerX, int centerY) + { + if (model.getBufferLen() > 0) + { + x = tileX * Perspective.LOCAL_TILE_SIZE; + y = 0; + z = tileY * Perspective.LOCAL_TILE_SIZE; + + x -= client.getCameraX2(); + y -= client.getCameraY2(); + z -= client.getCameraZ2(); + + GpuIntBuffer b = bufferForTriangles(model.getBufferLen() / 3); + + b.ensureCapacity(8); + IntBuffer buffer = b.getBuffer(); + buffer.put(model.getBufferOffset()); + buffer.put(model.getUvBufferOffset()); + buffer.put(model.getBufferLen() / 3); + buffer.put(targetBufferOffset); + buffer.put(FLAG_SCENE_BUFFER); + buffer.put(x).put(y).put(z); + + targetBufferOffset += model.getBufferLen(); + } + } + + @Override + public void draw() + { + if (jawtWindow.getAWTComponent() != client.getCanvas()) + { + // We inject code in the game engine mixin to prevent the client from doing canvas replacement, + // so this should not ever be hit + log.warn("Canvas invalidated!"); + shutDown(); + startUp(); + return; + } + + if (client.getGameState() == GameState.LOADING || client.getGameState() == GameState.HOPPING) + { + // While the client is loading it doesn't draw + return; + } + + final int canvasHeight = client.getCanvasHeight(); + final int canvasWidth = client.getCanvasWidth(); + + final int viewportHeight = client.getViewportHeight(); + final int viewportWidth = client.getViewportWidth(); + + gl.glClear(gl.GL_COLOR_BUFFER_BIT); + + // If the viewport has changed, update the projection matrix + if (viewportWidth > 0 && viewportHeight > 0 && (viewportWidth != lastViewportWidth || viewportHeight != lastViewportHeight)) + { + createProjectionMatrix(0, viewportWidth, viewportHeight, 0, 0, Constants.SCENE_SIZE * Perspective.LOCAL_TILE_SIZE); + lastViewportWidth = viewportWidth; + lastViewportHeight = viewportHeight; + } + + // Upload buffers + vertexBuffer.flip(); + uvBuffer.flip(); + modelBuffer.flip(); + modelBufferSmall.flip(); + + int bufferId = glGenBuffers(gl); // temporary scene vertex buffer + int uvBufferId = glGenBuffers(gl); // temporary scene uv buffer + int modelBufferId = glGenBuffers(gl); // scene model buffer, large + int modelBufferSmallId = glGenBuffers(gl); // scene model buffer, small + + IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); + FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); + IntBuffer modelBuffer = this.modelBuffer.getBuffer(); + IntBuffer modelBufferSmall = this.modelBufferSmall.getBuffer(); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, bufferId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_STREAM_DRAW); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, uvBufferId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_STREAM_DRAW); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, modelBufferId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBuffer.limit() * Integer.BYTES, modelBuffer, gl.GL_STREAM_DRAW); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, modelBufferSmallId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBufferSmall.limit() * Integer.BYTES, modelBufferSmall, gl.GL_STREAM_DRAW); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0); + + // allocate target vertex buffer for compute shaders + int outBufferId = glGenBuffers(gl); + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, outBufferId); + + gl.glBufferData(gl.GL_ARRAY_BUFFER, + targetBufferOffset * 16, // each vertex is an ivec4, which is 16 bytes + null, + gl.GL_STREAM_DRAW); + + // allocate target uv buffer for compute shaders + int outUvBufferId = glGenBuffers(gl); + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, outUvBufferId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, + targetBufferOffset * 16, + null, + gl.GL_STREAM_DRAW); + + // UBO + int uniformBufferId = glGenBuffers(gl); + gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, uniformBufferId); + uniformBuffer.clear(); + uniformBuffer + .put(client.getCameraYaw()) + .put(client.getCameraPitch()) + .put(centerX) + .put(centerY) + .put(client.getScale()); + uniformBuffer.flip(); + + gl.glBufferData(gl.GL_UNIFORM_BUFFER, uniformBuffer.limit() * Integer.BYTES, uniformBuffer, gl.GL_STATIC_DRAW); + gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, 0); + + gl.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0); + gl.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0); + + gl.glBindBufferBase(gl.GL_UNIFORM_BUFFER, 0, uniformBufferId); + + /* + * Compute is split into two separate programs 'small' and 'large' to + * save on GPU resources. Small will sort <= 512 faces, large will do <= 4096. + */ + + // small + gl.glUseProgram(glSmallComputeProgram); + + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, modelBufferSmallId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, bufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, outBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, outUvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, uvBufferId); + + gl.glDispatchCompute(smallModels, 1, 1); + + // large + gl.glUseProgram(glComputeProgram); + + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, modelBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, bufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, outBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, outUvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, uvBufferId); + + gl.glDispatchCompute(largeModels, 1, 1); + + gl.glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + // Draw 3d scene + final TextureProvider textureProvider = client.getTextureProvider(); + if (textureProvider != null) + { + if (textureArrayId == -1) + { + // lazy init textures as they may not be loaded at plugin start. + // this will return -1 and retry if not all textures are loaded yet, too. + textureArrayId = textureManager.initTextureArray(textureProvider, gl); + } + + final Texture[] textures = textureProvider.getTextures(); + final int heightOff = client.getViewportYOffset(); + final int widthOff = client.getViewportXOffset(); + + gl.glViewport(widthOff, canvasHeight - viewportHeight - heightOff, viewportWidth, viewportHeight); + + gl.glUseProgram(glProgram); + + // Brightness happens to also be stored in the texture provider, so we use that + gl.glUniform1f(uniBrightness, (float) textureProvider.getBrightness()); + + for (int id = 0; id < textures.length; ++id) + { + Texture texture = textures[id]; + if (texture == null) + { + continue; + } + + textureProvider.load(id); // trips the texture load flag which lets textures animate + + textureOffsets[id * 2] = texture.getU(); + textureOffsets[id * 2 + 1] = texture.getV(); + } + + // Bind uniforms + gl.glUniformBlockBinding(glProgram, uniBlockMain, 0); + gl.glUniform1i(uniTextures, 1); // texture sampler array is bound to texture1 + gl.glUniform2fv(uniTextureOffsets, 128, textureOffsets, 0); + + // We just allow the GL to do face culling. Note this requires the priority renderer + // to have logic to disregard culled faces in the priority depth testing. + gl.glEnable(gl.GL_CULL_FACE); + + // Enable blending for alpha + gl.glEnable(gl.GL_BLEND); + gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA); + + // Draw output of compute shaders + gl.glBindVertexArray(vaoHandle); + + gl.glEnableVertexAttribArray(0); + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, outBufferId); + gl.glVertexAttribIPointer(0, 4, gl.GL_INT, 0, 0); + + gl.glEnableVertexAttribArray(1); + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, outUvBufferId); + gl.glVertexAttribPointer(1, 4, gl.GL_FLOAT, false, 0, 0); + + gl.glDrawArrays(gl.GL_TRIANGLES, 0, targetBufferOffset); + + gl.glDisable(gl.GL_BLEND); + gl.glDisable(gl.GL_CULL_FACE); + + gl.glUseProgram(0); + } + + glDeleteBuffer(gl, uniformBufferId); + + vertexBuffer.clear(); + uvBuffer.clear(); + modelBuffer.clear(); + modelBufferSmall.clear(); + + targetBufferOffset = 0; + smallModels = largeModels = 0; + tempOffset = 0; + tempUvOffset = 0; + + glDeleteBuffer(gl, bufferId); + glDeleteBuffer(gl, uvBufferId); + glDeleteBuffer(gl, modelBufferId); + glDeleteBuffer(gl, modelBufferSmallId); + glDeleteBuffer(gl, outBufferId); + glDeleteBuffer(gl, outUvBufferId); + + // Texture on UI + drawUi(canvasHeight, canvasWidth); + + glDrawable.swapBuffers(); + + drawManager.processDrawComplete(this::screenshot); + } + + private void drawUi(final int canvasHeight, final int canvasWidth) + { + final BufferProvider bufferProvider = client.getBufferProvider(); + final int[] pixels = bufferProvider.getPixels(); + final int width = bufferProvider.getWidth(); + final int height = bufferProvider.getHeight(); + + if (client.getGameState() == GameState.LOGGED_IN) + { + gl.glEnable(gl.GL_BLEND); + } + else + { + gl.glDisable(gl.GL_BLEND); + } + + gl.glViewport(0, 0, canvasWidth, canvasHeight); + + vertexBuffer.clear(); // reuse vertex buffer for interface + vertexBuffer.ensureCapacity(pixels.length); + + IntBuffer interfaceBuffer = vertexBuffer.getBuffer(); + interfaceBuffer.put(pixels); + vertexBuffer.flip(); + + gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA); + gl.glBindTexture(gl.GL_TEXTURE_2D, interfaceTexture); + + if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) + { + gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, interfaceBuffer); + lastCanvasWidth = canvasWidth; + lastCanvasHeight = canvasHeight; + } + else + { + gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, width, height, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, interfaceBuffer); + } + + gl.glUseProgram(glUiProgram); + + // Bind texture to shader + gl.glActiveTexture(gl.GL_TEXTURE0); + gl.glBindTexture(gl.GL_TEXTURE_2D, interfaceTexture); + gl.glUniform1i(uniTex, 0); + + // Texture on UI + gl.glBindVertexArray(vaoUiHandle); + gl.glDrawArrays(gl.GL_QUADS, 0, 4); + + // Reset + gl.glBindTexture(gl.GL_TEXTURE_2D, 0); + gl.glBindVertexArray(0); + gl.glUseProgram(0); + gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA); + gl.glDisable(gl.GL_BLEND); + + vertexBuffer.clear(); + } + + /** + * Convert the front framebuffer to an Image + * + * @return + */ + private Image screenshot() + { + final int width = client.getCanvasWidth(); + final int height = client.getCanvasHeight(); + + ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4) + .order(ByteOrder.nativeOrder()); + + gl.glReadBuffer(gl.GL_FRONT); + gl.glReadPixels(0, 0, width, height, GL.GL_RGBA, gl.GL_UNSIGNED_BYTE, buffer); + + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + int r = buffer.get() & 0xff; + int g = buffer.get() & 0xff; + int b = buffer.get() & 0xff; + buffer.get(); // alpha + + pixels[(height - y - 1) * width + x] = (r << 16) | (g << 8) | b; + } + } + + return image; + } + + @Override + public void animate(Texture texture, int diff) + { + textureManager.animate(texture, diff); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + if (gameStateChanged.getGameState() != GameState.LOGGED_IN) + { + return; + } + + uploadScene(); + } + + private void uploadScene() + { + vertexBuffer.clear(); + uvBuffer.clear(); + + sceneUploader.upload(client.getScene(), vertexBuffer, uvBuffer); + + vertexBuffer.flip(); + uvBuffer.flip(); + + IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); + FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); + + if (bufferId != -1) + { + GLUtil.glDeleteBuffer(gl, bufferId); + bufferId = -1; + } + + if (uvBufferId != -1) + { + GLUtil.glDeleteBuffer(gl, uvBufferId); + uvBufferId = -1; + } + + bufferId = glGenBuffers(gl); + uvBufferId = glGenBuffers(gl); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, bufferId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_STATIC_COPY); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, uvBufferId); + gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_STATIC_COPY); + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0); + + vertexBuffer.clear(); + uvBuffer.clear(); + } + + /** + * Check is a model is visible and should be drawn. + */ + private boolean isVisible(Model model, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int _x, int _y, int _z, long hash) + { + final int XYZMag = model.getXYZMag(); + final int zoom = client.get3dZoom(); + final int modelHeight = model.getModelHeight(); + + int Rasterizer3D_clipMidX2 = client.getRasterizer3D_clipMidX2(); + int Rasterizer3D_clipNegativeMidX = client.getRasterizer3D_clipNegativeMidX(); + int Rasterizer3D_clipNegativeMidY = client.getRasterizer3D_clipNegativeMidY(); + int Rasterizer3D_clipMidY2 = client.getRasterizer3D_clipMidY2(); + + int var11 = yawCos * _z - yawSin * _x >> 16; + int var12 = pitchSin * _y + pitchCos * var11 >> 16; + int var13 = pitchCos * XYZMag >> 16; + int var14 = var12 + var13; + if (var14 > 50) + { + int var15 = _z * yawSin + yawCos * _x >> 16; + int var16 = (var15 - XYZMag) * zoom; + if (var16 / var14 < Rasterizer3D_clipMidX2) + { + int var17 = (var15 + XYZMag) * zoom; + if (var17 / var14 > Rasterizer3D_clipNegativeMidX) + { + int var18 = pitchCos * _y - var11 * pitchSin >> 16; + int var19 = pitchSin * XYZMag >> 16; + int var20 = (var18 + var19) * zoom; + if (var20 / var14 > Rasterizer3D_clipNegativeMidY) + { + int var21 = (pitchCos * modelHeight >> 16) + var19; + int var22 = (var18 - var21) * zoom; + if (var22 / var14 < Rasterizer3D_clipMidY2) + { + return true; + } + } + } + } + } + return false; + } + + /** + * Draw a renderable in the scene + * + * @param renderable + * @param orientation + * @param pitchSin + * @param pitchCos + * @param yawSin + * @param yawCos + * @param x + * @param y + * @param z + * @param hash + */ + @Override + public void draw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash) + { + // Model may be in the scene buffer + if (renderable instanceof Model && ((Model) renderable).getSceneId() == sceneUploader.sceneId) + { + Model model = (Model) renderable; + + model.calculateBoundsCylinder(); + model.calculateExtreme(orientation); + + if (!isVisible(model, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash)) + { + return; + } + + client.checkClickbox(model, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash); + + int tc = Math.min(MAX_TRIANGLE, model.getTrianglesCount()); + int uvOffset = model.getUvBufferOffset(); + + GpuIntBuffer b = bufferForTriangles(tc); + + b.ensureCapacity(8); + IntBuffer buffer = b.getBuffer(); + buffer.put(model.getBufferOffset()); + buffer.put(uvOffset); + buffer.put(tc); + buffer.put(targetBufferOffset); + buffer.put(FLAG_SCENE_BUFFER | (model.getRadius() << 12) | orientation); + buffer.put(x).put(y).put(z); + + targetBufferOffset += tc * 3; + } + else + { + // Temporary model (animated or otherwise not a static Model on the scene) + Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel(); + if (model != null) + { + // Apply height to renderable from the model + model.setModelHeight(model.getModelHeight()); + + model.calculateBoundsCylinder(); + model.calculateExtreme(orientation); + + if (!isVisible(model, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash)) + { + return; + } + + client.checkClickbox(model, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash); + + boolean hasUv = model.getFaceTextures() != null; + + int faces = Math.min(MAX_TRIANGLE, model.getTrianglesCount()); + vertexBuffer.ensureCapacity(12 * faces); + uvBuffer.ensureCapacity(12 * faces); + int len = 0; + for (int i = 0; i < faces; ++i) + { + len += sceneUploader.pushFace(model, i, vertexBuffer, uvBuffer); + } + + GpuIntBuffer b = bufferForTriangles(faces); + + b.ensureCapacity(8); + IntBuffer buffer = b.getBuffer(); + buffer.put(tempOffset); + buffer.put(hasUv ? tempUvOffset : -1); + buffer.put(len / 3); + buffer.put(targetBufferOffset); + buffer.put((model.getRadius() << 12) | orientation); + buffer.put(x).put(y).put(z); + + tempOffset += len; + if (hasUv) + { + tempUvOffset += len; + } + + targetBufferOffset += len; + } + } + } + + /** + * returns the correct buffer based on triangle count and updates model count + * + * @param triangles + * @return + */ + private GpuIntBuffer bufferForTriangles(int triangles) + { + if (triangles < SMALL_TRIANGLE_COUNT) + { + ++smallModels; + return modelBufferSmall; + } + else + { + ++largeModels; + return modelBuffer; + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java new file mode 100644 index 00000000000..5e2cedd8dc6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("gpu") +public interface GpuPluginConfig extends Config +{ + @ConfigItem( + keyName = "drawDistance", + name = "Draw Distance", + description = "Draw distance" + ) + default int drawDistance() + { + return 25; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java new file mode 100644 index 00000000000..7749f82820c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.DecorativeObject; +import net.runelite.api.GameObject; +import net.runelite.api.GroundObject; +import net.runelite.api.Model; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.Renderable; +import net.runelite.api.Scene; +import net.runelite.api.SceneTileModel; +import net.runelite.api.SceneTilePaint; +import net.runelite.api.Tile; +import net.runelite.api.WallObject; + +@Singleton +class SceneUploader +{ + @Inject + private Client client; + + int sceneId = (int) (System.currentTimeMillis() / 1000L); + private int offset; + private int uvoffset; + + void upload(Scene scene, GpuIntBuffer vertexbuffer, GpuFloatBuffer uvBuffer) + { + ++sceneId; + offset = 0; + uvoffset = 0; + vertexbuffer.clear(); + uvBuffer.clear(); + + for (int z = 0; z < Constants.MAX_Z; ++z) + { + for (int x = 0; x < Constants.SCENE_SIZE; ++x) + { + for (int y = 0; y < Constants.SCENE_SIZE; ++y) + { + Tile tile = scene.getTiles()[z][x][y]; + if (tile != null) + { + reset(tile); + } + } + } + } + + for (int z = 0; z < Constants.MAX_Z; ++z) + { + for (int x = 0; x < Constants.SCENE_SIZE; ++x) + { + for (int y = 0; y < Constants.SCENE_SIZE; ++y) + { + Tile tile = scene.getTiles()[z][x][y]; + if (tile != null) + { + upload(tile, vertexbuffer, uvBuffer); + } + } + } + } + } + + private void reset(Tile tile) + { + Tile bridge = tile.getBridge(); + if (bridge != null) + { + reset(bridge); + } + + SceneTilePaint sceneTilePaint = tile.getSceneTilePaint(); + if (sceneTilePaint != null) + { + sceneTilePaint.setBufferOffset(-1); + } + + SceneTileModel sceneTileModel = tile.getSceneTileModel(); + if (sceneTileModel != null) + { + sceneTileModel.setBufferOffset(-1); + } + + WallObject wallObject = tile.getWallObject(); + if (wallObject != null) + { + if (wallObject.getRenderable1() instanceof Model) + { + ((Model) wallObject.getRenderable1()).setBufferOffset(-1); + } + if (wallObject.getRenderable2() instanceof Model) + { + ((Model) wallObject.getRenderable2()).setBufferOffset(-1); + } + } + + GroundObject groundObject = tile.getGroundObject(); + if (groundObject != null) + { + if (groundObject.getRenderable() instanceof Model) + { + ((Model) groundObject.getRenderable()).setBufferOffset(-1); + } + } + + DecorativeObject decorativeObject = tile.getDecorativeObject(); + if (decorativeObject != null) + { + if (decorativeObject.getRenderable() instanceof Model) + { + ((Model) decorativeObject.getRenderable()).setBufferOffset(-1); + } + } + + GameObject[] gameObjects = tile.getGameObjects(); + for (GameObject gameObject : gameObjects) + { + if (gameObject == null) + { + continue; + } + if (gameObject.getRenderable() instanceof Model) + { + ((Model) gameObject.getRenderable()).setBufferOffset(-1); + } + } + } + + private void upload(Tile tile, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + { + Tile bridge = tile.getBridge(); + if (bridge != null) + { + upload(bridge, vertexBuffer, uvBuffer); + } + + SceneTilePaint sceneTilePaint = tile.getSceneTilePaint(); + if (sceneTilePaint != null) + { + sceneTilePaint.setBufferOffset(offset); + if (sceneTilePaint.getTexture() != -1) + { + sceneTilePaint.setUvBufferOffset(uvoffset); + } + else + { + sceneTilePaint.setUvBufferOffset(-1); + } + Point tilePoint = tile.getSceneLocation(); + int len = upload(sceneTilePaint, tile.getRenderLevel(), tilePoint.getX(), tilePoint.getY(), vertexBuffer, uvBuffer); + sceneTilePaint.setBufferLen(len); + offset += len; + if (sceneTilePaint.getTexture() != -1) + { + uvoffset += len; + } + } + + SceneTileModel sceneTileModel = tile.getSceneTileModel(); + if (sceneTileModel != null) + { + sceneTileModel.setBufferOffset(offset); + if (sceneTileModel.getTriangleTextureId() != null) + { + sceneTileModel.setUvBufferOffset(uvoffset); + } + else + { + sceneTileModel.setUvBufferOffset(-1); + } + Point tilePoint = tile.getSceneLocation(); + int len = upload(sceneTileModel, tilePoint.getX(), tilePoint.getY(), vertexBuffer, uvBuffer); + sceneTileModel.setBufferLen(len); + offset += len; + if (sceneTileModel.getTriangleTextureId() != null) + { + uvoffset += len; + } + } + + WallObject wallObject = tile.getWallObject(); + if (wallObject != null) + { + Renderable renderable1 = wallObject.getRenderable1(); + if (renderable1 instanceof Model) + { + uploadModel((Model) renderable1, vertexBuffer, uvBuffer); + } + + Renderable renderable2 = wallObject.getRenderable2(); + if (renderable2 instanceof Model) + { + uploadModel((Model) renderable2, vertexBuffer, uvBuffer); + } + } + + GroundObject groundObject = tile.getGroundObject(); + if (groundObject != null) + { + Renderable renderable = groundObject.getRenderable(); + if (renderable instanceof Model) + { + uploadModel((Model) renderable, vertexBuffer, uvBuffer); + } + } + + DecorativeObject decorativeObject = tile.getDecorativeObject(); + if (decorativeObject != null) + { + Renderable renderable = decorativeObject.getRenderable(); + if (renderable instanceof Model) + { + uploadModel((Model) renderable, vertexBuffer, uvBuffer); + } + + Renderable renderable2 = decorativeObject.getRenderable2(); + if (renderable2 instanceof Model) + { + uploadModel((Model) renderable2, vertexBuffer, uvBuffer); + } + } + + GameObject[] gameObjects = tile.getGameObjects(); + for (GameObject gameObject : gameObjects) + { + if (gameObject == null) + { + continue; + } + + Renderable renderable = gameObject.getRenderable(); + if (renderable instanceof Model) + { + uploadModel((Model) gameObject.getRenderable(), vertexBuffer, uvBuffer); + } + } + } + + private int upload(SceneTilePaint tile, int tileZ, int tileX, int tileY, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + { + final int[][][] tileHeights = client.getTileHeights(); + + final int localX = 0; + final int localY = 0; + + int swHeight = tileHeights[tileZ][tileX][tileY]; + int seHeight = tileHeights[tileZ][tileX + 1][tileY]; + int neHeight = tileHeights[tileZ][tileX + 1][tileY + 1]; + int nwHeight = tileHeights[tileZ][tileX][tileY + 1]; + + final int neColor = tile.getNeColor(); + final int nwColor = tile.getNwColor(); + final int seColor = tile.getSeColor(); + final int swColor = tile.getSwColor(); + + if (neColor == 12345678) + { + return 0; + } + + vertexBuffer.ensureCapacity(24); + uvBuffer.ensureCapacity(24); + + // 0,0 + int vertexDx = localX; + int vertexDy = localY; + int vertexDz = swHeight; + final int c1 = swColor; + + // 1,0 + int vertexCx = localX + Perspective.LOCAL_TILE_SIZE; + int vertexCy = localY; + int vertexCz = seHeight; + final int c2 = nwColor; + + // 1,1 + int vertexAx = localX + Perspective.LOCAL_TILE_SIZE; + int vertexAy = localY + Perspective.LOCAL_TILE_SIZE; + int vertexAz = neHeight; + final int c3 = neColor; + + // 0,1 + int vertexBx = localX; + int vertexBy = localY + Perspective.LOCAL_TILE_SIZE; + int vertexBz = nwHeight; + final int c4 = seColor; + + vertexBuffer.put(vertexAx, vertexAz, vertexAy, c3); + vertexBuffer.put(vertexBx, vertexBz, vertexBy, c4); + vertexBuffer.put(vertexCx, vertexCz, vertexCy, c2); + + vertexBuffer.put(vertexDx, vertexDz, vertexDy, c1); + vertexBuffer.put(vertexCx, vertexCz, vertexCy, c2); + vertexBuffer.put(vertexBx, vertexBz, vertexBy, c4); + + if (tile.getTexture() != -1) + { + float tex = tile.getTexture(); + uvBuffer.put(tex, 1.0f + 1f, 1.0f + 1f, 0f); + uvBuffer.put(tex, 0.0f + 1f, 1.0f + 1f, 0f); + uvBuffer.put(tex, 1.0f + 1f, 0.0f + 1f, 0f); + + uvBuffer.put(tex, 0.0f + 1f, 0.0f + 1f, 0f); + uvBuffer.put(tex, 1.0f + 1f, 0.0f + 1f, 0f); + uvBuffer.put(tex, 0.0f + 1f, 1.0f + 1f, 0f); + } + + return 6; + } + + private int upload(SceneTileModel sceneTileModel, int tileX, int tileY, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + { + final int[] faceX = sceneTileModel.getFaceX(); + final int[] faceY = sceneTileModel.getFaceY(); + final int[] faceZ = sceneTileModel.getFaceZ(); + + final int[] vertexX = sceneTileModel.getVertexX(); + final int[] vertexY = sceneTileModel.getVertexY(); + final int[] vertexZ = sceneTileModel.getVertexZ(); + + final int[] triangleColorA = sceneTileModel.getTriangleColorA(); + final int[] triangleColorB = sceneTileModel.getTriangleColorB(); + final int[] triangleColorC = sceneTileModel.getTriangleColorC(); + + final int[] triangleTextures = sceneTileModel.getTriangleTextureId(); + + final int faceCount = faceX.length; + + vertexBuffer.ensureCapacity(faceCount * 12); + uvBuffer.ensureCapacity(faceCount * 12); + + int baseX = Perspective.LOCAL_TILE_SIZE * tileX; + int baseY = Perspective.LOCAL_TILE_SIZE * tileY; + + int cnt = 0; + for (int i = 0; i < faceCount; ++i) + { + final int triangleA = faceX[i]; + final int triangleB = faceY[i]; + final int triangleC = faceZ[i]; + + final int colorA = triangleColorA[i]; + final int colorB = triangleColorB[i]; + final int colorC = triangleColorC[i]; + + if (colorA == 12345678) + { + continue; + } + + cnt += 3; + + int vertexXA = vertexX[triangleA] - baseX; + int vertexZA = vertexZ[triangleA] - baseY; + + int vertexXB = vertexX[triangleB] - baseX; + int vertexZB = vertexZ[triangleB] - baseY; + + int vertexXC = vertexX[triangleC] - baseX; + int vertexZC = vertexZ[triangleC] - baseY; + + vertexBuffer.put(vertexXA, vertexY[triangleA], vertexZA, colorA); + vertexBuffer.put(vertexXB, vertexY[triangleB], vertexZB, colorB); + vertexBuffer.put(vertexXC, vertexY[triangleC], vertexZC, colorC); + + if (triangleTextures != null) + { + if (triangleTextures[i] != -1) + { + float tex = triangleTextures[i]; + uvBuffer.put(tex, vertexXA / 128f + 1f, vertexZA / 128f + 1f, 0f); + uvBuffer.put(tex, vertexXB / 128f + 1f, vertexZB / 128f + 1f, 0f); + uvBuffer.put(tex, vertexXC / 128f + 1f, vertexZC / 128f + 1f, 0f); + } + else + { + uvBuffer.put(0, 0, 0, 0f); + uvBuffer.put(0, 0, 0, 0f); + uvBuffer.put(0, 0, 0, 0f); + } + } + } + + return cnt; + } + + private void uploadModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + { + if (model.getBufferOffset() > 0) + { + return; + } + + model.setBufferOffset(offset); + if (model.getFaceTextures() != null) + { + model.setUvBufferOffset(uvoffset); + } + else + { + model.setUvBufferOffset(-1); + } + model.setSceneId(sceneId); + + vertexBuffer.ensureCapacity(model.getTrianglesCount() * 12); + uvBuffer.ensureCapacity(model.getTrianglesCount() * 12); + + final int triangleCount = model.getTrianglesCount(); + int len = 0; + for (int i = 0; i < triangleCount; ++i) + { + len += pushFace(model, i, vertexBuffer, uvBuffer); + } + + offset += len; + if (model.getFaceTextures() != null) + { + uvoffset += len; + } + } + + int pushFace(Model model, int face, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + { + final int[] vertexX = model.getVerticesX(); + final int[] vertexY = model.getVerticesY(); + final int[] vertexZ = model.getVerticesZ(); + + final int[] trianglesX = model.getTrianglesX(); + final int[] trianglesY = model.getTrianglesY(); + final int[] trianglesZ = model.getTrianglesZ(); + + final int[] color1s = model.getFaceColors1(); + final int[] color2s = model.getFaceColors2(); + final int[] color3s = model.getFaceColors3(); + + final byte[] transparencies = model.getTriangleTransparencies(); + final short[] faceTextures = model.getFaceTextures(); + final byte[] facePriorities = model.getFaceRenderPriorities(); + + int triangleA = trianglesX[face]; + int triangleB = trianglesY[face]; + int triangleC = trianglesZ[face]; + + int color1 = color1s[face]; + int color2 = color2s[face]; + int color3 = color3s[face]; + + int alpha = 0; + if (transparencies != null) + { + alpha = (transparencies[face] & 0xFF) << 24; + } + int priority = 0; + if (facePriorities != null) + { + priority = (facePriorities[face] & 0xff) << 16; + } + + if (color3 == -1) + { + color2 = color3 = color1; + } + else if (color3 == -2) + { + vertexBuffer.put(0, 0, 0, 0); + vertexBuffer.put(0, 0, 0, 0); + vertexBuffer.put(0, 0, 0, 0); + + if (faceTextures != null) + { + uvBuffer.put(0, 0, 0, 0f); + uvBuffer.put(0, 0, 0, 0f); + uvBuffer.put(0, 0, 0, 0f); + } + return 3; + } + + int a, b, c; + + a = vertexX[triangleA]; + b = vertexY[triangleA]; + c = vertexZ[triangleA]; + + vertexBuffer.put(a, b, c, alpha | priority | color1); + + a = vertexX[triangleB]; + b = vertexY[triangleB]; + c = vertexZ[triangleB]; + + vertexBuffer.put(a, b, c, alpha | priority | color2); + + a = vertexX[triangleC]; + b = vertexY[triangleC]; + c = vertexZ[triangleC]; + + vertexBuffer.put(a, b, c, alpha | priority | color3); + + float[][] u = model.getFaceTextureUCoordinates(); + float[][] v = model.getFaceTextureVCoordinates(); + float[] uf, vf; + if (faceTextures != null) + { + if (u != null && v != null && (uf = u[face]) != null && (vf = v[face]) != null) + { + final short texture = faceTextures[face]; + uvBuffer.put(texture, 1f + uf[0], 1f + vf[0], 0f); + uvBuffer.put(texture, 1f + uf[1], 1f + vf[1], 0f); + uvBuffer.put(texture, 1f + uf[2], 1f + vf[2], 0f); + } + else + { + uvBuffer.put(0f, 0f, 0f, 0f); + uvBuffer.put(0f, 0f, 0f, 0f); + uvBuffer.put(0f, 0f, 0f, 0f); + } + } + + return 3; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java new file mode 100644 index 00000000000..78a36eaa34a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +class ShaderException extends Exception +{ + ShaderException(String message) + { + super(message); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java new file mode 100644 index 00000000000..0ce04acb1e1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import com.jogamp.opengl.GL4; +import java.nio.ByteBuffer; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Texture; +import net.runelite.api.TextureProvider; + +@Singleton +@Slf4j +class TextureManager +{ + private static final float PERC_64 = 1f / 64f; + private static final float PERC_128 = 1f / 128f; + + private static final int SMALL_TEXTURE_SIZE = 64; + private static final int TEXTURE_SIZE = 128; + + int initTextureArray(TextureProvider textureProvider, GL4 gl) + { + if (!allTexturesLoaded(textureProvider)) + { + return -1; + } + + Texture[] textures = textureProvider.getTextures(); + + int textureArrayId = GLUtil.glGenTexture(gl); + gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId); + gl.glTexStorage3D(gl.GL_TEXTURE_2D_ARRAY, 1, gl.GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length); + + gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST); + gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST); + + gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE); + + // Set brightness to 1.0d to upload unmodified textures to GPU + double save = textureProvider.getBrightness(); + textureProvider.setBrightness(1.0d); + + updateTextures(textureProvider, gl, textureArrayId); + + textureProvider.setBrightness(save); + + gl.glActiveTexture(gl.GL_TEXTURE1); + gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId); + gl.glActiveTexture(gl.GL_TEXTURE0); + + return textureArrayId; + } + + void freeTextureArray(GL4 gl, int textureArrayId) + { + GLUtil.glDeleteTexture(gl, textureArrayId); + } + + /** + * Check if all textures have been loaded and cached yet. + * + * @param textureProvider + * @return + */ + private boolean allTexturesLoaded(TextureProvider textureProvider) + { + Texture[] textures = textureProvider.getTextures(); + if (textures == null || textures.length == 0) + { + return false; + } + + for (int textureId = 0; textureId < textures.length; textureId++) + { + Texture texture = textures[textureId]; + if (texture != null) + { + int[] pixels = textureProvider.load(textureId); + if (pixels == null) + { + return false; + } + } + } + + return true; + } + + private void updateTextures(TextureProvider textureProvider, GL4 gl, int textureArrayId) + { + Texture[] textures = textureProvider.getTextures(); + + gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId); + + int cnt = 0; + for (int textureId = 0; textureId < textures.length; textureId++) + { + Texture texture = textures[textureId]; + if (texture != null) + { + int[] srcPixels = textureProvider.load(textureId); + if (srcPixels == null) + { + log.warn("No pixels for texture {}!", textureId); + continue; // this can't happen + } + + ++cnt; + + int srcSize = srcPixels.length == 4096 ? SMALL_TEXTURE_SIZE : TEXTURE_SIZE; + byte[] pixels = convertPixels(srcPixels, srcSize, srcSize, TEXTURE_SIZE, TEXTURE_SIZE); + ByteBuffer pixelBuffer = ByteBuffer.wrap(pixels); + gl.glTexSubImage3D(gl.GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE, + 1, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixelBuffer); + } + } + + log.debug("Uploaded textures {}", cnt); + } + + private static byte[] convertPixels(int[] srcPixels, int width, int height, int textureWidth, int textureHeight) + { + byte[] pixels = new byte[textureWidth * textureHeight * 4]; + + int pixelIdx = 0; + int srcPixelIdx = 0; + + int offset = (textureWidth - width) * 4; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int rgb = srcPixels[srcPixelIdx++]; + if (rgb != 0) + { + pixels[pixelIdx++] = (byte) (rgb >> 16); + pixels[pixelIdx++] = (byte) (rgb >> 8); + pixels[pixelIdx++] = (byte) rgb; + pixels[pixelIdx++] = (byte) -1; + } + else + { + pixelIdx += 4; + } + } + pixelIdx += offset; + } + return pixels; + } + + /** + * Animate the given texture + * + * @param texture + * @param diff Number of elapsed client ticks since last animation + */ + void animate(Texture texture, int diff) + { + final int[] pixels = texture.getPixels(); + if (pixels == null) + { + return; + } + + final int animationSpeed = texture.getAnimationSpeed(); + final float uvdiff = pixels.length == 4096 ? PERC_64 : PERC_128; + + float u = texture.getU(); + float v = texture.getV(); + + int offset = animationSpeed * diff; + float d = (float) offset * uvdiff; + + switch (texture.getAnimationDirection()) + { + case 1: + v -= d; + if (v < 0f) + { + v += 1f; + } + break; + case 3: + v += d; + if (v > 1f) + { + v -= 1f; + } + break; + case 2: + u -= d; + if (u < 0f) + { + u += 1f; + } + break; + case 4: + u += d; + if (u > 1f) + { + u -= 1f; + } + break; + default: + return; + } + + texture.setU(u); + texture.setV(v); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java new file mode 100644 index 00000000000..1f2ea735493 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu.template; + +import java.util.function.Function; + +public class Template +{ + private final Function resourceLoader; + + public Template(Function resourceLoader) + { + this.resourceLoader = resourceLoader; + } + + public String process(String str) + { + StringBuilder sb = new StringBuilder(); + for (String line : str.split("\r?\n")) + { + if (line.startsWith("#include ")) + { + String resource = line.substring(9); + String resourceStr = resourceLoader.apply(resource); + sb.append(process(resourceStr)); + } + else + { + sb.append(line).append('\n'); + } + } + return sb.toString(); + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl new file mode 100644 index 00000000000..e93d66abe02 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include to_screen.glsl + +/* + * Rotate a vertex by a given orientation in JAU + */ +ivec4 rotate(ivec4 vertex, int orientation) { + int s = int(65536.0f * sin(orientation * UNIT)); + int c = int(65536.0f * cos(orientation * UNIT)); + int x = vertex.z * s + vertex.x * c >> 16; + int z = vertex.z * c - vertex.x * s >> 16; + return ivec4(x, vertex.y, z, vertex.w); +} + +/* + * Calculate the distance to a vertex given the camera angle + */ +int distance(ivec4 vertex, int cameraYaw, int cameraPitch) { + int yawSin = int(65536.0f * sin(cameraYaw * UNIT)); + int yawCos = int(65536.0f * cos(cameraYaw * UNIT)); + + int pitchSin = int(65536.0f * sin(cameraPitch * UNIT)); + int pitchCos = int(65536.0f * cos(cameraPitch * UNIT)); + + int j = vertex.z * yawCos - vertex.x * yawSin >> 16; + int l = vertex.y * pitchSin + j * pitchCos >> 16; + + return l; +} + +/* + * Calculate the distance to a face + */ +int face_distance(ivec4 vA, ivec4 vB, ivec4 vC, int cameraYaw, int cameraPitch) { + int dvA = distance(vA, cameraYaw, cameraPitch); + int dvB = distance(vB, cameraYaw, cameraPitch); + int dvC = distance(vC, cameraYaw, cameraPitch); + int faceDistance = (dvA + dvB + dvC) / 3; + return faceDistance; +} + +/* + * Test if a face is visible (not backward facing) + */ +bool face_visible(ivec4 vA, ivec4 vB, ivec4 vC, ivec4 position, int cameraYaw, int cameraPitch, int centerX, int centerY, int zoom) { + vA += position; + vB += position; + vC += position; + + ivec3 sA = toScreen(vA.xyz, cameraYaw, cameraPitch, centerX, centerY, zoom); + ivec3 sB = toScreen(vB.xyz, cameraYaw, cameraPitch, centerX, centerY, zoom); + ivec3 sC = toScreen(vC.xyz, cameraYaw, cameraPitch, centerX, centerY, zoom); + + return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0; +} + +// Calculate adjusted priority for a face with a given priority, distance, and +// model global min10 and face distance averages. This allows positioning faces +// with priorities 10/11 into the correct 'slots' resulting in 18 possible +// adjusted priorities +int priority_map(int p, int distance, int _min10, int avg1, int avg2, int avg3) { + // (10, 11) 0 1 2 (10, 11) 3 4 (10, 11) 5 6 7 8 9 (10, 11) + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + switch (p) { + case 0: return 2; + case 1: return 3; + case 2: return 4; + case 3: return 7; + case 4: return 8; + case 5: return 11; + case 6: return 12; + case 7: return 13; + case 8: return 14; + case 9: return 15; + case 10: + if (distance > avg1) { + return 0; + } else if (distance > avg2) { + return 5; + } else if (distance > avg3) { + return 9; + } else { + return 16; + } + case 11: + if (distance > avg1 && _min10 > avg1) { + return 1; + } else if (distance > avg2 && (_min10 > avg1 || _min10 > avg2)) { + return 6; + } else if (distance > avg3 && (_min10 > avg1 || _min10 > avg2 || _min10 > avg3)) { + return 10; + } else { + return 17; + } + default: + return -1; + } +} + +// calculate the number of faces with a lower adjusted priority than +// the given adjusted priority +int count_prio_offset(int priority) { + int total = 0; + switch (priority) { + case 17: + total += totalMappedNum[16]; + case 16: + total += totalMappedNum[15]; + case 15: + total += totalMappedNum[14]; + case 14: + total += totalMappedNum[13]; + case 13: + total += totalMappedNum[12]; + case 12: + total += totalMappedNum[11]; + case 11: + total += totalMappedNum[10]; + case 10: + total += totalMappedNum[9]; + case 9: + total += totalMappedNum[8]; + case 8: + total += totalMappedNum[7]; + case 7: + total += totalMappedNum[6]; + case 6: + total += totalMappedNum[5]; + case 5: + total += totalMappedNum[4]; + case 4: + total += totalMappedNum[3]; + case 3: + total += totalMappedNum[2]; + case 2: + total += totalMappedNum[1]; + case 1: + total += totalMappedNum[0]; + case 0: + return total; + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl new file mode 100644 index 00000000000..35b257a2266 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#version 430 core + +#define PI 3.1415926535897932384626433832795f +#define UNIT PI / 1024.0f + +layout(std140) uniform uniforms { + int cameraYaw; + int cameraPitch; + int centerX; + int centerY; + int zoom; +}; + +shared int totalNum[12]; // number of faces with a given priority +shared int totalDistance[12]; // sum of distances to faces of a given priority + +shared int totalMappedNum[18]; // number of faces with a given adjusted priority + +shared int min10; // minimum distance to a face of priority 10 +shared int dfs[4096]; // packed face id and distance + +struct modelinfo { + int offset; // offset into buffer + int uvOffset; // offset into uv buffer + int length; // length in faces + int idx; // write idx in target buffer + int flags; // radius, orientation + int x; // scene position x + int y; // scene position y + int z; // scene position z +}; + +layout(std430, binding = 0) readonly buffer modelbuffer_in { + modelinfo ol[]; +}; + +layout(std430, binding = 1) readonly buffer vertexbuffer_in { + ivec4 vb[]; +}; + +layout(std430, binding = 2) readonly buffer tempvertexbuffer_in { + ivec4 tempvb[]; +}; + +layout(std430, binding = 3) writeonly buffer vertex_out { + ivec4 vout[]; +}; + +layout(std430, binding = 4) writeonly buffer uv_out { + vec4 uvout[]; +}; + +layout(std430, binding = 5) readonly buffer uvbuffer_in { + vec4 uv[]; +}; + +layout(std430, binding = 6) readonly buffer tempuvbuffer_in { + vec4 tempuv[]; +}; + +layout(local_size_x = 1024) in; + +#include common.glsl +#include priority_render.glsl + +void main() { + uint groupId = gl_WorkGroupID.x; + uint localId = gl_LocalInvocationID.x * 4; + modelinfo minfo = ol[groupId]; + int length = minfo.length; + + if (localId == 0) { + min10 = 1600; + for (int i = 0; i < 12; ++i) { + totalNum[i] = 0; + totalDistance[i] = 0; + } + for (int i = 0; i < 18; ++i) { + totalMappedNum[i] = 0; + } + } + + memoryBarrierShared(); + barrier(); + + int prio1, dis1, prio1Adj; + ivec4 vA1, vA2, vA3; + + int prio2, dis2, prio2Adj; + ivec4 vB1, vB2, vB3; + + int prio3, dis3, prio3Adj; + ivec4 vC1, vC2, vC3; + + int prio4, dis4, prio4Adj; + ivec4 vD1, vD2, vD3; + + get_face(localId, minfo, cameraYaw, cameraPitch, centerX, centerY, zoom, prio1, dis1, vA1, vA2, vA3); + get_face(localId + 1, minfo, cameraYaw, cameraPitch, centerX, centerY, zoom, prio2, dis2, vB1, vB2, vB3); + get_face(localId + 2, minfo, cameraYaw, cameraPitch, centerX, centerY, zoom, prio3, dis3, vC1, vC2, vC3); + get_face(localId + 3, minfo, cameraYaw, cameraPitch, centerX, centerY, zoom, prio4, dis4, vD1, vD2, vD3); + + memoryBarrierShared(); + barrier(); + + int idx1 = map_face_priority(localId, minfo, prio1, dis1, prio1Adj); + int idx2 = map_face_priority(localId + 1, minfo, prio2, dis2, prio2Adj); + int idx3 = map_face_priority(localId + 2, minfo, prio3, dis3, prio3Adj); + int idx4 = map_face_priority(localId + 3, minfo, prio4, dis4, prio4Adj); + + memoryBarrierShared(); + barrier(); + + insert_dfs(localId , minfo, prio1Adj, dis1, idx1); + insert_dfs(localId + 1, minfo, prio2Adj, dis2, idx2); + insert_dfs(localId + 2, minfo, prio3Adj, dis3, idx3); + insert_dfs(localId + 3, minfo, prio4Adj, dis4, idx4); + + memoryBarrierShared(); + barrier(); + + sort_and_insert(localId , minfo, prio1Adj, dis1, vA1, vA2, vA3); + sort_and_insert(localId + 1, minfo, prio2Adj, dis2, vB1, vB2, vB3); + sort_and_insert(localId + 2, minfo, prio3Adj, dis3, vC1, vC2, vC3); + sort_and_insert(localId + 3, minfo, prio4Adj, dis4, vD1, vD2, vD3); +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_small.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_small.glsl new file mode 100644 index 00000000000..c486c4c02c8 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_small.glsl @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#version 430 core + +#define PI 3.1415926535897932384626433832795f +#define UNIT PI / 1024.0f + +layout(std140) uniform uniforms { + int cameraYaw; + int cameraPitch; + int centerX; + int centerY; + int zoom; +}; + +shared int totalNum[12]; // number of faces with a given priority +shared int totalDistance[12]; // sum of distances to faces of a given priority + +shared int totalMappedNum[18]; // number of faces with a given adjusted priority + +shared int min10; // minimum distance to a face of priority 10 +shared int dfs[512]; // packed face id and distance + +struct modelinfo { + int offset; // offset into buffer + int uvOffset; // offset into uv buffer + int length; // length in faces + int idx; // write idx in target buffer + int flags; // radius, orientation + int x; // scene position x + int y; // scene position y + int z; // scene position z +}; + +layout(std430, binding = 0) readonly buffer modelbuffer_in { + modelinfo ol[]; +}; + +layout(std430, binding = 1) readonly buffer vertexbuffer_in { + ivec4 vb[]; +}; + +layout(std430, binding = 2) readonly buffer tempvertexbuffer_in { + ivec4 tempvb[]; +}; + +layout(std430, binding = 3) writeonly buffer vertex_out { + ivec4 vout[]; +}; + +layout(std430, binding = 4) writeonly buffer uv_out { + vec4 uvout[]; +}; + +layout(std430, binding = 5) readonly buffer uvbuffer_in { + vec4 uv[]; +}; + +layout(std430, binding = 6) readonly buffer tempuvbuffer_in { + vec4 tempuv[]; +}; + +layout(local_size_x = 512) in; + +#include common.glsl +#include priority_render.glsl + +void main() { + uint groupId = gl_WorkGroupID.x; + uint localId = gl_LocalInvocationID.x; + modelinfo minfo = ol[groupId]; + + if (localId == 0) { + min10 = 1600; + for (int i = 0; i < 12; ++i) { + totalNum[i] = 0; + totalDistance[i] = 0; + } + for (int i = 0; i < 18; ++i) { + totalMappedNum[i] = 0; + } + } + + memoryBarrierShared(); + barrier(); + + int prio1, dis1, prio1Adj; + ivec4 vA1, vA2, vA3; + + get_face(localId, minfo, cameraYaw, cameraPitch, centerX, centerY, zoom, prio1, dis1, vA1, vA2, vA3); + + memoryBarrierShared(); + barrier(); + + int idx1 = map_face_priority(localId, minfo, prio1, dis1, prio1Adj); + + memoryBarrierShared(); + barrier(); + + insert_dfs(localId, minfo, prio1Adj, dis1, idx1); + + memoryBarrierShared(); + barrier(); + + sort_and_insert(localId, minfo, prio1Adj, dis1, vA1, vA2, vA3); +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl new file mode 100644 index 00000000000..2a83da42812 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#version 400 + +uniform sampler2DArray textures; +uniform vec2 textureOffsets[64]; +uniform float brightness; + +in vec4 Color; +in vec4 fUv; +out vec4 FragColor; + +void main() { + float n = fUv.x; + float u = fUv.y; + float v = fUv.z; + + if (u > 0.0f && v > 0.0f) { + int textureIdx = int(n); + + vec2 uv = vec2(u - 1, v - 1); + vec2 animatedUv = uv + textureOffsets[textureIdx]; + + vec4 textureColor = texture(textures, vec3(animatedUv, n)); + vec4 textureColorBrightness = pow(textureColor, vec4(brightness, brightness, brightness, 1.0f)); + + FragColor = textureColorBrightness * Color; + } else { + FragColor = Color; + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl new file mode 100644 index 00000000000..57731df5b8f --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#version 330 + +uniform sampler2D tex; + +in vec2 TexCoord; + +out vec4 FragColor; + +void main() { + vec4 c = texture(tex, TexCoord); + FragColor = c; +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl new file mode 100644 index 00000000000..0fe57ecdb77 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#version 400 + +#define PI 3.1415926535897932384626433832795f +#define UNIT PI / 1024.0f + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(std140) uniform Uniforms { + int cameraYaw; + int cameraPitch; + int centerX; + int centerY; + int zoom; +}; + +uniform mat4 projectionMatrix; + +in ivec3 vPosition[]; +in vec4 vColor[]; +in vec4 vUv[]; + +out vec4 Color; +out vec4 fUv; + +#include to_screen.glsl + +void main() { + ivec3 screenA = toScreen(vPosition[0], cameraYaw, cameraPitch, centerX, centerY, zoom); + ivec3 screenB = toScreen(vPosition[1], cameraYaw, cameraPitch, centerX, centerY, zoom); + ivec3 screenC = toScreen(vPosition[2], cameraYaw, cameraPitch, centerX, centerY, zoom); + + if (-screenA.z < 50 || -screenB.z < 50 || -screenC.z < 50) { + // the client does not draw a triangle if any vertex distance is <50 + return; + } + + vec4 tmp = vec4(screenA.xyz, 1.0); + Color = vColor[0]; + fUv = vUv[0]; + gl_Position = projectionMatrix * tmp; + EmitVertex(); + + tmp = vec4(screenB.xyz, 1.0); + Color = vColor[1]; + fUv = vUv[1]; + gl_Position = projectionMatrix * tmp; + EmitVertex(); + + tmp = vec4(screenC.xyz, 1.0); + Color = vColor[2]; + fUv = vUv[2]; + gl_Position = projectionMatrix * tmp; + EmitVertex(); + + EndPrimitive(); +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl new file mode 100644 index 00000000000..7240b32d42e --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void get_face(uint localId, modelinfo minfo, int cameraYaw, int cameraPitch, int centerX, int centerY, int zoom, + out int prio, out int dis, out ivec4 o1, out ivec4 o2, out ivec4 o3) { + int offset = minfo.offset; + int length = minfo.length; + int flags = minfo.flags; + int radius = (flags & 0x7fffffff) >> 12; + int orientation = flags & 0x7ff; + ivec4 pos = ivec4(minfo.x, minfo.y, minfo.z, 0); + + uint ssboOffset; + + if (localId < length) { + ssboOffset = localId; + } else { + ssboOffset = 0; + } + + ivec4 thisA; + ivec4 thisB; + ivec4 thisC; + + // Grab triangle vertices from the correct buffer + if (flags < 0) { + thisA = vb[offset + ssboOffset * 3 ]; + thisB = vb[offset + ssboOffset * 3 + 1]; + thisC = vb[offset + ssboOffset * 3 + 2]; + } else { + thisA = tempvb[offset + ssboOffset * 3 ]; + thisB = tempvb[offset + ssboOffset * 3 + 1]; + thisC = tempvb[offset + ssboOffset * 3 + 2]; + } + + ivec4 thisrvA; + ivec4 thisrvB; + ivec4 thisrvC; + + int thisPriority, thisDistance; + + if (localId < length) { + // rotate for model orientation + thisrvA = rotate(thisA, orientation); + thisrvB = rotate(thisB, orientation); + thisrvC = rotate(thisC, orientation); + + // calculate distance to face + thisPriority = (thisA.w >> 16) & 0xff; // all vertices on the face have the same priority + if (radius == 0) { + thisDistance = 0; + } else { + thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch) + radius; + } + + // if the face is not culled, it is calculated into priority distance averages + if (face_visible(thisrvA, thisrvB, thisrvC, pos, cameraYaw, cameraPitch, centerX, centerY, zoom)) { + atomicAdd(totalNum[thisPriority], 1); + atomicAdd(totalDistance[thisPriority], thisDistance); + + // calculate minimum distance to any face of priority 10 for positioning the 11 faces later + if (thisPriority == 10) { + atomicMin(min10, thisDistance); + } + } + + o1 = thisrvA; + o2 = thisrvB; + o3 = thisrvC; + + prio = thisPriority; + dis = thisDistance; + } else { + prio = 0; + dis = 0; + } +} + +int map_face_priority(uint localId, modelinfo minfo, int thisPriority, int thisDistance, out int prio) { + int length = minfo.length; + + // Compute average distances for 0/2, 3/4, and 6/8 + + int adjPrio; + int prioIdx; + + if (localId < length) { + int avg1 = 0; + int avg2 = 0; + int avg3 = 0; + + if (totalNum[1] > 0 || totalNum[2] > 0) { + avg1 = (totalDistance[1] + totalDistance[2]) / (totalNum[1] + totalNum[2]); + } + + if (totalNum[3] > 0 || totalNum[4] > 0) { + avg2 = (totalDistance[3] + totalDistance[4]) / (totalNum[3] + totalNum[4]); + } + + if (totalNum[6] > 0 || totalNum[8] > 0) { + avg3 = (totalDistance[6] + totalDistance[8]) / (totalNum[6] + totalNum[8]); + } + + int _min10 = min10; + adjPrio = priority_map(thisPriority, thisDistance, _min10, avg1, avg2, avg3); + + int prioIdx = atomicAdd(totalMappedNum[adjPrio], 1); + + prio = adjPrio; + + return prioIdx; + } + + return 0; +} + +void insert_dfs(uint localId, modelinfo minfo, int adjPrio, int distance, int prioIdx) { + int length = minfo.length; + + if (localId < length) { + // calculate base offset into dfs based on number of faces with a lower priority + int baseOff = count_prio_offset(adjPrio); + // store into face array offset array by unique index + dfs[baseOff + prioIdx] = (int(localId) << 16) | distance; + } +} + +void sort_and_insert(uint localId, modelinfo minfo, int thisPriority, int thisDistance, ivec4 thisrvA, ivec4 thisrvB, ivec4 thisrvC) { + /* compute face distance */ + int length = minfo.length; + int outOffset = minfo.idx; + int uvOffset = minfo.uvOffset; + int flags = minfo.flags; + ivec4 pos = ivec4(minfo.x, minfo.y, minfo.z, 0); + + int start, end, myOffset; + if (localId < length) { + const int priorityOffset = count_prio_offset(thisPriority); + const int numOfPriority = totalMappedNum[thisPriority]; + start = priorityOffset; // index of first face with this priority + end = priorityOffset + numOfPriority; // index of last face with this priority + myOffset = priorityOffset; + } else { + start = end = myOffset = 0; + } + + if (localId < length) { + // we only have to order faces against others of the same priority + // calculate position this face will be in + for (int i = start; i < end; ++i) { + int d1 = dfs[i]; + int theirId = d1 >> 16; + int theirDistance = d1 & 0xffff; + + // the closest faces draw last, so have the highest index + // if two faces have the same distance, the one with the + // higher id draws last + if ((theirDistance > thisDistance) + || (theirDistance == thisDistance && theirId < localId)) { + ++myOffset; + } + } + + // position vertices in scene and write to out buffer + vout[outOffset + myOffset * 3] = pos + thisrvA; + vout[outOffset + myOffset * 3 + 1] = pos + thisrvB; + vout[outOffset + myOffset * 3 + 2] = pos + thisrvC; + + if (uvOffset < 0) { + uvout[outOffset + myOffset * 3] = vec4(0, 0, 0, 0); + uvout[outOffset + myOffset * 3 + 1] = vec4(0, 0, 0, 0); + uvout[outOffset + myOffset * 3 + 2] = vec4(0, 0, 0, 0); + } else if (flags >= 0) { + uvout[outOffset + myOffset * 3] = tempuv[uvOffset + localId * 3]; + uvout[outOffset + myOffset * 3 + 1] = tempuv[uvOffset + localId * 3 + 1]; + uvout[outOffset + myOffset * 3 + 2] = tempuv[uvOffset + localId * 3 + 2]; + } else { + uvout[outOffset + myOffset * 3] = uv[uvOffset + localId * 3]; + uvout[outOffset + myOffset * 3 + 1] = uv[uvOffset + localId * 3 + 1]; + uvout[outOffset + myOffset * 3 + 2] = uv[uvOffset + localId * 3 + 2]; + } + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl new file mode 100644 index 00000000000..612cf8fce86 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Convert a vertex to screen space + */ +ivec3 toScreen(ivec3 vertex, int cameraYaw, int cameraPitch, int centerX, int centerY, int zoom) { + int yawSin = int(65536.0f * sin(cameraYaw * UNIT)); + int yawCos = int(65536.0f * cos(cameraYaw * UNIT)); + + int pitchSin = int(65536.0f * sin(cameraPitch * UNIT)); + int pitchCos = int(65536.0f * cos(cameraPitch * UNIT)); + + int rotatedX = ((vertex.z * yawSin) + (vertex.x * yawCos)) >> 16; + int rotatedZ = ((vertex.z * yawCos) - (vertex.x * yawSin)) >> 16; + + int var13 = ((vertex.y * pitchCos) - (rotatedZ * pitchSin)) >> 16; + int var12 = ((vertex.y * pitchSin) + (rotatedZ * pitchCos)) >> 16; + + int x = rotatedX * zoom / var12 + centerX; + int y = var13 * zoom / var12 + centerY; + int z = -var12; // in OpenGL depth is negative + + return ivec3(x, y, z); +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl new file mode 100644 index 00000000000..557255570b6 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#version 400 + +layout (location = 0) in ivec4 VertexPosition; +layout (location = 1) in vec4 uv; + +uniform float brightness; + +out ivec3 vPosition; +out vec4 vColor; +out vec4 vUv; + +vec3 hslToRgb(int hsl) { + int var5 = hsl/128; + float var6 = float(var5 >> 3) / 64.0f + 0.0078125f; + float var8 = float(var5 & 7) / 8.0f + 0.0625f; + + int var10 = hsl % 128; + + float var11 = float(var10) / 128.0f; + float var13 = var11; + float var15 = var11; + float var17 = var11; + + if(var8 != 0.0f) { + float var19; + if(var11 < 0.5f) { + var19 = var11 * (1.0f + var8); + } else { + var19 = var11 + var8 - var11 * var8; + } + + float var21 = 2.0f * var11 - var19; + float var23 = var6 + 0.3333333333333333f; + if(var23 > 1.0f) { + var23 -= 1.f; + } + + float var27 = var6 - 0.3333333333333333f; + if(var27 < 0.0f) { + var27 += 1.f; + } + + if(6.0f * var23 < 1.0f) { + var13 = var21 + (var19 - var21) * 6.0f * var23; + } else if(2.0f * var23 < 1.0f) { + var13 = var19; + } else if(3.0f * var23 < 2.0f) { + var13 = var21 + (var19 - var21) * (0.6666666666666666f - var23) * 6.0f; + } else { + var13 = var21; + } + + if(6.0f * var6 < 1.0f) { + var15 = var21 + (var19 - var21) * 6.0f * var6; + } else if(2.0f * var6 < 1.0f) { + var15 = var19; + } else if(3.0f * var6 < 2.0f) { + var15 = var21 + (var19 - var21) * (0.6666666666666666f - var6) * 6.0f; + } else { + var15 = var21; + } + + if(6.0f * var27 < 1.0f) { + var17 = var21 + (var19 - var21) * 6.0f * var27; + } else if(2.0f * var27 < 1.0f) { + var17 = var19; + } else if(3.0f * var27 < 2.0f) { + var17 = var21 + (var19 - var21) * (0.6666666666666666f - var27) * 6.0f; + } else { + var17 = var21; + } + } + + vec3 rgb = vec3( + pow(var13, brightness), + pow(var15, brightness), + pow(var17, brightness) + ); + + // I don't think we actually need this + if (rgb == vec3(0, 0, 0)) { + rgb = vec3(0, 0, 1/255.f); + } + + return rgb; +} + +void main() +{ + ivec3 vertex = VertexPosition.xyz; + int ahsl = VertexPosition.w; + int hsl = ahsl & 0xffff; + float a = float(ahsl >> 24 & 0xff) / 255.f; + + vec3 rgb = hslToRgb(hsl); + + vPosition = vertex; + vColor = vec4(rgb, 1.f - a); + vUv = uv; +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl new file mode 100644 index 00000000000..c6a11152262 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#version 330 + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoord; + +out vec2 TexCoord; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + TexCoord = aTexCoord; +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java new file mode 100644 index 00000000000..0889b0d0f64 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; +import com.jogamp.nativewindow.awt.JAWTWindow; +import com.jogamp.opengl.GL4; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.GLDrawableFactory; +import com.jogamp.opengl.GLProfile; +import java.awt.Canvas; +import java.util.function.Function; +import javax.swing.JFrame; +import static net.runelite.client.plugins.gpu.GLUtil.inputStreamToString; +import net.runelite.client.plugins.gpu.template.Template; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +public class ShaderTest +{ + private static final String VERTEX_SHADER = "" + + "void main() {" + + " gl_Position = vec4(1.0, 1.0, 1.0, 1.0);" + + "}"; + private GL4 gl; + + @Before + public void before() + { + Canvas canvas = new Canvas(); + JFrame frame = new JFrame(); + frame.setSize(100, 100); + frame.add(canvas); + frame.setVisible(true); + + GLProfile glProfile = GLProfile.getMaxFixedFunc(true); + + GLCapabilities glCaps = new GLCapabilities(glProfile); + AbstractGraphicsConfiguration config = AWTGraphicsConfiguration.create(canvas.getGraphicsConfiguration(), + glCaps, glCaps); + + JAWTWindow jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(canvas, config); + + GLDrawableFactory glDrawableFactory = GLDrawableFactory.getFactory(glProfile); + + GLDrawable glDrawable = glDrawableFactory.createGLDrawable(jawtWindow); + glDrawable.setRealized(true); + + + GLContext glContext = glDrawable.createContext(null); + int res = glContext.makeCurrent(); + if (res == GLContext.CONTEXT_NOT_CURRENT) + { + fail("error making context current"); + } + + gl = glContext.getGL().getGL4(); + } + + @Test + @Ignore + public void testSmall() throws ShaderException + { + int glComputeProgram = gl.glCreateProgram(); + int glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); + try + { + Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); + Template template = new Template(func); + String source = template.process(func.apply("comp_small.glsl")); + + int line = 0; + for (String str : source.split("\\n")) + { + System.out.println(++line + " " + str); + } + + GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); + } + finally + { + gl.glDeleteShader(glComputeShader); + gl.glDeleteProgram(glComputeProgram); + } + } + + @Test + @Ignore + public void testComp() throws ShaderException + { + int glComputeProgram = gl.glCreateProgram(); + int glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); + try + { + Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); + Template template = new Template(func); + String source = template.process(func.apply("comp.glsl")); + + int line = 0; + for (String str : source.split("\\n")) + { + System.out.println(++line + " " + str); + } + + GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); + } + finally + { + gl.glDeleteShader(glComputeShader); + gl.glDeleteProgram(glComputeProgram); + } + } + + @Test + @Ignore + public void testGeom() throws ShaderException + { + int glComputeProgram = gl.glCreateProgram(); + int glVertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER); + int glGeometryShader = gl.glCreateShader(gl.GL_GEOMETRY_SHADER); + int glFragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER); + try + { + Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); + Template template = new Template(func); + String source = template.process(func.apply("geom.glsl")); + + int line = 0; + for (String str : source.split("\\n")) + { + System.out.println(++line + " " + str); + } + + GLUtil.loadShaders(gl, glComputeProgram, glVertexShader, glGeometryShader, glFragmentShader, VERTEX_SHADER, source, ""); + } + finally + { + gl.glDeleteShader(glVertexShader); + gl.glDeleteShader(glGeometryShader); + gl.glDeleteShader(glFragmentShader); + gl.glDeleteProgram(glComputeProgram); + } + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java new file mode 100644 index 00000000000..0c35e65d52a --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu.template; + +import java.util.function.Function; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class TemplateTest +{ + private static final String FILE1 = "" + + "test1\n" + + "#include file2\n" + + "test3\n"; + + private static final String FILE2 = "" + + "test4\n" + + "test5\n"; + + private static final String RESULT = "" + + "test1\n" + + "test4\n" + + "test5\n" + + "test3\n"; + + @Test + public void testProcess() + { + Function func = (String resource) -> + { + switch (resource) + { + case "file2": + return FILE2; + default: + throw new RuntimeException("unknown resource"); + } + }; + String out = new Template(func).process(FILE1); + assertEquals(RESULT, out); + } +} \ No newline at end of file diff --git a/runelite-mixins/pom.xml b/runelite-mixins/pom.xml index b426eaf216f..1069b81b0a0 100644 --- a/runelite-mixins/pom.xml +++ b/runelite-mixins/pom.xml @@ -58,6 +58,19 @@ 1 provided + + + junit + junit + 4.12 + test + + + org.mockito + mockito-all + 1.10.19 + test + diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/ClickboxMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/ClickboxMixin.java new file mode 100644 index 00000000000..91a4d7f1282 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/ClickboxMixin.java @@ -0,0 +1,314 @@ +package net.runelite.mixins; + +import net.runelite.api.Model; +import net.runelite.api.Perspective; +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Shadow; +import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSModel; + +/** + * Class to check clickboxes of models. Mostly refactored code from the client. + */ +@Mixin(RSClient.class) +public abstract class ClickboxMixin implements RSClient +{ + @Shadow("clientInstance") + private static RSClient client; + + private static final int MAX_ENTITES_AT_MOUSE = 1000; + private static final int CLICKBOX_CLOSE = 50; + private static final int CLICKBOX_FAR = 3500; + + @Inject + private static final int[] rl$modelViewportXs = new int[4700]; + + @Inject + private static final int[] rl$modelViewportYs = new int[4700]; + + @Inject + public void checkClickbox(Model rlModel, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int _x, int _y, int _z, long hash) + { + RSModel model = (RSModel) rlModel; + boolean hasFlag = hash != 0L && (int) (hash >>> 16 & 1L) != 1; + boolean viewportContainsMouse = client.getViewportContainsMouse(); + + if (!hasFlag || !viewportContainsMouse || client.getOculusOrbState() != 0) + { + return; + } + + boolean bb = boundingboxCheck(model, _x, _y, _z); + if (!bb) + { + return; + } + + // only need a boundingbox check? + if (model.isClickable()) + { + addHashAtMouse(hash); + return; + } + + // otherwise we must check if the mouse is in a triangle + final int vertexCount = model.getVerticesCount(); + final int triangleCount = model.getTrianglesCount(); + + final int[] vertexX = model.getVerticesX(); + final int[] vertexY = model.getVerticesY(); + final int[] vertexZ = model.getVerticesZ(); + + final int[] triangleX = model.getTrianglesX(); + final int[] triangleY = model.getTrianglesY(); + final int[] triangleZ = model.getTrianglesZ(); + + final int[] color3 = model.getFaceColors3(); + + final int zoom = client.get3dZoom(); + + final int centerX = client.getCenterX(); + final int centerY = client.getCenterY(); + + int sin = 0; + int cos = 0; + if (orientation != 0) + { + sin = Perspective.SINE[orientation]; + cos = Perspective.COSINE[orientation]; + } + + for (int i = 0; i < vertexCount; ++i) + { + int x = vertexX[i]; + int y = vertexY[i]; + int z = vertexZ[i]; + + int var42; + if (orientation != 0) + { + var42 = z * sin + x * cos >> 16; + z = z * cos - x * sin >> 16; + x = var42; + } + + x += _x; + y += _y; + z += _z; + + var42 = z * yawSin + yawCos * x >> 16; + z = yawCos * z - x * yawSin >> 16; + x = var42; + var42 = pitchCos * y - z * pitchSin >> 16; + z = y * pitchSin + pitchCos * z >> 16; + + if (z >= 50) + { + rl$modelViewportYs[i] = x * zoom / z + centerX; + rl$modelViewportXs[i] = var42 * zoom / z + centerY; + } + else + { + rl$modelViewportYs[i] = -5000; + } + } + + final int viewportMouseX = client.getViewportMouseX(); + final int viewportMouseY = client.getViewportMouseY(); + + for (int i = 0; i < triangleCount; ++i) + { + if (color3[i] == -2) + { + continue; + } + + final int vA = triangleX[i]; + final int vB = triangleY[i]; + final int vC = triangleZ[i]; + + int y1 = rl$modelViewportYs[vA]; + int y2 = rl$modelViewportYs[vB]; + int y3 = rl$modelViewportYs[vC]; + + int x1 = rl$modelViewportXs[vA]; + int x2 = rl$modelViewportXs[vB]; + int x3 = rl$modelViewportXs[vC]; + + if (y1 == -5000 || y2 == -5000 || y3 == -5000) + { + continue; + } + + final int radius = model.isClickable() ? 20 : 5; + + int var18 = radius + viewportMouseY; + boolean var34; + if (var18 < x1 && var18 < x2 && var18 < x3) + { + var34 = false; + } + else + { + var18 = viewportMouseY - radius; + if (var18 > x1 && var18 > x2 && var18 > x3) + { + var34 = false; + } + else + { + var18 = radius + viewportMouseX; + if (var18 < y1 && var18 < y2 && var18 < y3) + { + var34 = false; + } + else + { + var18 = viewportMouseX - radius; + if (var18 > y1 && var18 > y2 && var18 > y3) + { + var34 = false; + } + else + { + var34 = true; + } + } + } + } + + if (var34) + { + addHashAtMouse(hash); + break; + } + } + } + + @Inject + private void addHashAtMouse(long hash) + { + long[] entitiesAtMouse = client.getEntitiesAtMouse(); + int count = client.getEntitiesAtMouseCount(); + if (count < MAX_ENTITES_AT_MOUSE) + { + entitiesAtMouse[count] = hash; + client.setEntitiesAtMouseCount(count + 1); + } + } + + @Inject + private boolean boundingboxCheck(Model model, int x, int y, int z) + { + final int cameraPitch = client.getCameraPitch(); + final int cameraYaw = client.getCameraYaw(); + + final int pitchSin = Perspective.SINE[cameraPitch]; + final int pitchCos = Perspective.COSINE[cameraPitch]; + + final int yawSin = Perspective.SINE[cameraYaw]; + final int yawCos = Perspective.COSINE[cameraYaw]; + + final int centerX = client.getCenterX(); + final int centerY = client.getCenterY(); + + final int viewportMouseX = client.getViewportMouseX(); + final int viewportMouseY = client.getViewportMouseY(); + + final int Rasterizer3D_zoom = client.get3dZoom(); + + int var6 = (viewportMouseX - centerX) * CLICKBOX_CLOSE / Rasterizer3D_zoom; + int var7 = (viewportMouseY - centerY) * CLICKBOX_CLOSE / Rasterizer3D_zoom; + int var8 = (viewportMouseX - centerX) * CLICKBOX_FAR / Rasterizer3D_zoom; + int var9 = (viewportMouseY - centerY) * CLICKBOX_FAR / Rasterizer3D_zoom; + int var10 = rl$rot1(var7, CLICKBOX_CLOSE, pitchCos, pitchSin); + int var11 = rl$rot2(var7, CLICKBOX_CLOSE, pitchCos, pitchSin); + var7 = var10; + var10 = rl$rot1(var9, CLICKBOX_FAR, pitchCos, pitchSin); + int var12 = rl$rot2(var9, CLICKBOX_FAR, pitchCos, pitchSin); + var9 = var10; + var10 = rl$rot3(var6, var11, yawCos, yawSin); + var11 = rl$rot4(var6, var11, yawCos, yawSin); + var6 = var10; + var10 = rl$rot3(var8, var12, yawCos, yawSin); + var12 = rl$rot4(var8, var12, yawCos, yawSin); + int field1720 = (var10 - var6) / 2; + int field638 = (var9 - var7) / 2; + int field1846 = (var12 - var11) / 2; + int field1722 = Math.abs(field1720); + int field601 = Math.abs(field638); + int field38 = Math.abs(field1846); + + int var38 = x + model.getCenterX(); + int var39 = y + model.getCenterY(); + int var40 = z + model.getCenterZ(); + int var41 = model.getExtremeX(); + int var42 = model.getExtremeY(); + int var43 = model.getExtremeZ(); + + int field1861 = (var6 + var10) / 2; + int field2317 = (var7 + var9) / 2; + int field528 = (var12 + var11) / 2; + + int var44 = field1861 - var38; + int var45 = field2317 - var39; + int var46 = field528 - var40; + + boolean passes; + if (Math.abs(var44) > var41 + field1722) + { + passes = false; + } + else if (Math.abs(var45) > var42 + field601) + { + passes = false; + } + else if (Math.abs(var46) > var43 + field38) + { + passes = false; + } + else if (Math.abs(var46 * field638 - var45 * field1846) > var42 * field38 + var43 * field601) + { + passes = false; + } + else if (Math.abs(var44 * field1846 - var46 * field1720) > var43 * field1722 + var41 * field38) + { + passes = false; + } + else if (Math.abs(var45 * field1720 - var44 * field638) > var42 * field1722 + var41 * field601) + { + passes = false; + } + else + { + passes = true; + } + + return passes; + } + + @Inject + private static int rl$rot1(int var0, int var1, int var2, int var3) + { + return var0 * var2 + var3 * var1 >> 16; + } + + @Inject + private static int rl$rot2(int var0, int var1, int var2, int var3) + { + return var2 * var1 - var3 * var0 >> 16; + } + + @Inject + private static int rl$rot3(int var0, int var1, int var2, int var3) + { + return var0 * var2 - var3 * var1 >> 16; + } + + @Inject + private static int rl$rot4(int var0, int var1, int var2, int var3) + { + return var3 * var0 + var2 * var1 >> 16; + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java index 79db219b75b..cab2e9be5a1 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -87,6 +87,7 @@ import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.hooks.Callbacks; +import net.runelite.api.hooks.DrawCallbacks; import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.FieldHook; import net.runelite.api.mixins.Inject; @@ -124,6 +125,9 @@ public abstract class RSClientMixin implements RSClient @javax.inject.Inject private Callbacks callbacks; + @Inject + private DrawCallbacks drawCallbacks; + @Inject @javax.inject.Inject @Named("Core Logger") @@ -156,6 +160,9 @@ public abstract class RSClientMixin implements RSClient @Inject private static RSItem lastItemDespawn; + @Inject + private boolean gpu; + @Inject private static boolean oldIsResized; @@ -166,6 +173,20 @@ public Callbacks getCallbacks() return callbacks; } + @Inject + @Override + public DrawCallbacks getDrawCallbacks() + { + return drawCallbacks; + } + + @Inject + @Override + public void setDrawCallbacks(DrawCallbacks drawCallbacks) + { + this.drawCallbacks = drawCallbacks; + } + @Inject @Override public Logger getLogger() @@ -1277,6 +1298,20 @@ public void setLastItemDespawn(RSItem lastItemDespawn) RSClientMixin.lastItemDespawn = lastItemDespawn; } + @Inject + @Override + public boolean isGpu() + { + return gpu; + } + + @Inject + @Override + public void setGpu(boolean gpu) + { + this.gpu = gpu; + } + @Inject @Override public void queueChangedSkill(Skill skill) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java index ae241006e49..48e374f84ae 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java @@ -26,6 +26,8 @@ import java.awt.event.FocusEvent; import net.runelite.api.events.FocusChanged; +import net.runelite.api.hooks.DrawCallbacks; +import net.runelite.api.mixins.FieldHook; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.MethodHook; import net.runelite.api.mixins.Mixin; @@ -71,4 +73,27 @@ public void onFocusGained(FocusEvent focusEvent) focusChanged.setFocused(true); client.getCallbacks().post(focusChanged); } + + @Inject + @MethodHook("post") + public void onPost(Object canvas) + { + DrawCallbacks drawCallbacks = client.getDrawCallbacks(); + if (drawCallbacks != null) + { + drawCallbacks.draw(); + } + } + + @FieldHook("replaceCanvasNextFrame") + @Inject + public void onReplaceCanvasNextFrameChanged(int idx) + { + // when this is initially called the client instance doesn't exist yet + if (client != null && client.isGpu() && isReplaceCanvasNextFrame()) + { + setReplaceCanvasNextFrame(false); + setResizeCanvasNextFrame(true); + } + } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSMainBufferProviderMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSMainBufferProviderMixin.java index 98f24b5fca2..1042ecae54e 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSMainBufferProviderMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSMainBufferProviderMixin.java @@ -24,7 +24,17 @@ */ package net.runelite.mixins; +import java.awt.Component; import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.nio.IntBuffer; +import java.util.Hashtable; +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.MethodHook; import net.runelite.api.mixins.Mixin; import net.runelite.api.mixins.Replace; import net.runelite.api.mixins.Shadow; @@ -37,6 +47,30 @@ public abstract class RSMainBufferProviderMixin implements RSMainBufferProvider @Shadow("clientInstance") private static RSClient client; + @Inject + private IntBuffer buffer; + + @MethodHook(value = "", end = true) + @Inject + public void init(int width, int height, Component canvas) + { + if (!client.isGpu()) + { + return; + } + + final int[] pixels = getPixels(); + + // we need to make our own buffered image for the client with the alpha channel enabled in order to + // have alphas for the overlays applied correctly + DataBufferInt dataBufferInt = new DataBufferInt(pixels, pixels.length); + DirectColorModel directColorModel = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000); + WritableRaster writableRaster = Raster.createWritableRaster(directColorModel.createCompatibleSampleModel(width, height), dataBufferInt, null); + BufferedImage bufferedImage = new BufferedImage(directColorModel, writableRaster, false, new Hashtable()); + + setImage(bufferedImage); + } + /** * Replacing this method makes it so we can completely * control when/what is drawn on the game's canvas, diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSModelDataMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSModelDataMixin.java new file mode 100644 index 00000000000..90c17daea27 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSModelDataMixin.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import net.runelite.api.Model; +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; +import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSModel; +import net.runelite.rs.api.RSModelData; + +@Mixin(RSModelData.class) +public abstract class RSModelDataMixin implements RSModelData +{ + @Shadow("clientInstance") + private static RSClient client; + + @Inject + private float[][] faceTextureUCoordinates; + + @Inject + private float[][] faceTextureVCoordinates; + + @Copy("light") + public abstract Model rs$light(int ambient, int contrast, int var3, int var4, int var5); + + @Replace("light") + public Model rl$light(int ambient, int contrast, int var3, int var4, int var5) + { + client.getLogger().debug("Lighting model {}", this); + + Model model = rs$light(ambient, contrast, var3, var4, var5); + if (model == null) + { + return null; + } + + if (faceTextureUCoordinates == null) + { + computeTextureUVCoordinates(); + } + + RSModel rsModel = (RSModel) model; + rsModel.setFaceTextureUCoordinates(faceTextureUCoordinates); + rsModel.setFaceTextureVCoordinates(faceTextureVCoordinates); + return model; + } + + @Inject + public void computeTextureUVCoordinates() + { + final short[] faceTextures = getFaceTextures(); + if (faceTextures == null) + { + return; + } + + final int[] vertexPositionsX = getVertexX(); + final int[] vertexPositionsY = getVertexY(); + final int[] vertexPositionsZ = getVertexZ(); + + final int[] trianglePointsX = getTrianglePointsX(); + final int[] trianglePointsY = getTrianglePointsY(); + final int[] trianglePointsZ = getTrianglePointsZ(); + + final short[] texTriangleX = getTexTriangleX(); + final short[] texTriangleY = getTexTriangleY(); + final short[] texTriangleZ = getTexTriangleZ(); + + final byte[] textureCoords = getTextureCoords(); + final byte[] textureRenderTypes = getTextureRenderTypes(); + + int faceCount = getTriangleFaceCount(); + this.faceTextureUCoordinates = new float[faceCount][]; + this.faceTextureVCoordinates = new float[faceCount][]; + + for (int i = 0; i < faceCount; i++) + { + int textureCoordinate; + if (textureCoords == null) + { + textureCoordinate = -1; + } + else + { + textureCoordinate = textureCoords[i]; + } + + short textureIdx; + textureIdx = faceTextures[i]; + + if (textureIdx != -1) + { + float[] u = new float[3]; + float[] v = new float[3]; + + if (textureCoordinate == -1) + { + u[0] = 0.0F; + v[0] = 1.0F; + + u[1] = 1.0F; + v[1] = 1.0F; + + u[2] = 0.0F; + v[2] = 0.0F; + } + else + { + textureCoordinate &= 0xFF; + + byte textureRenderType = 0; + if (textureRenderTypes != null) + { + textureRenderType = textureRenderTypes[textureCoordinate]; + } + + if (textureRenderType == 0) + { + int faceVertexIdx1 = trianglePointsX[i]; + int faceVertexIdx2 = trianglePointsY[i]; + int faceVertexIdx3 = trianglePointsZ[i]; + + int triangleVertexIdx1 = texTriangleX[textureCoordinate]; + int triangleVertexIdx2 = texTriangleY[textureCoordinate]; + int triangleVertexIdx3 = texTriangleZ[textureCoordinate]; + + float triangleX = (float) vertexPositionsX[triangleVertexIdx1]; + float triangleY = (float) vertexPositionsY[triangleVertexIdx1]; + float triangleZ = (float) vertexPositionsZ[triangleVertexIdx1]; + + float f_882_ = (float) vertexPositionsX[triangleVertexIdx2] - triangleX; + float f_883_ = (float) vertexPositionsY[triangleVertexIdx2] - triangleY; + float f_884_ = (float) vertexPositionsZ[triangleVertexIdx2] - triangleZ; + float f_885_ = (float) vertexPositionsX[triangleVertexIdx3] - triangleX; + float f_886_ = (float) vertexPositionsY[triangleVertexIdx3] - triangleY; + float f_887_ = (float) vertexPositionsZ[triangleVertexIdx3] - triangleZ; + float f_888_ = (float) vertexPositionsX[faceVertexIdx1] - triangleX; + float f_889_ = (float) vertexPositionsY[faceVertexIdx1] - triangleY; + float f_890_ = (float) vertexPositionsZ[faceVertexIdx1] - triangleZ; + float f_891_ = (float) vertexPositionsX[faceVertexIdx2] - triangleX; + float f_892_ = (float) vertexPositionsY[faceVertexIdx2] - triangleY; + float f_893_ = (float) vertexPositionsZ[faceVertexIdx2] - triangleZ; + float f_894_ = (float) vertexPositionsX[faceVertexIdx3] - triangleX; + float f_895_ = (float) vertexPositionsY[faceVertexIdx3] - triangleY; + float f_896_ = (float) vertexPositionsZ[faceVertexIdx3] - triangleZ; + + float f_897_ = f_883_ * f_887_ - f_884_ * f_886_; + float f_898_ = f_884_ * f_885_ - f_882_ * f_887_; + float f_899_ = f_882_ * f_886_ - f_883_ * f_885_; + float f_900_ = f_886_ * f_899_ - f_887_ * f_898_; + float f_901_ = f_887_ * f_897_ - f_885_ * f_899_; + float f_902_ = f_885_ * f_898_ - f_886_ * f_897_; + float f_903_ = 1.0F / (f_900_ * f_882_ + f_901_ * f_883_ + f_902_ * f_884_); + + u[0] = (f_900_ * f_888_ + f_901_ * f_889_ + f_902_ * f_890_) * f_903_; + u[1] = (f_900_ * f_891_ + f_901_ * f_892_ + f_902_ * f_893_) * f_903_; + u[2] = (f_900_ * f_894_ + f_901_ * f_895_ + f_902_ * f_896_) * f_903_; + + f_900_ = f_883_ * f_899_ - f_884_ * f_898_; + f_901_ = f_884_ * f_897_ - f_882_ * f_899_; + f_902_ = f_882_ * f_898_ - f_883_ * f_897_; + f_903_ = 1.0F / (f_900_ * f_885_ + f_901_ * f_886_ + f_902_ * f_887_); + + v[0] = (f_900_ * f_888_ + f_901_ * f_889_ + f_902_ * f_890_) * f_903_; + v[1] = (f_900_ * f_891_ + f_901_ * f_892_ + f_902_ * f_893_) * f_903_; + v[2] = (f_900_ * f_894_ + f_901_ * f_895_ + f_902_ * f_896_) * f_903_; + } + } + + this.faceTextureUCoordinates[i] = u; + this.faceTextureVCoordinates[i] = v; + } + } + } + +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSModelMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSModelMixin.java index 478e12cc3ed..a1becc9d9b6 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSModelMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSModelMixin.java @@ -27,11 +27,15 @@ import java.awt.Polygon; import java.util.ArrayList; import java.util.List; +import net.runelite.api.Model; import net.runelite.api.Perspective; import net.runelite.api.Point; import net.runelite.api.coords.LocalPoint; +import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.MethodHook; import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Replace; import net.runelite.api.mixins.Shadow; import net.runelite.api.model.Jarvis; import net.runelite.api.model.Triangle; @@ -48,6 +52,63 @@ public abstract class RSModelMixin implements RSModel @Shadow("clientInstance") private static RSClient client; + @Inject + private int rl$sceneId; + + @Inject + private int rl$bufferOffset; + + @Inject + private int rl$uvBufferOffset; + + @Inject + private float[][] rl$faceTextureUCoordinates; + + @Inject + private float[][] rl$faceTextureVCoordinates; + + @MethodHook(value = "", end = true) + @Inject + public void rl$init(RSModel[] models, int length) + { + int count = 0; + for (int i = 0; i < length; ++i) + { + RSModel model = models[i]; + if (model != null) + { + count += model.getTrianglesCount(); + } + } + + float[][] u = new float[count][]; + float[][] v = new float[count][]; + int idx = 0; + + for (int i = 0; i < length; ++i) + { + RSModel model = models[i]; + if (model != null) + { + float[][] modelU = model.getFaceTextureUCoordinates(); + float[][] modelV = model.getFaceTextureVCoordinates(); + + for (int j = 0; j < model.getTrianglesCount(); ++j) + { + if (modelU != null && modelV != null) + { + u[idx] = modelU[j]; + v[idx] = modelV[j]; + } + ++idx; + } + } + } + + setFaceTextureUCoordinates(u); + setFaceTextureVCoordinates(v); + } + @Override @Inject public List getVertices() @@ -99,6 +160,33 @@ public List getTriangles() return triangles; } + @Copy("contourGround") + public abstract Model rs$contourGround(int[][] tileHeights, int packedX, int height, int packedY, boolean copy, int contouredGround); + + @Replace("contourGround") + public Model rl$contourGround(int[][] tileHeights, int packedX, int height, int packedY, boolean copy, int contouredGround) + { + // With contouredGround >= 0 lighted models are countoured, so we need to copy uvs + Model model = rs$contourGround(tileHeights, packedX, height, packedY, copy, contouredGround); + if (model != null && model != this) + { + RSModel rsModel = (RSModel) model; + rsModel.setFaceTextureUCoordinates(rl$faceTextureUCoordinates); + rsModel.setFaceTextureVCoordinates(rl$faceTextureVCoordinates); + } + return model; + } + + @MethodHook("buildSharedModel") + @Inject + public void rl$buildSharedModel(boolean refTransparencies, Model sharedModel, byte[] transparencyBuffer) + { + // Animated models are usually a shared Model instance that is global + RSModel rsModel = (RSModel) sharedModel; + rsModel.setFaceTextureUCoordinates(rl$faceTextureUCoordinates); + rsModel.setFaceTextureVCoordinates(rl$faceTextureVCoordinates); + } + @Inject public void interpolateFrames(RSFrames frames, int frameId, RSFrames nextFrames, int nextFrameId, int interval, int intervalCount) @@ -277,4 +365,74 @@ public Polygon getConvexHull(int localX, int localY, int orientation) return p; } + + @Inject + @Override + public int getSceneId() + { + return rl$sceneId; + } + + @Inject + @Override + public void setSceneId(int sceneId) + { + this.rl$sceneId = sceneId; + } + + @Inject + @Override + public int getBufferOffset() + { + return rl$bufferOffset; + } + + @Inject + @Override + public void setBufferOffset(int bufferOffset) + { + rl$bufferOffset = bufferOffset; + } + + @Inject + @Override + public int getUvBufferOffset() + { + return rl$uvBufferOffset; + } + + @Inject + @Override + public void setUvBufferOffset(int bufferOffset) + { + rl$uvBufferOffset = bufferOffset; + } + + @Inject + @Override + public float[][] getFaceTextureUCoordinates() + { + return rl$faceTextureUCoordinates; + } + + @Inject + @Override + public void setFaceTextureUCoordinates(float[][] faceTextureUCoordinates) + { + this.rl$faceTextureUCoordinates = faceTextureUCoordinates; + } + + @Inject + @Override + public float[][] getFaceTextureVCoordinates() + { + return rl$faceTextureVCoordinates; + } + + @Inject + @Override + public void setFaceTextureVCoordinates(float[][] faceTextureVCoordinates) + { + this.rl$faceTextureVCoordinates = faceTextureVCoordinates; + } } 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 7b65715fd1a..32180e1def2 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneMixin.java @@ -26,9 +26,13 @@ import net.runelite.api.Perspective; import net.runelite.api.Renderable; +import net.runelite.api.SceneTileModel; import net.runelite.api.SceneTilePaint; import net.runelite.api.Tile; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.hooks.DrawCallbacks; 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,26 +41,45 @@ import net.runelite.rs.api.RSGroundObject; import net.runelite.rs.api.RSItemLayer; import net.runelite.rs.api.RSScene; +import net.runelite.rs.api.RSSceneTileModel; 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 DEFAULT_DISTANCE = 25; + private static final int MAX_DISTANCE = 90; private static final int PITCH_LOWER_LIMIT = 128; private static final int PITCH_UPPER_LIMIT = 383; + private static final int MAX_TARGET_DISTANCE = 45; + @Shadow("clientInstance") - private static RSClient client; + static RSClient client; @Shadow("pitchRelaxEnabled") private static boolean pitchRelaxEnabled; + @Inject + private static int[] tmpX = new int[6]; + + @Inject + private static int[] tmpY = new int[6]; + + @Inject + private static int rl$drawDistance; + @Replace("drawScene") void rl$drawScene(int cameraX, int cameraY, int cameraZ, int cameraPitch, int cameraYaw, int plane) { + final DrawCallbacks drawCallbacks = client.getDrawCallbacks(); + if (drawCallbacks != null) + { + drawCallbacks.drawScene(cameraX, cameraY, cameraZ, cameraPitch, cameraYaw, plane); + } + final int maxX = getMaxX(); final int maxY = getMaxY(); final int maxZ = getMaxZ(); @@ -64,7 +87,8 @@ public abstract class RSSceneMixin implements RSScene final int minLevel = getMinLevel(); final RSTile[][][] tiles = getTiles(); - final int distance = MAX_DISTANCE; + final boolean isGpu = client.isGpu(); + final int distance = isGpu ? rl$drawDistance : DEFAULT_DISTANCE; if (cameraX < 0) { @@ -168,7 +192,8 @@ else if (cameraPitch > PITCH_UPPER_LIMIT) { if (tile.getPhysicalLevel() <= plane && (renderArea[x - screenCenterX + MAX_DISTANCE][y - screenCenterZ + MAX_DISTANCE] - || tileHeights[z][x][y] - cameraY >= 2000)) + || tileHeights[z][x][y] - cameraY >= 2000 + || isGpu)) { tile.setDraw(true); tile.setVisible(true); @@ -402,13 +427,271 @@ else if (cameraPitch > PITCH_UPPER_LIMIT) @Replace("drawTileUnderlay") public void rl$drawTileUnderlay(SceneTilePaint tile, int z, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y) { + if (!client.isGpu()) + { + try + { + rs$drawTileUnderlay(tile, z, pitchSin, pitchCos, yawSin, yawCos, x, y); + } + catch (Exception ex) + { + client.getLogger().warn("error during tile underlay rendering", ex); + } + return; + } + + final DrawCallbacks drawCallbacks = client.getDrawCallbacks(); + + if (drawCallbacks == null) + { + return; + } + + try + { + final int[][][] tileHeights = getTileHeights(); + + final int cameraX2 = client.getCameraX2(); + final int cameraY2 = client.getCameraY2(); + final int cameraZ2 = client.getCameraZ2(); + + final int zoom = client.get3dZoom(); + final int centerX = client.getCenterX(); + final int centerY = client.getCenterY(); + + final int mouseX2 = client.getMouseX2(); + final int mouseY2 = client.getMouseY2(); + + final boolean checkClick = client.isCheckClick(); + + int var9; + int var10 = var9 = (x << 7) - cameraX2; + int var11; + int var12 = var11 = (y << 7) - cameraZ2; + int var13; + int var14 = var13 = var10 + 128; + int var15; + int var16 = var15 = var12 + 128; + int var17 = tileHeights[z][x][y] - cameraY2; + int var18 = tileHeights[z][x + 1][y] - cameraY2; + int var19 = tileHeights[z][x + 1][y + 1] - cameraY2; + int var20 = tileHeights[z][x][y + 1] - cameraY2; + int var21 = var10 * yawCos + yawSin * var12 >> 16; + var12 = var12 * yawCos - yawSin * var10 >> 16; + var10 = var21; + var21 = var17 * pitchCos - pitchSin * var12 >> 16; + var12 = pitchSin * var17 + var12 * pitchCos >> 16; + var17 = var21; + if (var12 >= 50) + { + var21 = var14 * yawCos + yawSin * var11 >> 16; + var11 = var11 * yawCos - yawSin * var14 >> 16; + var14 = var21; + var21 = var18 * pitchCos - pitchSin * var11 >> 16; + var11 = pitchSin * var18 + var11 * pitchCos >> 16; + var18 = var21; + if (var11 >= 50) + { + var21 = var13 * yawCos + yawSin * var16 >> 16; + var16 = var16 * yawCos - yawSin * var13 >> 16; + var13 = var21; + var21 = var19 * pitchCos - pitchSin * var16 >> 16; + var16 = pitchSin * var19 + var16 * pitchCos >> 16; + var19 = var21; + if (var16 >= 50) + { + var21 = var9 * yawCos + yawSin * var15 >> 16; + var15 = var15 * yawCos - yawSin * var9 >> 16; + var9 = var21; + var21 = var20 * pitchCos - pitchSin * var15 >> 16; + var15 = pitchSin * var20 + var15 * pitchCos >> 16; + if (var15 >= 50) + { + int dy = var10 * zoom / var12 + centerX; + int dx = var17 * zoom / var12 + centerY; + int cy = var14 * zoom / var11 + centerX; + int cx = var18 * zoom / var11 + centerY; + int ay = var13 * zoom / var16 + centerX; + int ax = var19 * zoom / var16 + centerY; + int by = var9 * zoom / var15 + centerX; + int bx = var21 * zoom / var15 + centerY; + + drawCallbacks.drawScenePaint(0, pitchSin, pitchCos, yawSin, yawCos, + -cameraX2, -cameraY2, -cameraZ2, + tile, z, x, y, + zoom, centerX, centerY); + + if ((ay - by) * (cx - bx) - (ax - bx) * (cy - by) > 0) + { + if (checkClick && client.containsBounds(mouseX2, mouseY2, ax, bx, cx, ay, by, cy)) + { + setTargetTile(x, y); + } + } + + if ((dy - cy) * (bx - cx) - (dx - cx) * (by - cy) > 0) + { + if (checkClick && client.containsBounds(mouseX2, mouseY2, dx, cx, bx, dy, cy, by)) + { + setTargetTile(x, y); + } + } + + } + } + } + } + } + catch (Exception ex) + { + client.getLogger().warn("error during underlay rendering", ex); + } + } + + @Copy("drawTileOverlay") + abstract public void rs$drawTileOverlay(SceneTileModel tile, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y); + + @Replace("drawTileOverlay") + public void rl$drawTileOverlay(SceneTileModel tile, int pitchSin, int pitchCos, int yawSin, int yawCos, int tileX, int tileY) + { + if (!client.isGpu()) + { + rs$drawTileOverlay(tile, pitchSin, pitchCos, yawSin, yawCos, tileX, tileY); + return; + } + + final DrawCallbacks drawCallbacks = client.getDrawCallbacks(); + + if (drawCallbacks == null) + { + return; + } + try { - rs$drawTileUnderlay(tile, z, pitchSin, pitchCos, yawSin, yawCos, x, y); + final int cameraX2 = client.getCameraX2(); + final int cameraY2 = client.getCameraY2(); + final int cameraZ2 = client.getCameraZ2(); + final int zoom = client.get3dZoom(); + final int centerX = client.getCenterX(); + final int centerY = client.getCenterY(); + + drawCallbacks.drawSceneModel(0, pitchSin, pitchCos, yawSin, yawCos, -cameraX2, -cameraY2, -cameraZ2, + tile, client.getPlane(), tileX, tileY, + zoom, centerX, centerY); + + final boolean checkClick = client.isCheckClick(); + if (!checkClick) + { + return; + } + + RSSceneTileModel sceneTileModel = (RSSceneTileModel) tile; + + final int[] faceX = sceneTileModel.getFaceX(); + final int[] faceY = sceneTileModel.getFaceY(); + final int[] faceZ = sceneTileModel.getFaceZ(); + + final int[] vertexX = sceneTileModel.getVertexX(); + final int[] vertexY = sceneTileModel.getVertexY(); + final int[] vertexZ = sceneTileModel.getVertexZ(); + + final int vertexCount = vertexX.length; + final int faceCount = faceX.length; + + final int mouseX2 = client.getMouseX2(); + final int mouseY2 = client.getMouseY2(); + + for (int i = 0; i < vertexCount; ++i) + { + int vx = vertexX[i] - cameraX2; + int vy = vertexY[i] - cameraY2; + int vz = vertexZ[i] - cameraZ2; + + int rotA = vz * yawSin + vx * yawCos >> 16; + int rotB = vz * yawCos - vx * yawSin >> 16; + + int var13 = vy * pitchCos - rotB * pitchSin >> 16; + int var12 = vy * pitchSin + rotB * pitchCos >> 16; + if (var12 < 50) + { + return; + } + + int ax = rotA * zoom / var12 + centerX; + int ay = var13 * zoom / var12 + centerY; + + tmpX[i] = ax; + tmpY[i] = ay; + } + + for (int i = 0; i < faceCount; ++i) + { + int va = faceX[i]; + int vb = faceY[i]; + int vc = faceZ[i]; + + int x1 = tmpX[va]; + int x2 = tmpX[vb]; + int x3 = tmpX[vc]; + + int y1 = tmpY[va]; + int y2 = tmpY[vb]; + int y3 = tmpY[vc]; + + if ((x1 - x2) * (y3 - y2) - (y1 - y2) * (x3 - x2) > 0) + { + if (client.containsBounds(mouseX2, mouseY2, y1, y2, y3, x1, x2, x3)) + { + setTargetTile(tileX, tileY); + break; + } + } + } } catch (Exception ex) { - client.getLogger().warn("error during tile underlay rendering", ex); + client.getLogger().warn("error during overlay rendering", ex); } } + + @Inject + @Override + public int getDrawDistance() + { + return rl$drawDistance; + } + + @Inject + @Override + public void setDrawDistance(int drawDistance) + { + rl$drawDistance = drawDistance; + } + + @Inject + static void setTargetTile(int targetX, int targetY) + { + final LocalPoint current = client.getLocalPlayer().getLocalLocation(); + + // Limit walk distance - https://math.stackexchange.com/a/85582 + final int a = current.getSceneX(); + final int b = current.getSceneY(); + final int c = targetX; + final int d = targetY; + + final int r = MAX_TARGET_DISTANCE; + final int t = (int) Math.hypot(a - c, b - d) - r; + int x = targetX; + int y = targetY; + + if (t > 0) + { + x = (r * c + t * a) / (r + t); + y = (r * d + t * b) / (r + t); + } + + client.setSelectedSceneTileX(x); + client.setSelectedSceneTileY(y); + } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTileModelMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTileModelMixin.java new file mode 100644 index 00000000000..a1ab51260ed --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTileModelMixin.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.Mixin; +import net.runelite.rs.api.RSSceneTileModel; + +@Mixin(RSSceneTileModel.class) +public abstract class RSSceneTileModelMixin implements RSSceneTileModel +{ + @Inject + private int rl$modelBufferOffset; + + @Inject + private int rl$modelUvBufferOffset; + + @Inject + private int rl$modelBufferLen; + + @Inject + @Override + public int getBufferOffset() + { + return rl$modelBufferOffset; + } + + @Inject + @Override + public void setBufferOffset(int bufferOffset) + { + rl$modelBufferOffset = bufferOffset; + } + + @Inject + @Override + public int getUvBufferOffset() + { + return rl$modelUvBufferOffset; + } + + @Inject + @Override + public void setUvBufferOffset(int bufferOffset) + { + rl$modelUvBufferOffset = bufferOffset; + } + + @Inject + @Override + public int getBufferLen() + { + return rl$modelBufferLen; + } + + @Inject + @Override + public void setBufferLen(int bufferLen) + { + rl$modelBufferLen = bufferLen; + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTilePaintMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTilePaintMixin.java new file mode 100644 index 00000000000..27c719d58c4 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSSceneTilePaintMixin.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.Mixin; +import net.runelite.rs.api.RSSceneTilePaint; + +@Mixin(RSSceneTilePaint.class) +public abstract class RSSceneTilePaintMixin implements RSSceneTilePaint +{ + @Inject + private int rl$paintModelBufferOffset; + + @Inject + private int rl$paintModelUvBufferOffset; + + @Inject + private int rl$paintModelBufferLen; + + @Inject + @Override + public int getBufferOffset() + { + return rl$paintModelBufferOffset; + } + + @Inject + @Override + public void setBufferOffset(int bufferOffset) + { + rl$paintModelBufferOffset = bufferOffset; + } + + @Inject + @Override + public int getUvBufferOffset() + { + return rl$paintModelUvBufferOffset; + } + + @Inject + @Override + public void setUvBufferOffset(int bufferOffset) + { + rl$paintModelUvBufferOffset = bufferOffset; + } + + @Inject + @Override + public int getBufferLen() + { + return rl$paintModelBufferLen; + } + + @Inject + @Override + public void setBufferLen(int bufferLen) + { + rl$paintModelBufferLen = bufferLen; + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureMixin.java new file mode 100644 index 00000000000..357da3f2f2e --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureMixin.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +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; +import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSTexture; + +@Mixin(RSTexture.class) +public abstract class RSTextureMixin implements RSTexture +{ + @Shadow("clientInstance") + private static RSClient client; + + @Inject + private float rl$u; + + @Inject + private float rl$v; + + @Copy("animate") + public abstract void rs$animate(int diff); + + @Replace("animate") + public void rl$animate(int diff) + { + // The client animates textures by cycling the backing pixels of the texture each fram + // based on how long it was since the last tick. On GPU we let the plugin manage this + // which will calculate uvs instead. + if (!client.isGpu()) + { + rs$animate(diff); + return; + } + + client.getDrawCallbacks().animate(this, diff); + } + + @Inject + @Override + public float getU() + { + return rl$u; + } + + @Inject + @Override + public void setU(float u) + { + this.rl$u = u; + } + + @Inject + @Override + public float getV() + { + return rl$v; + } + + @Inject + @Override + public void setV(float v) + { + this.rl$v = v; + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureProviderMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureProviderMixin.java index b48b1f33ae9..df3c15b38f8 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureProviderMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSTextureProviderMixin.java @@ -24,6 +24,7 @@ */ package net.runelite.mixins; +import net.runelite.api.IndexDataBase; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.MethodHook; import net.runelite.api.mixins.Mixin; @@ -37,9 +38,20 @@ public abstract class RSTextureProviderMixin implements RSTextureProvider @Shadow("clientInstance") private static RSClient client; + @MethodHook(value = "", end = true) @Inject + public void rl$init(IndexDataBase indexTextures, IndexDataBase indexSprites, int maxSize, double brightness, int width) + { + // the client's max size is 20, however there are many scenes with >20 textures, + // which causes continuous alloc/free of textures with the gl. There are + // only ~57 textures in total. + setMaxSize(64); + setSize(64); + } + @MethodHook(value = "checkTextures", end = true) - public void checkTextures(int var1) + @Inject + public void checkTextures(int diff) { client.getCallbacks().drawAboveOverheads(); } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/Rasterizer2DMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/Rasterizer2DMixin.java new file mode 100644 index 00000000000..618a45962e3 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/Rasterizer2DMixin.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +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; +import net.runelite.rs.api.RSClient; + +@Mixin(RSClient.class) +public abstract class Rasterizer2DMixin implements RSClient +{ + @Shadow("clientInstance") + private static RSClient client; + + @Inject + private static void drawAlpha(int[] pixels, int index, int value, int color, int alpha) + { + if (!client.isGpu() || pixels != client.getBufferProvider().getPixels()) + { + pixels[index] = value; + return; + } + + if (alpha == 0) + { + return; + } + + int prevColor = pixels[index]; + + if ((prevColor & 0xFF000000) == 0 || alpha == 255) + { + // No transparency, so we can cheat to save CPU resources + pixels[index] = (color & 0xFFFFFF) | (alpha << 24); + return; + } + + if ((prevColor & 0xFF000000) == 0xFF000000) + { + // When the background is opaque, the result will also be opaque, + // so we can simply use the value calculated by Jagex. + pixels[index] = value | 0xFF000000; + return; + } + + int prevAlpha = (prevColor >>> 24) * (255 - alpha) >>> 8; + int finalAlpha = alpha + prevAlpha; + + // Scale alphas so (relativeAlpha >>> 8) is approximately equal to (alpha / finalAlpha). + // Avoiding extra divisions increase performance by quite a bit. + // And with divisions we get a problems if dividing a number where + // the last bit is 1 (as it will become negative). + int relativeAlpha1 = (alpha << 8) / finalAlpha; + int relativeAlpha2 = (prevAlpha << 8) / finalAlpha; + + // Red and blue are calculated at the same time to save CPU cycles + int finalColor = + (((color & 0xFF00FF) * relativeAlpha1 + (prevColor & 0xFF00FF) * relativeAlpha2 & 0xFF00FF00) | + ((color & 0x00FF00) * relativeAlpha1 + (prevColor & 0x00FF00) * relativeAlpha2 & 0x00FF0000)) >>> 8; + + pixels[index] = finalColor | (finalAlpha << 24); + } + + @Copy("drawGradientAlpha") + private static void rs$raster2d6(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) + { + throw new RuntimeException(); + } + + @Replace("drawGradientAlpha") + private static void rl$drawGradientAlpha(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) + { + final int width = client.getGraphicsPixelsWidth(); + final int startX = client.getStartX(); + final int startY = client.getStartY(); + final int endX = client.getEndX(); + final int endY = client.getEndY(); + final int[] pixels = client.getGraphicsPixels(); + + if (!client.isGpu()) + { + rs$raster2d6(var0, var1, var2, var3, var4, var5, var6, var7); + return; + } + + if (var2 > 0 && var3 > 0) + { + int var8 = 0; + int var9 = 65536 / var3; + if (var0 < startX) + { + var2 -= startX - var0; + var0 = startX; + } + + if (var1 < startY) + { + var8 += (startY - var1) * var9; + var3 -= startY - var1; + var1 = startY; + } + + if (var0 + var2 > endX) + { + var2 = endX - var0; + } + + if (var3 + var1 > endY) + { + var3 = endY - var1; + } + + int var10 = width - var2; + int var11 = var0 + width * var1; + + for (int var12 = -var3; var12 < 0; ++var12) + { + int var13 = 65536 - var8 >> 8; + int var14 = var8 >> 8; + int var15 = (var13 * var6 + var14 * var7 & 65280) >>> 8; + if (var15 == 0) + { + var11 += width; + var8 += var9; + } + else + { + int var16 = (var14 * (var5 & 16711935) + var13 * (var4 & 16711935) & -16711936) + (var14 * (var5 & 65280) + var13 * (var4 & 65280) & 16711680) >>> 8; + + for (int var19 = -var2; var19 < 0; ++var19) + { + drawAlpha(pixels, var11++, 0, var16, var15); + } + + var11 += var10; + var8 += var9; + } + } + } + } + + @Copy("raster2d7") + public static void rs$raster2d7(int var0, int var1, int var2, int var3, int var4, int var5, byte[] var6, int var7) + { + throw new RuntimeException(); + } + + @Replace("raster2d7") + public static void rl$raster2d7(int var0, int var1, int var2, int var3, int var4, int var5, byte[] var6, int var7) + { + final int width = client.getGraphicsPixelsWidth(); + final int height = client.getGraphicsPixelsHeight(); + final int[] pixels = client.getGraphicsPixels(); + + if (!client.isGpu()) + { + rs$raster2d7(var0, var1, var2, var3, var4, var5, var6, var7); + return; + } + + if (var0 + var2 >= 0 && var3 + var1 >= 0) + { + if (var0 < width && var1 < height) + { + int var8 = 0; + int var9 = 0; + if (var0 < 0) + { + var8 -= var0; + var2 += var0; + } + + if (var1 < 0) + { + var9 -= var1; + var3 += var1; + } + + if (var0 + var2 > width) + { + var2 = width - var0; + } + + if (var3 + var1 > height) + { + var3 = height - var1; + } + + int var10 = var6.length / var7; + int var11 = width - var2; + int var12 = var4 >>> 24; + int var13 = var5 >>> 24; + int var14; + int var15; + int var16; + int var17; + int var18; + if (var12 == 255 && var13 == 255) + { + var14 = var0 + var8 + (var9 + var1) * width; + + for (var15 = var9 + var1; var15 < var3 + var9 + var1; ++var15) + { + for (var16 = var0 + var8; var16 < var0 + var8 + var2; ++var16) + { + var17 = (var15 - var1) % var10; + var18 = (var16 - var0) % var7; + if (var6[var18 + var17 * var7] != 0) + { + pixels[var14++] = var5; + } + else + { + pixels[var14++] = var4; + } + } + + var14 += var11; + } + } + else + { + var14 = var0 + var8 + (var9 + var1) * width; + + for (var15 = var9 + var1; var15 < var3 + var9 + var1; ++var15) + { + for (var16 = var0 + var8; var16 < var0 + var8 + var2; ++var16) + { + var17 = (var15 - var1) % var10; + var18 = (var16 - var0) % var7; + int var19 = var4; + if (var6[var18 + var17 * var7] != 0) + { + var19 = var5; + } + + int var20 = var19 >>> 24; + drawAlpha(pixels, var14++, 0, var19, var20); + } + + var14 += var11; + } + } + } + } + } +} diff --git a/runelite-mixins/src/test/java/net/runelite/mixins/RSSceneMixinTest.java b/runelite-mixins/src/test/java/net/runelite/mixins/RSSceneMixinTest.java new file mode 100644 index 00000000000..54097331eb4 --- /dev/null +++ b/runelite-mixins/src/test/java/net/runelite/mixins/RSSceneMixinTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import net.runelite.api.coords.LocalPoint; +import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSPlayer; +import org.junit.Test; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class RSSceneMixinTest +{ + @Test + public void testWalkDistance() + { + RSClient client = mock(RSClient.class); + RSPlayer localPlayer = mock(RSPlayer.class); + when(client.getLocalPlayer()).thenReturn(localPlayer); + + when(localPlayer.getLocalLocation()).thenReturn(new LocalPoint(0, 0)); + + RSSceneMixin.client = client; + RSSceneMixin.setTargetTile(90, 0); + + verify(client).setSelectedSceneTileX(eq(45)); + verify(client).setSelectedSceneTileY(eq(0)); + } +} \ No newline at end of file diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSBuffer.java b/runescape-api/src/main/java/net/runelite/rs/api/RSBuffer.java index 361147d7159..0c357c3fee7 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSBuffer.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSBuffer.java @@ -26,7 +26,7 @@ import net.runelite.mapping.Import; -public interface RSBuffer +public interface RSBuffer extends RSNode { @Import("payload") byte[] getPayload(); diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSBufferProvider.java b/runescape-api/src/main/java/net/runelite/rs/api/RSBufferProvider.java index 8c555a28735..c46cfa6b7cf 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSBufferProvider.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSBufferProvider.java @@ -29,6 +29,18 @@ public interface RSBufferProvider extends BufferProvider { + @Import("pixels") + @Override + int[] getPixels(); + + @Import("width") + @Override + int getWidth(); + + @Import("height") + @Override + int getHeight(); + @Import("setRaster") void setRaster(); } 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 c4bacaf9848..ba38426ffe0 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 @@ -48,6 +48,18 @@ public interface RSClient extends RSGameEngine, Client @Override int getCameraZ(); + @Import("cameraX2") + @Override + int getCameraX2(); + + @Import("cameraY2") + @Override + int getCameraY2(); + + @Import("cameraZ2") + @Override + int getCameraZ2(); + @Import("plane") @Override int getPlane(); @@ -146,9 +158,15 @@ public interface RSClient extends RSGameEngine, Client @Import("selectedSceneTileX") int getSelectedSceneTileX(); + @Import("selectedSceneTileX") + void setSelectedSceneTileX(int selectedSceneTileX); + @Import("selectedSceneTileY") int getSelectedSceneTileY(); + @Import("selectedSceneTileY") + void setSelectedSceneTileY(int selectedSceneTileY); + @Import("draggingWidget") @Override boolean isDraggingWidget(); @@ -246,6 +264,18 @@ public interface RSClient extends RSGameEngine, Client @Import("mouseY") int getMouseY(); + @Import("mouseX2") + int getMouseX2(); + + @Import("mouseY2") + int getMouseY2(); + + @Import("containsBounds") + boolean containsBounds(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7); + + @Import("checkClick") + boolean isCheckClick(); + @Import("menuOptionCount") int getMenuOptionCount(); @@ -289,6 +319,14 @@ public interface RSClient extends RSGameEngine, Client @Override int getScale(); + @Import("canvasHeight") + @Override + int getCanvasHeight(); + + @Import("canvasWidth") + @Override + int getCanvasWidth(); + @Import("viewportHeight") @Override int getViewportHeight(); @@ -565,11 +603,36 @@ public interface RSClient extends RSGameEngine, Client void setYawCos(int v); @Import("Rasterizer3D_zoom") + @Override int get3dZoom(); @Import("Rasterizer3D_zoom") void set3dZoom(int zoom); + @Import("Rasterizer3D_clipMidX2") + @Override + int getRasterizer3D_clipMidX2(); + + @Import("Rasterizer3D_clipNegativeMidX") + @Override + int getRasterizer3D_clipNegativeMidX(); + + @Import("Rasterizer3D_clipNegativeMidY") + @Override + int getRasterizer3D_clipNegativeMidY(); + + @Import("Rasterizer3D_clipMidY2") + @Override + int getRasterizer3D_clipMidY2(); + + @Import("centerX") + @Override + int getCenterX(); + + @Import("centerY") + @Override + int getCenterY(); + @Import("renderOverview") RSRenderOverview getRenderOverview(); @@ -664,6 +727,10 @@ public interface RSClient extends RSGameEngine, Client @Override RSNodeCache getWidgetSpriteCache(); + @Import("oculusOrbState") + @Override + int getOculusOrbState(); + @Import("oculusOrbState") @Override void setOculusOrbState(int state); @@ -689,9 +756,31 @@ public interface RSClient extends RSGameEngine, Client @Protect void menuAction(int var0, int var1, int var2, int var3, String var4, String var5, int var6, int var7); + @Import("Viewport_entityCountAtMouse") + int getEntitiesAtMouseCount(); + + @Import("Viewport_entityCountAtMouse") + void setEntitiesAtMouseCount(int i); + + @Import("Viewport_entitiesAtMouse") + long[] getEntitiesAtMouse(); + + @Import("Viewport_mouseX") + int getViewportMouseX(); + + @Import("Viewport_mouseY") + int getViewportMouseY(); + + @Import("textureProvider") + @Override + RSTextureProvider getTextureProvider(); + @Import("occupiedTilesTick") int[][] getOccupiedTilesTick(); + @Import("cachedModels2") + RSNodeCache getCachedModels2(); + @Import("cycle") int getCycle(); @@ -739,4 +828,28 @@ public interface RSClient extends RSGameEngine, Client @Import("tileUpdateCount") void setTileUpdateCount(int tileUpdateCount); + + @Import("Viewport_containsMouse") + boolean getViewportContainsMouse(); + + @Import("graphicsPixels") + int[] getGraphicsPixels(); + + @Import("graphicsPixelsWidth") + int getGraphicsPixelsWidth(); + + @Import("graphicsPixelsHeight") + int getGraphicsPixelsHeight(); + + @Import("startX") + int getStartX(); + + @Import("startY") + int getStartY(); + + @Import("endX") + int getEndX(); + + @Import("endY") + int getEndY(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java index 7e4977d26c2..1a53a49cb9d 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java @@ -43,7 +43,12 @@ public interface RSDecorativeObject extends DecorativeObject int getOrientation(); @Import("renderable1") + @Override RSRenderable getRenderable(); + @Import("renderable2") + @Override + RSRenderable getRenderable2(); + void setPlane(int plane); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSGameEngine.java b/runescape-api/src/main/java/net/runelite/rs/api/RSGameEngine.java index 9af800a78b3..7dafb17a774 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSGameEngine.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSGameEngine.java @@ -33,6 +33,22 @@ public interface RSGameEngine extends GameEngine @Import("canvas") Canvas getCanvas(); + @Import("post") + void post(Object canvas); + + @Import("resizeCanvas") + @Override + void resizeCanvas(); + + @Import("resizeCanvasNextFrame") + boolean isResizeCanvasNextFrame(); + @Import("resizeCanvasNextFrame") - void setResizeCanvasNextFrame(boolean state); + void setResizeCanvasNextFrame(boolean resize); + + @Import("replaceCanvasNextFrame") + boolean isReplaceCanvasNextFrame(); + + @Import("replaceCanvasNextFrame") + void setReplaceCanvasNextFrame(boolean replace); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java index 73b754aa99b..efd272cdff1 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java @@ -25,7 +25,6 @@ package net.runelite.rs.api; import net.runelite.api.GroundObject; -import net.runelite.api.Renderable; import net.runelite.mapping.Import; public interface RSGroundObject extends GroundObject @@ -41,7 +40,8 @@ public interface RSGroundObject extends GroundObject int getY(); @Import("renderable") - Renderable getRenderable(); + @Override + RSRenderable getRenderable(); void setPlane(int plane); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSMainBufferProvider.java b/runescape-api/src/main/java/net/runelite/rs/api/RSMainBufferProvider.java index b7e7e733ef0..8b27911a272 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSMainBufferProvider.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSMainBufferProvider.java @@ -24,6 +24,7 @@ */ package net.runelite.rs.api; +import java.awt.Component; import java.awt.Image; import net.runelite.api.MainBufferProvider; import net.runelite.mapping.Import; @@ -33,4 +34,10 @@ public interface RSMainBufferProvider extends RSBufferProvider, MainBufferProvid @Import("image") @Override Image getImage(); + + @Import("image") + void setImage(Image image); + + @Import("canvas") + Component getCanvas(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSModel.java b/runescape-api/src/main/java/net/runelite/rs/api/RSModel.java index 8f4072f5812..ba7f29788d5 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSModel.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSModel.java @@ -31,35 +31,75 @@ public interface RSModel extends RSRenderable, Model { @Import("verticesCount") + @Override int getVerticesCount(); @Import("verticesX") + @Override int[] getVerticesX(); @Import("verticesY") + @Override int[] getVerticesY(); @Import("verticesZ") + @Override int[] getVerticesZ(); @Import("indicesCount") + @Override int getTrianglesCount(); @Import("indices1") + @Override int[] getTrianglesX(); @Import("indices2") + @Override int[] getTrianglesY(); @Import("indices3") + @Override int[] getTrianglesZ(); + @Import("faceColors1") + @Override + int[] getFaceColors1(); + + @Import("faceColors2") + @Override + int[] getFaceColors2(); + + @Import("faceColors3") + @Override + int[] getFaceColors3(); + + @Import("triangleTransparencies") + @Override + byte[] getTriangleTransparencies(); + + @Import("faceRenderPriorities") + @Override + byte[] getFaceRenderPriorities(); + @Import("vertexGroups") int[][] getVertexGroups(); + @Import("modelHeight") + @Override + int getModelHeight(); + @Import("animate") void animate(int type, int[] list, int x, int y, int z); + @Import("calculateBoundsCylinder") + @Override + void calculateBoundsCylinder(); + + @Import("calculateExtreme") + @Override + void calculateExtreme(int orientation); + @Import("resetBounds") void resetBounds(); @@ -78,6 +118,45 @@ public interface RSModel extends RSRenderable, Model @Import("rotateY270Ccw") void rotateY270Ccw(); + @Import("isClickable") + boolean isClickable(); + + @Import("radius") + @Override + int getRadius(); + + @Import("centerX") + @Override + int getCenterX(); + + @Import("centerY") + @Override + int getCenterY(); + + @Import("centerZ") + @Override + int getCenterZ(); + + @Import("extremeX") + @Override + int getExtremeX(); + + @Import("extremeY") + @Override + int getExtremeY(); + + @Import("extremeZ") + @Override + int getExtremeZ(); + + @Import("faceTextures") + @Override + short[] getFaceTextures(); + + @Import("XYZMag") + @Override + int getXYZMag(); + void interpolateFrames(RSFrames frames, int frameId, RSFrames nextFrames, int nextFrameId, int interval, int intervalCount); @@ -89,4 +168,10 @@ void interpolateFrames(RSFrames frames, int frameId, RSFrames nextFrames, int ne * @return */ Polygon getConvexHull(int localX, int localY, int orientation); + + float[][] getFaceTextureUCoordinates(); + void setFaceTextureUCoordinates(float[][] rl$faceTextureUCoordinates); + + float[][] getFaceTextureVCoordinates(); + void setFaceTextureVCoordinates(float[][] rl$faceTextureVCoordinates); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSModelData.java b/runescape-api/src/main/java/net/runelite/rs/api/RSModelData.java new file mode 100644 index 00000000000..4503e75729e --- /dev/null +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSModelData.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.rs.api; + +import net.runelite.mapping.Import; + +public interface RSModelData extends RSRenderable +{ + @Import("triangleFaceCount") + int getTriangleFaceCount(); + + @Import("trianglePointsX") + int[] getTrianglePointsX(); + + @Import("trianglePointsY") + int[] getTrianglePointsY(); + + @Import("trianglePointsZ") + int[] getTrianglePointsZ(); + + @Import("vertexX") + int[] getVertexX(); + + @Import("vertexY") + int[] getVertexY(); + + @Import("vertexZ") + int[] getVertexZ(); + + @Import("texTriangleX") + short[] getTexTriangleX(); + + @Import("texTriangleY") + short[] getTexTriangleY(); + + @Import("texTriangleZ") + short[] getTexTriangleZ(); + + @Import("faceTextures") + short[] getFaceTextures(); + + @Import("textureCoords") + byte[] getTextureCoords(); + + @Import("textureRenderTypes") + byte[] getTextureRenderTypes(); +} diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSNodeCache.java b/runescape-api/src/main/java/net/runelite/rs/api/RSNodeCache.java index fef11bb3701..6a7eb9e2ecd 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSNodeCache.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSNodeCache.java @@ -33,5 +33,14 @@ public interface RSNodeCache extends NodeCache RSCacheableNode get(long id); @Import("reset") + @Override void reset(); + + @Import("capacity") + @Override + void setCapacity(int capacity); + + @Import("remainingCapacity") + @Override + void setRemainingCapacity(int remainingCapacity); } \ No newline at end of file diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSRenderable.java b/runescape-api/src/main/java/net/runelite/rs/api/RSRenderable.java index d9bff9eb5bb..398c68088a5 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSRenderable.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSRenderable.java @@ -32,7 +32,15 @@ public interface RSRenderable extends RSNode, Renderable @Import("modelHeight") int getModelHeight(); + @Import("modelHeight") + @Override + void setModelHeight(int modelHeight); + @Import("getModel") @Override RSModel getModel(); + + @Import("draw") + @Override + void draw(int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash); } 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 312b12f5577..3cfeed4fe55 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 @@ -40,6 +40,9 @@ public interface RSScene extends Scene @Import("draw") void draw(Tile tile, boolean var2); + @Import("tileHeights") + int[][][] getTileHeights(); + @Import("drawTile") void drawTile(int[] pixels, int pixelOffset, int width, int z, int x, int y); diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTileModel.java b/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTileModel.java index dcec5d5f5c6..35c3fa6e5c3 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTileModel.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTileModel.java @@ -44,4 +44,44 @@ public interface RSSceneTileModel extends SceneTileModel @Import("rotation") @Override int getRotation(); + + @Import("faceX") + @Override + int[] getFaceX(); + + @Import("faceY") + @Override + int[] getFaceY(); + + @Import("faceZ") + @Override + int[] getFaceZ(); + + @Import("vertexX") + @Override + int[] getVertexX(); + + @Import("vertexY") + @Override + int[] getVertexY(); + + @Import("vertexZ") + @Override + int[] getVertexZ(); + + @Import("triangleColorA") + @Override + int[] getTriangleColorA(); + + @Import("triangleColorB") + @Override + int[] getTriangleColorB(); + + @Import("triangleColorC") + @Override + int[] getTriangleColorC(); + + @Import("triangleTextureId") + @Override + int[] getTriangleTextureId(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTilePaint.java b/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTilePaint.java index 30d61a5ab25..99c6b9f7633 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTilePaint.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSSceneTilePaint.java @@ -32,4 +32,24 @@ public interface RSSceneTilePaint extends SceneTilePaint @Import("rgb") @Override int getRBG(); + + @Import("swColor") + @Override + int getSwColor(); + + @Import("seColor") + @Override + int getSeColor(); + + @Import("nwColor") + @Override + int getNwColor(); + + @Import("neColor") + @Override + int getNeColor(); + + @Import("texture") + @Override + int getTexture(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSTexture.java b/runescape-api/src/main/java/net/runelite/rs/api/RSTexture.java new file mode 100644 index 00000000000..60cbe94b032 --- /dev/null +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSTexture.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.rs.api; + +import net.runelite.api.Texture; +import net.runelite.mapping.Import; + +public interface RSTexture extends Texture, RSNode +{ + @Import("pixels") + @Override + int[] getPixels(); + + @Import("animationDirection") + @Override + int getAnimationDirection(); + + @Import("animationSpeed") + @Override + int getAnimationSpeed(); + + @Import("loaded") + @Override + boolean isLoaded(); +} diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSTextureProvider.java b/runescape-api/src/main/java/net/runelite/rs/api/RSTextureProvider.java index fd41b1aa7eb..2651367376f 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSTextureProvider.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSTextureProvider.java @@ -29,7 +29,25 @@ public interface RSTextureProvider extends TextureProvider { + @Import("brightness") @Override - @Import("checkTextures") - void checkTextures(int var1); + double getBrightness(); + + @Import("setBrightness") + @Override + void setBrightness(double brightness); + + @Import("maxSize") + void setMaxSize(int maxSize); + + @Import("size") + void setSize(int size); + + @Import("textures") + @Override + RSTexture[] getTextures(); + + @Import("load") + @Override + int[] load(int textureId); } 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 bd53aa30601..30a81196a5f 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 @@ -74,9 +74,20 @@ public interface RSTile extends Tile @Override int getPlane(); + @Import("renderLevel") + @Override + int getRenderLevel(); + @Import("physicalLevel") int getPhysicalLevel(); + @Import("flags") + int getFlags(); + + @Import("bridge") + @Override + RSTile getBridge(); + @Import("draw") boolean isDraw(); @@ -89,9 +100,6 @@ public interface RSTile extends Tile @Import("visible") void setVisible(boolean visible); - @Import("drawEntities") - boolean isDrawEntities(); - @Import("drawEntities") void setDrawEntities(boolean drawEntities); diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java index e8765bb6454..1406f49e5d8 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java @@ -24,7 +24,6 @@ */ package net.runelite.rs.api; -import net.runelite.api.Renderable; import net.runelite.api.WallObject; import net.runelite.mapping.Import; @@ -51,10 +50,12 @@ public interface RSWallObject extends WallObject int getOrientationB(); @Import("renderable1") - Renderable getRenderable1(); + @Override + RSRenderable getRenderable1(); @Import("renderable2") - Renderable getRenderable2(); + @Override + RSRenderable getRenderable2(); @Import("config") @Override