diff --git a/cache/pom.xml b/cache/pom.xml
index 5e2dff68969..c6d9f6da299 100644
--- a/cache/pom.xml
+++ b/cache/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.11.23-SNAPSHOT
+ 1.12.0-SNAPSHOT
cache
diff --git a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java
index f2397f4e864..69db696418d 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java
@@ -78,5 +78,7 @@ public class NpcDefinition
public int height = -1;
public int[] stats = {1, 1, 1, 1, 1, 1};
public int footprintSize = -1;
+ public boolean canHideForOverlap;
+ public int overlapTintHSL = 39188;
public boolean unknown1 = false;
}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java
index 0b1f07e50a4..3a93d10baee 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java
@@ -79,6 +79,7 @@ public class ObjectDefinition
private int soundFadeOutDuration = 300;
private int soundFadeInCurve;
private int soundFadeOutCurve;
+ private int soundVisibility = 2;
private int ambientSoundChangeTicksMin = 0;
private int ambientSoundChangeTicksMax = 0;
private boolean blocksProjectile = true;
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java
index d76b6bbee83..726148d1385 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java
@@ -383,6 +383,14 @@ else if (opcode == 129)
{
def.unknown1 = true;
}
+ else if (opcode == 145)
+ {
+ def.canHideForOverlap = true;
+ }
+ else if (opcode == 146)
+ {
+ def.overlapTintHSL = stream.readUnsignedShort();
+ }
else if (opcode == 249)
{
length = stream.readUnsignedByte();
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java
index 15a8ec35d84..f11aab4ac8b 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java
@@ -392,6 +392,10 @@ else if (opcode == 94)
{
def.setUnknown1(true);
}
+ else if (opcode == 95)
+ {
+ def.setSoundVisibility(is.readUnsignedByte());
+ }
else if (opcode == 249)
{
int length = is.readUnsignedByte();
diff --git a/cache/src/main/java/net/runelite/cache/fs/Archive.java b/cache/src/main/java/net/runelite/cache/fs/Archive.java
index b6b4105c97f..46b77ce5fd2 100644
--- a/cache/src/main/java/net/runelite/cache/fs/Archive.java
+++ b/cache/src/main/java/net/runelite/cache/fs/Archive.java
@@ -82,15 +82,7 @@ public byte[] decompress(byte[] data, int[] keys) throws IOException
return null;
}
- byte[] encryptedData = data;
-
- Container container = Container.decompress(encryptedData, keys);
- if (container == null)
- {
- logger.warn("Unable to decrypt archive {}", this);
- return null;
- }
-
+ Container container = Container.decompress(data, keys);
byte[] decompressedData = container.data;
if (this.crc != container.crc)
@@ -99,20 +91,20 @@ public byte[] decompress(byte[] data, int[] keys) throws IOException
throw new IOException("CRC mismatch for " + index.getId() + "/" + this.getArchiveId());
}
- if (container.revision != -1 && this.getRevision() != container.revision)
+ if (container.revision != -1 && this.revision != container.revision)
{
// compressed data doesn't always include a revision, but check it if it does
logger.warn("revision mismatch for archive {}/{}, expected {} was {}",
index.getId(), this.getArchiveId(),
- this.getRevision(), container.revision);
+ this.revision, container.revision);
// I've seen this happen with vanilla caches where the
// revision in the index data differs from the revision
// stored for the archive data on disk... I assume this
// is more correct
- this.setRevision(container.revision);
+ this.revision = container.revision;
}
- setCompression(container.compression);
+ this.compression = container.compression;
return decompressedData;
}
diff --git a/cache/src/main/java/net/runelite/cache/fs/Container.java b/cache/src/main/java/net/runelite/cache/fs/Container.java
index e528f941647..52635d19278 100644
--- a/cache/src/main/java/net/runelite/cache/fs/Container.java
+++ b/cache/src/main/java/net/runelite/cache/fs/Container.java
@@ -105,7 +105,6 @@ public static Container decompress(byte[] b, int[] keys) throws IOException
crc32.update(b, 0, 5); // compression + length
byte[] data;
- int revision = -1;
switch (compression)
{
case CompressionType.NONE:
@@ -116,12 +115,6 @@ public static Container decompress(byte[] b, int[] keys) throws IOException
crc32.update(encryptedData, 0, compressedLength);
byte[] decryptedData = decrypt(encryptedData, encryptedData.length, keys);
- if (stream.remaining() >= 2)
- {
- revision = stream.readUnsignedShort();
- assert revision != -1;
- }
-
data = decryptedData;
break;
@@ -134,16 +127,10 @@ public static Container decompress(byte[] b, int[] keys) throws IOException
crc32.update(encryptedData, 0, encryptedData.length);
byte[] decryptedData = decrypt(encryptedData, encryptedData.length, keys);
- if (stream.remaining() >= 2)
- {
- revision = stream.readUnsignedShort();
- assert revision != -1;
- }
-
- stream = new InputStream(decryptedData);
+ InputStream decryptedStream = new InputStream(decryptedData);
- int decompressedLength = stream.readInt();
- data = BZip2.decompress(stream.getRemaining(), compressedLength);
+ int decompressedLength = decryptedStream.readInt();
+ data = BZip2.decompress(decryptedStream.getRemaining(), compressedLength);
assert data.length == decompressedLength;
break;
@@ -156,22 +143,26 @@ public static Container decompress(byte[] b, int[] keys) throws IOException
crc32.update(encryptedData, 0, encryptedData.length);
byte[] decryptedData = decrypt(encryptedData, encryptedData.length, keys);
- if (stream.remaining() >= 2)
- {
- revision = stream.readUnsignedShort();
- assert revision != -1;
- }
+ InputStream decryptedStream = new InputStream(decryptedData);
- stream = new InputStream(decryptedData);
-
- int decompressedLength = stream.readInt();
- data = GZip.decompress(stream.getRemaining(), compressedLength);
+ int decompressedLength = decryptedStream.readInt();
+ data = GZip.decompress(decryptedStream.getRemaining(), compressedLength);
assert data.length == decompressedLength;
break;
}
default:
- throw new RuntimeException("Unknown decompression type");
+ throw new RuntimeException("Unknown compression type");
+ }
+
+ int revision = -1;
+ if (stream.remaining() >= 4)
+ {
+ revision = stream.readInt();
+ }
+ else if (stream.remaining() >= 2)
+ {
+ revision = stream.readUnsignedShort();
}
Container container = new Container(compression, revision);
diff --git a/pom.xml b/pom.xml
index 1ba106c7249..035871b94ea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
net.runelite
runelite-parent
- 1.11.23-SNAPSHOT
+ 1.12.0-SNAPSHOT
pom
RuneLite
diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml
index 5c540048b26..7a3a3a3601a 100644
--- a/runelite-api/pom.xml
+++ b/runelite-api/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.11.23-SNAPSHOT
+ 1.12.0-SNAPSHOT
runelite-api
diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index eb5c87c18ea..3fe1c6c2621 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.11.23-SNAPSHOT
+ 1.12.0-SNAPSHOT
client
@@ -41,7 +41,7 @@
nogit
false
false
- 2.0.35
+ 2.0.36
nogit
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 7e997d21058..665a78188c4 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
@@ -38,7 +38,6 @@
import java.awt.image.VolatileImage;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -115,6 +114,7 @@ public class Hooks implements Callbacks
@Nullable
private final RuntimeConfig runtimeConfig;
private final boolean developerMode;
+ private final RenderCallbackManager renderCallbackManager;
private Dimension lastStretchedDimensions;
private VolatileImage stretchedImage;
@@ -130,13 +130,21 @@ public class Hooks implements Callbacks
private boolean rateLimitedError;
private int errorBackoff = 1;
+ /**
+ * use {@link RenderCallbackManager} instead
+ */
@FunctionalInterface
- public interface RenderableDrawListener
+ @Deprecated
+ public interface RenderableDrawListener extends RenderCallback
{
boolean draw(Renderable renderable, boolean ui);
- }
- private final List renderableDrawListeners = new ArrayList<>();
+ @Override
+ default boolean drawEntity(Renderable renderable, boolean ui)
+ {
+ return draw(renderable, ui);
+ }
+ }
/**
* Get the Graphics2D for the MainBufferProvider image
@@ -177,7 +185,8 @@ private Hooks(
ClientUI clientUi,
@Nullable TelemetryClient telemetryClient,
@Nullable RuntimeConfig runtimeConfig,
- @Named("developerMode") final boolean developerMode
+ @Named("developerMode") final boolean developerMode,
+ RenderCallbackManager renderCallbackManager
)
{
this.client = client;
@@ -196,6 +205,7 @@ private Hooks(
this.telemetryClient = telemetryClient;
this.runtimeConfig = runtimeConfig;
this.developerMode = developerMode;
+ this.renderCallbackManager = renderCallbackManager;
eventBus.register(this);
}
@@ -577,14 +587,22 @@ public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent)
eventBus.post(fakeXpDrop);
}
+ /**
+ * use {@link RenderCallbackManager#register(RenderCallback)} instead
+ */
+ @Deprecated
public void registerRenderableDrawListener(RenderableDrawListener listener)
{
- renderableDrawListeners.add(listener);
+ renderCallbackManager.register(listener);
}
+ /**
+ * use {@link RenderCallbackManager#unregister(RenderCallback)} instead
+ */
+ @Deprecated
public void unregisterRenderableDrawListener(RenderableDrawListener listener)
{
- renderableDrawListeners.remove(listener);
+ renderCallbackManager.unregister(listener);
}
@Override
@@ -592,17 +610,11 @@ public boolean draw(Renderable renderable, boolean drawingUi)
{
try
{
- for (RenderableDrawListener renderableDrawListener : renderableDrawListeners)
- {
- if (!renderableDrawListener.draw(renderable, drawingUi))
- {
- return false;
- }
- }
+ return renderCallbackManager.drawEntity(renderable, drawingUi);
}
catch (Exception ex)
{
- log.error("exception from renderable draw listener", ex);
+ log.error("exception from render callback", ex);
}
return true;
}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java
similarity index 52%
rename from runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl
rename to runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java
index 530cb990cc9..9749178aa3e 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl
+++ b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Adam
+ * Copyright (c) 2025, Adam
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,26 +22,50 @@
* (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.callback;
-/*
- * Convert a vertex to screen space
- */
-vec3 toScreen(vec3 vertex, float cameraYaw, float cameraPitch, int centerX, int centerY, int zoom) {
- float yawSin = sin(cameraYaw);
- float yawCos = cos(cameraYaw);
-
- float pitchSin = sin(cameraPitch);
- float pitchCos = cos(cameraPitch);
-
- float rotatedX = (vertex.z * yawSin) + (vertex.x * yawCos);
- float rotatedZ = (vertex.z * yawCos) - (vertex.x * yawSin);
+import net.runelite.api.Renderable;
+import net.runelite.api.Scene;
+import net.runelite.api.Tile;
+import net.runelite.api.TileObject;
- float var13 = (vertex.y * pitchCos) - (rotatedZ * pitchSin);
- float var12 = (vertex.y * pitchSin) + (rotatedZ * pitchCos);
+public interface RenderCallback
+{
+ /**
+ * Tests if an entity should be rendered.
+ * This is called multiple times per frame from the client thread.
+ * Entities are temporary entities (players, npcs, projectiles, spotanims, etc).
+ * @param renderable the entity
+ * @param ui true if this test is for drawing the ui (hitbars etc)
+ * @return
+ */
+ default boolean drawEntity(Renderable renderable, boolean ui)
+ {
+ return true;
+ }
- float x = rotatedX * zoom / var12 + centerX;
- float y = var13 * zoom / var12 + centerY;
- float z = -var12; // in OpenGL depth is negative
+ /**
+ * Test if a tile should be drawn.
+ * This is called on scene upload, by the maploader thread.
+ * @param scene
+ * @param tile
+ * @return
+ */
+ default boolean drawTile(Scene scene, Tile tile)
+ {
+ return true;
+ }
- return vec3(x, y, z);
+ /**
+ * Test if a tileobject should be drawn.
+ * This is called on scene upload, by the maploader thread, as well as
+ * each frame by the client thread for dynamic objects (animated objects)
+ * @param scene
+ * @param object
+ * @return
+ */
+ default boolean drawObject(Scene scene, TileObject object)
+ {
+ return true;
+ }
}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java
similarity index 51%
rename from runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl
rename to runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java
index 39dbed12fe3..8fc6a861001 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl
+++ b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Adam
+ * Copyright (c) 2025, Adam
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,61 +22,82 @@
* (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.callback;
-#define PI 3.1415926535897932384626433832795f
-#define UNIT PI / 1024.0f
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import javax.inject.Singleton;
+import net.runelite.api.Renderable;
+import net.runelite.api.Scene;
+import net.runelite.api.Tile;
+import net.runelite.api.TileObject;
-layout(std140) uniform uniforms {
- float cameraYaw;
- float cameraPitch;
- int centerX;
- int centerY;
- int zoom;
- float cameraX;
- float cameraY;
- float cameraZ;
-};
+@Singleton
+public class RenderCallbackManager
+{
+ private final List callbacks = new CopyOnWriteArrayList<>();
-struct modelinfo {
- int offset; // offset into vertex buffer
- int toffset; // offset into texture buffer
- int size; // length in faces
- int idx; // write idx in target buffer
- int flags; // buffer, hillskew, plane, orientation
- int x; // scene position x
- int y; // scene position y
- int z; // scene position z
-};
+ public void register(RenderCallback cb)
+ {
+ callbacks.add(cb);
+ }
-struct vert {
- vec3 pos;
- int ahsl;
-};
+ public void unregister(RenderCallback cb)
+ {
+ callbacks.remove(cb);
+ }
-layout(std430, binding = 0) readonly buffer modelbuffer_in {
- modelinfo ol[];
-};
+ public boolean drawEntity(Renderable renderable, boolean ui)
+ {
+ if (callbacks.isEmpty())
+ {
+ return true;
+ }
-layout(std430, binding = 1) readonly buffer vertexbuffer_in {
- vert vb[];
-};
+ for (var cb : callbacks)
+ {
+ if (!cb.drawEntity(renderable, ui))
+ {
+ return false;
+ }
+ }
-layout(std430, binding = 2) readonly buffer tempvertexbuffer_in {
- vert tempvb[];
-};
+ return true;
+ }
-layout(std430, binding = 3) writeonly buffer vertex_out {
- vert vout[];
-};
+ public boolean drawTile(Scene scene, Tile tile)
+ {
+ if (callbacks.isEmpty())
+ {
+ return true;
+ }
-layout(std430, binding = 4) writeonly buffer uv_out {
- vec4 uvout[];
-};
+ for (var cb : callbacks)
+ {
+ if (!cb.drawTile(scene, tile))
+ {
+ return false;
+ }
+ }
-layout(std430, binding = 5) readonly buffer texturebuffer_in {
- vec4 texb[];
-};
+ return true;
+ }
-layout(std430, binding = 6) readonly buffer temptexturebuffer_in {
- vec4 temptexb[];
-};
\ No newline at end of file
+ public boolean drawObject(Scene scene, TileObject object)
+ {
+ if (callbacks.isEmpty())
+ {
+ return true;
+ }
+
+ for (var cb : callbacks)
+ {
+ if (!cb.drawObject(scene, object))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/FacePrioritySorter.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/FacePrioritySorter.java
new file mode 100644
index 00000000000..a82ca655a05
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/FacePrioritySorter.java
@@ -0,0 +1,479 @@
+/*
+ * 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.IntBuffer;
+import java.util.Arrays;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.Client;
+import net.runelite.api.Model;
+import net.runelite.api.Perspective;
+import net.runelite.api.Projection;
+
+@Singleton
+@RequiredArgsConstructor(onConstructor = @__(@Inject))
+class FacePrioritySorter
+{
+ static final int[] distances;
+ static final char[] distanceFaceCount;
+ static final char[][] distanceToFaces;
+
+ private static final float[] modelCanvasX;
+ private static final float[] modelCanvasY;
+
+ private static final float[] modelLocalX;
+ private static final float[] modelLocalY;
+ private static final float[] modelLocalZ;
+
+ static final int[] numOfPriority;
+ private static final int[] eq10;
+ private static final int[] eq11;
+ private static final int[] lt10;
+ static final int[][] orderedFaces;
+
+ private static final int MAX_VERTEX_COUNT = SceneUploader.MAX_VERTEX_COUNT;
+ private static final int MAX_DIAMETER = 6000;
+ private static final int ZSORT_GROUP_SIZE = 1024; // was 512
+ private static final int MAX_FACES_PER_PRIORITY = 4000; // was 2500
+
+ static
+ {
+ distances = new int[MAX_VERTEX_COUNT];
+ distanceFaceCount = new char[MAX_DIAMETER];
+ distanceToFaces = new char[MAX_DIAMETER][ZSORT_GROUP_SIZE];
+
+ modelCanvasX = new float[MAX_VERTEX_COUNT];
+ modelCanvasY = new float[MAX_VERTEX_COUNT];
+
+ modelLocalX = SceneUploader.modelLocalX;
+ modelLocalY = SceneUploader.modelLocalY;
+ modelLocalZ = SceneUploader.modelLocalZ;
+
+ numOfPriority = new int[12];
+ eq10 = new int[MAX_FACES_PER_PRIORITY];
+ eq11 = new int[MAX_FACES_PER_PRIORITY];
+ lt10 = new int[12];
+ orderedFaces = new int[12][MAX_FACES_PER_PRIORITY];
+ }
+
+ private final Client client;
+
+ int uploadSortedModel(Projection proj, Model model, int orientation, int x, int y, int z, IntBuffer opaqueBuffer, IntBuffer alphaBuffer)
+ {
+ final int vertexCount = model.getVerticesCount();
+ final float[] verticesX = model.getVerticesX();
+ final float[] verticesY = model.getVerticesY();
+ final float[] verticesZ = model.getVerticesZ();
+
+ final int faceCount = model.getFaceCount();
+ final int[] indices1 = model.getFaceIndices1();
+ final int[] indices2 = model.getFaceIndices2();
+ final int[] indices3 = model.getFaceIndices3();
+
+ final int[] faceColors3 = model.getFaceColors3();
+ final byte[] faceRenderPriorities = model.getFaceRenderPriorities();
+
+ final int centerX = client.getCenterX();
+ final int centerY = client.getCenterY();
+ final int zoom = client.get3dZoom();
+
+ float orientSine = 0;
+ float orientCosine = 0;
+ if (orientation != 0)
+ {
+ orientSine = Perspective.SINE[orientation] / 65536f;
+ orientCosine = Perspective.COSINE[orientation] / 65536f;
+ }
+
+ float[] p = proj.project(x, y, z);
+ int zero = (int) p[2];
+
+ for (int v = 0; v < vertexCount; ++v)
+ {
+ float vertexX = verticesX[v];
+ float vertexY = verticesY[v];
+ float vertexZ = verticesZ[v];
+
+ if (orientation != 0)
+ {
+ float x0 = vertexX;
+ vertexX = vertexZ * orientSine + x0 * orientCosine;
+ vertexZ = vertexZ * orientCosine - x0 * orientSine;
+ }
+
+ // move to local position
+ vertexX += x;
+ vertexY += y;
+ vertexZ += z;
+
+ modelLocalX[v] = vertexX;
+ modelLocalY[v] = vertexY;
+ modelLocalZ[v] = vertexZ;
+
+ p = proj.project(vertexX, vertexY, vertexZ);
+ if (p[2] < 50)
+ {
+ return 0;
+ }
+
+ modelCanvasX[v] = centerX + p[0] * zoom / p[2];
+ modelCanvasY[v] = centerY + p[1] * zoom / p[2];
+ distances[v] = (int) p[2] - zero;
+ }
+
+ final int diameter = model.getDiameter();
+ final int radius = model.getRadius();
+ if (diameter >= 6000)
+ {
+ return 0;
+ }
+
+ Arrays.fill(distanceFaceCount, 0, diameter, (char) 0);
+
+ for (char i = 0; i < faceCount; ++i)
+ {
+ if (faceColors3[i] != -2)
+ {
+ final int v1 = indices1[i];
+ final int v2 = indices2[i];
+ final int v3 = indices3[i];
+
+ final float
+ aX = modelCanvasX[v1],
+ aY = modelCanvasY[v1],
+ bX = modelCanvasX[v2],
+ bY = modelCanvasY[v2],
+ cX = modelCanvasX[v3],
+ cY = modelCanvasY[v3];
+
+ if ((aX - bX) * (cY - bY) - (cX - bX) * (aY - bY) > 0)
+ {
+ int distance = radius + (distances[v1] + distances[v2] + distances[v3]) / 3;
+ assert distance >= 0 && distance < diameter;
+ distanceToFaces[distance][distanceFaceCount[distance]++] = i;
+ }
+ }
+ }
+
+ int len = 0;
+ if (faceRenderPriorities == null)
+ {
+ for (int i = diameter - 1; i >= 0; --i)
+ {
+ final int cnt = distanceFaceCount[i];
+ if (cnt > 0)
+ {
+ final char[] faces = distanceToFaces[i];
+
+ for (int faceIdx = 0; faceIdx < cnt; ++faceIdx)
+ {
+ final int face = faces[faceIdx];
+ len += pushFace(model, face, opaqueBuffer, alphaBuffer);
+ }
+ }
+ }
+ }
+ else
+ {
+ Arrays.fill(numOfPriority, 0);
+ Arrays.fill(lt10, 0);
+
+ for (int i = diameter - 1; i >= 0; --i)
+ {
+ final int cnt = distanceFaceCount[i];
+ if (cnt > 0)
+ {
+ final char[] faces = distanceToFaces[i];
+
+ for (int faceIdx = 0; faceIdx < cnt; ++faceIdx)
+ {
+ final int face = faces[faceIdx];
+ final byte pri = faceRenderPriorities[face];
+ final int distIdx = numOfPriority[pri]++;
+
+ orderedFaces[pri][distIdx] = face;
+ if (pri < 10)
+ {
+ lt10[pri] += i;
+ }
+ else if (pri == 10)
+ {
+ eq10[distIdx] = i;
+ }
+ else
+ {
+ eq11[distIdx] = i;
+ }
+ }
+ }
+ }
+
+ int avg12 = 0;
+ if (numOfPriority[1] > 0 || numOfPriority[2] > 0)
+ {
+ avg12 = (lt10[1] + lt10[2]) / (numOfPriority[1] + numOfPriority[2]);
+ }
+
+ int avg34 = 0;
+ if (numOfPriority[3] > 0 || numOfPriority[4] > 0)
+ {
+ avg34 = (lt10[3] + lt10[4]) / (numOfPriority[3] + numOfPriority[4]);
+ }
+
+ int avg68 = 0;
+ if (numOfPriority[6] > 0 || numOfPriority[8] > 0)
+ {
+ avg68 = (lt10[8] + lt10[6]) / (numOfPriority[8] + numOfPriority[6]);
+ }
+
+ int drawnFaces = 0;
+ int numDynFaces = numOfPriority[10];
+ int[] dynFaces = orderedFaces[10];
+ int[] dynFaceDistances = eq10;
+ if (drawnFaces == numDynFaces)
+ {
+ drawnFaces = 0;
+ numDynFaces = numOfPriority[11];
+ dynFaces = orderedFaces[11];
+ dynFaceDistances = eq11;
+ }
+
+ int currFaceDistance;
+ if (drawnFaces < numDynFaces)
+ {
+ currFaceDistance = dynFaceDistances[drawnFaces];
+ }
+ else
+ {
+ currFaceDistance = -1000;
+ }
+
+ for (int pri = 0; pri < 10; ++pri)
+ {
+ while (pri == 0 && currFaceDistance > avg12)
+ {
+ final int face = dynFaces[drawnFaces++];
+ len += pushFace(model, face, opaqueBuffer, alphaBuffer);
+
+ if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
+ {
+ drawnFaces = 0;
+ numDynFaces = numOfPriority[11];
+ dynFaces = orderedFaces[11];
+ dynFaceDistances = eq11;
+ }
+
+ if (drawnFaces < numDynFaces)
+ {
+ currFaceDistance = dynFaceDistances[drawnFaces];
+ }
+ else
+ {
+ currFaceDistance = -1000;
+ }
+ }
+
+ while (pri == 3 && currFaceDistance > avg34)
+ {
+ final int face = dynFaces[drawnFaces++];
+ len += pushFace(model, face, opaqueBuffer, alphaBuffer);
+
+ if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
+ {
+ drawnFaces = 0;
+ numDynFaces = numOfPriority[11];
+ dynFaces = orderedFaces[11];
+ dynFaceDistances = eq11;
+ }
+
+ if (drawnFaces < numDynFaces)
+ {
+ currFaceDistance = dynFaceDistances[drawnFaces];
+ }
+ else
+ {
+ currFaceDistance = -1000;
+ }
+ }
+
+ while (pri == 5 && currFaceDistance > avg68)
+ {
+ final int face = dynFaces[drawnFaces++];
+ len += pushFace(model, face, opaqueBuffer, alphaBuffer);
+
+ if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
+ {
+ drawnFaces = 0;
+ numDynFaces = numOfPriority[11];
+ dynFaces = orderedFaces[11];
+ dynFaceDistances = eq11;
+ }
+
+ if (drawnFaces < numDynFaces)
+ {
+ currFaceDistance = dynFaceDistances[drawnFaces];
+ }
+ else
+ {
+ currFaceDistance = -1000;
+ }
+ }
+
+ final int priNum = numOfPriority[pri];
+ final int[] priFaces = orderedFaces[pri];
+
+ for (int faceIdx = 0; faceIdx < priNum; ++faceIdx)
+ {
+ final int face = priFaces[faceIdx];
+ len += pushFace(model, face, opaqueBuffer, alphaBuffer);
+ }
+ }
+
+ while (currFaceDistance != -1000)
+ {
+ final int face = dynFaces[drawnFaces++];
+ len += pushFace(model, face, opaqueBuffer, alphaBuffer);
+
+ if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
+ {
+ drawnFaces = 0;
+ dynFaces = orderedFaces[11];
+ numDynFaces = numOfPriority[11];
+ dynFaceDistances = eq11;
+ }
+
+ if (drawnFaces < numDynFaces)
+ {
+ currFaceDistance = dynFaceDistances[drawnFaces];
+ }
+ else
+ {
+ currFaceDistance = -1000;
+ }
+ }
+ }
+
+ return len;
+ }
+
+ private int pushFace(Model model, int face, IntBuffer opaqueBuffer, IntBuffer alphaBuffer)
+ {
+ final int[] indices1 = model.getFaceIndices1();
+ final int[] indices2 = model.getFaceIndices2();
+ final int[] indices3 = model.getFaceIndices3();
+
+ final int[] faceColors1 = model.getFaceColors1();
+ final int[] faceColors2 = model.getFaceColors2();
+ final int[] faceColors3 = model.getFaceColors3();
+
+ final byte overrideAmount = model.getOverrideAmount();
+ final byte overrideHue = model.getOverrideHue();
+ final byte overrideSat = model.getOverrideSaturation();
+ final byte overrideLum = model.getOverrideLuminance();
+
+ final short[] faceTextures = model.getFaceTextures();
+ final byte[] textureFaces = model.getTextureFaces();
+ final int[] texIndices1 = model.getTexIndices1();
+ final int[] texIndices2 = model.getTexIndices2();
+ final int[] texIndices3 = model.getTexIndices3();
+
+ final byte[] transparencies = model.getFaceTransparencies();
+ final byte[] bias = model.getFaceBias();
+
+ final int triangleA = indices1[face];
+ final int triangleB = indices2[face];
+ final int triangleC = indices3[face];
+
+ int color1 = faceColors1[face];
+ int color2 = faceColors2[face];
+ int color3 = faceColors3[face];
+
+ boolean alpha = (transparencies != null && transparencies[face] != 0);
+
+ if (color3 == -1)
+ {
+ color2 = color3 = color1;
+ }
+
+ // HSL override is not applied to textured faces
+ if (faceTextures == null || faceTextures[face] == -1)
+ {
+ if (overrideAmount > 0)
+ {
+ color1 = SceneUploader.interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount);
+ color2 = SceneUploader.interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount);
+ color3 = SceneUploader.interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount);
+ }
+ }
+
+ float vx1 = modelLocalX[triangleA];
+ float vy1 = modelLocalY[triangleA];
+ float vz1 = modelLocalZ[triangleA];
+
+ float vx2 = modelLocalX[triangleB];
+ float vy2 = modelLocalY[triangleB];
+ float vz2 = modelLocalZ[triangleB];
+
+ float vx3 = modelLocalX[triangleC];
+ float vy3 = modelLocalY[triangleC];
+ float vz3 = modelLocalZ[triangleC];
+
+ int texA, texB, texC;
+
+ if (textureFaces != null && textureFaces[face] != -1)
+ {
+ int tface = textureFaces[face] & 0xff;
+ texA = texIndices1[tface];
+ texB = texIndices2[tface];
+ texC = texIndices3[tface];
+ }
+ else
+ {
+ texA = triangleA;
+ texB = triangleB;
+ texC = triangleC;
+ }
+
+ int alphaBias = 0;
+ alphaBias |= transparencies != null ? (transparencies[face] & 0xff) << 24 : 0;
+ alphaBias |= bias != null ? (bias[face] & 0xff) << 16 : 0;
+ int texture = faceTextures != null ? faceTextures[face] + 1 : 0;
+
+ var vb = alpha ? alphaBuffer : opaqueBuffer;
+
+ SceneUploader.putfff4(vb, vx1, vy1, vz1, alphaBias | color1);
+ SceneUploader.put2222(vb, texture, (int) modelLocalX[texA] - (int) vx1, (int) modelLocalY[texA] - (int) vy1, (int) modelLocalZ[texA] - (int) vz1);
+
+ SceneUploader.putfff4(vb, vx2, vy2, vz2, alphaBias | color2);
+ SceneUploader.put2222(vb, texture, (int) modelLocalX[texB] - (int) vx2, (int) modelLocalY[texB] - (int) vy2, (int) modelLocalZ[texB] - (int) vz2);
+
+ SceneUploader.putfff4(vb, vx3, vy3, vz3, alphaBias | color3);
+ SceneUploader.put2222(vb, texture, (int) modelLocalX[texC] - (int) vx3, (int) modelLocalY[texC] - (int) vy3, (int) modelLocalZ[texC] - (int) vz3);
+
+ return 3;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java
index 3c183bb937b..9193a9aacd8 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java
@@ -29,7 +29,6 @@ class GLBuffer
String name;
int glBufferId = -1;
int size = -1;
- long clBuffer = -1;
GLBuffer(String name)
{
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
index 0d274f8bbed..8f27b4c48af 100644
--- 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
@@ -30,11 +30,17 @@
class GpuFloatBuffer
{
- private FloatBuffer buffer = allocateDirect(65536);
+ private final FloatBuffer buffer;
- void put(float s, float t, float p, float q)
+ GpuFloatBuffer(int size)
{
- buffer.put(s).put(t).put(p).put(q);
+ buffer = allocateDirect(size);
+ }
+
+ GpuFloatBuffer put(float f)
+ {
+ buffer.put(f);
+ return this;
}
void flip()
@@ -47,25 +53,6 @@ void clear()
buffer.clear();
}
- void ensureCapacity(int size)
- {
- int capacity = buffer.capacity();
- final int position = buffer.position();
- if ((capacity - position) < size)
- {
- do
- {
- capacity *= 2;
- }
- while ((capacity - position) < size);
-
- FloatBuffer newB = allocateDirect(capacity);
- buffer.flip();
- newB.put(buffer);
- buffer = newB;
- }
- }
-
FloatBuffer getBuffer()
{
return buffer;
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
index 9f8ab2eae83..0245935f1b9 100644
--- 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
@@ -30,43 +30,34 @@
class GpuIntBuffer
{
- private IntBuffer buffer = allocateDirect(65536);
+ private final IntBuffer buffer;
- void put(float x, float y, float z, int w)
+ GpuIntBuffer(IntBuffer ib)
{
- buffer.put(Float.floatToIntBits(x))
- .put(Float.floatToIntBits(y))
- .put(Float.floatToIntBits(z))
- .put(w);
+ buffer = ib;
}
- void flip()
+ void put22224(int x, int y, int z, int w)
{
- buffer.flip();
+ buffer.put(((y & 0xffff) << 16) | (x & 0xffff));
+ buffer.put(z & 0xffff);
+ buffer.put(w);
}
- void clear()
+ void put2222(int x, int y, int z, int w)
{
- buffer.clear();
+ buffer.put(((y & 0xffff) << 16) | (x & 0xffff));
+ buffer.put(((w & 0xffff) << 16) | (z & 0xffff));
}
- void ensureCapacity(int size)
+ void flip()
{
- int capacity = buffer.capacity();
- final int position = buffer.position();
- if ((capacity - position) < size)
- {
- do
- {
- capacity *= 2;
- }
- while ((capacity - position) < size);
+ buffer.flip();
+ }
- IntBuffer newB = allocateDirect(capacity);
- buffer.flip();
- newB.put(buffer);
- buffer = newB;
- }
+ void clear()
+ {
+ buffer.clear();
}
IntBuffer getBuffer()
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
index fc02c8df0b9..32fd818376f 100644
--- 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
@@ -24,13 +24,16 @@
*/
package net.runelite.client.plugins.gpu;
+import com.google.common.base.Stopwatch;
import com.google.common.primitives.Ints;
import com.google.inject.Provides;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.*;
import net.runelite.api.events.GameStateChanged;
+import net.runelite.api.events.PostClientTick;
import net.runelite.api.hooks.DrawCallbacks;
import net.runelite.client.callback.ClientThread;
+import net.runelite.client.callback.RenderCallbackManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
@@ -44,13 +47,11 @@
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.DrawManager;
-import net.runelite.client.util.OSType;
import net.runelite.rlawt.AWTContext;
-import org.lwjgl.opencl.CL10;
-import org.lwjgl.opencl.CL10GL;
-import org.lwjgl.opencl.CL12;
import org.lwjgl.opengl.GL;
-import org.lwjgl.opengl.GL43C;
+import static org.lwjgl.opengl.GL43C.*;
+import static org.lwjgl.opengl.GL45C.GL_ZERO_TO_ONE;
+import static org.lwjgl.opengl.GL45C.glClipControl;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.opengl.GLUtil;
import org.lwjgl.system.Callback;
@@ -67,7 +68,7 @@
@PluginDescriptor(
name = "GPU",
- description = "Utilizes the GPU",
+ description = "Offloads rendering to GPU",
enabledByDefault = true,
tags = {"fog", "draw distance"},
loadInSafeMode = false
@@ -75,14 +76,12 @@
@Slf4j
public class GpuPlugin extends Plugin implements DrawCallbacks
{
- // This is the maximum number of triangles the compute shaders support
- static final int MAX_TRIANGLE = 6144;
- static final int SMALL_TRIANGLE_COUNT = 512;
- private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE;
static final int MAX_DISTANCE = 184;
static final int MAX_FOG_DEPTH = 100;
static final int SCENE_OFFSET = (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2; // offset for sxy -> msxy
- private static final int GROUND_MIN_Y = 350; // how far below the ground models extend
+ private static final int UNIFORM_BUFFER_SIZE = 5 * Float.BYTES;
+ private static final int NUM_ZONES = Constants.EXTENDED_SCENE_SIZE >> 3;
+ private static final int MAX_WORLDVIEWS = 4096;
@Inject
private Client client;
@@ -90,9 +89,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Inject
private ClientUI clientUI;
- @Inject
- private OpenCLManager openCLManager;
-
@Inject
private ClientThread clientThread;
@@ -103,7 +99,10 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private TextureManager textureManager;
@Inject
- private SceneUploader sceneUploader;
+ private RegionManager regionManager;
+
+ @Inject
+ private FacePrioritySorter facePrioritySorter;
@Inject
private DrawManager drawManager;
@@ -111,55 +110,28 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Inject
private PluginManager pluginManager;
- enum ComputeMode
- {
- NONE,
- OPENGL,
- OPENCL
- }
-
- private ComputeMode computeMode = ComputeMode.NONE;
+ @Inject
+ private RenderCallbackManager renderCallbackManager;
private Canvas canvas;
private AWTContext awtContext;
private Callback debugCallback;
+ private boolean lwjglInitted = false;
private GLCapabilities glCapabilities;
- static final String LINUX_VERSION_HEADER =
- "#version 420\n" +
- "#extension GL_ARB_compute_shader : require\n" +
- "#extension GL_ARB_shader_storage_buffer_object : require\n" +
- "#extension GL_ARB_explicit_attrib_location : require\n";
- static final String WINDOWS_VERSION_HEADER = "#version 430\n";
-
static final Shader PROGRAM = new Shader()
- .add(GL43C.GL_VERTEX_SHADER, "vert.glsl")
- .add(GL43C.GL_GEOMETRY_SHADER, "geom.glsl")
- .add(GL43C.GL_FRAGMENT_SHADER, "frag.glsl");
-
- static final Shader COMPUTE_PROGRAM = new Shader()
- .add(GL43C.GL_COMPUTE_SHADER, "comp.glsl");
-
- static final Shader SMALL_COMPUTE_PROGRAM = new Shader()
- .add(GL43C.GL_COMPUTE_SHADER, "comp.glsl");
-
- static final Shader UNORDERED_COMPUTE_PROGRAM = new Shader()
- .add(GL43C.GL_COMPUTE_SHADER, "comp_unordered.glsl");
+ .add(GL_VERTEX_SHADER, "vert.glsl")
+ .add(GL_GEOMETRY_SHADER, "geom.glsl")
+ .add(GL_FRAGMENT_SHADER, "frag.glsl");
static final Shader UI_PROGRAM = new Shader()
- .add(GL43C.GL_VERTEX_SHADER, "vertui.glsl")
- .add(GL43C.GL_FRAGMENT_SHADER, "fragui.glsl");
+ .add(GL_VERTEX_SHADER, "vertui.glsl")
+ .add(GL_FRAGMENT_SHADER, "fragui.glsl");
- private int glProgram;
- private int glComputeProgram;
- private int glSmallComputeProgram;
- private int glUnorderedComputeProgram;
+ static int glProgram;
private int glUiProgram;
- private int vaoCompute;
- private int vaoTemp;
-
private int interfaceTexture;
private int interfacePbo;
@@ -167,113 +139,122 @@ enum ComputeMode
private int vboUiHandle;
private int fboScene;
+ private boolean sceneFboValid;
private int rboColorBuffer;
-
- private final GLBuffer sceneVertexBuffer = new GLBuffer("scene vertex buffer");
- private final GLBuffer sceneUvBuffer = new GLBuffer("scene tex buffer");
- private final GLBuffer tmpVertexBuffer = new GLBuffer("tmp vertex buffer");
- private final GLBuffer tmpUvBuffer = new GLBuffer("tmp tex buffer");
- private final GLBuffer tmpModelBufferLarge = new GLBuffer("model buffer large");
- private final GLBuffer tmpModelBufferSmall = new GLBuffer("model buffer small");
- private final GLBuffer tmpModelBufferUnordered = new GLBuffer("model buffer unordered");
- private final GLBuffer tmpOutBuffer = new GLBuffer("out vertex buffer");
- private final GLBuffer tmpOutUvBuffer = new GLBuffer("out tex buffer");
+ private int rboDepthBuffer;
private int textureArrayId;
- private int tileHeightTex;
-
- private final GLBuffer uniformBuffer = new GLBuffer("uniform buffer");
- private GpuIntBuffer vertexBuffer;
- private GpuFloatBuffer uvBuffer;
+ private final GLBuffer glUniformBuffer = new GLBuffer("uniform buffer");
- private GpuIntBuffer modelBufferUnordered;
- private GpuIntBuffer modelBufferSmall;
- private GpuIntBuffer modelBuffer;
+ private int lastCanvasWidth;
+ private int lastCanvasHeight;
+ private int lastStretchedCanvasWidth;
+ private int lastStretchedCanvasHeight;
+ private AntiAliasingMode lastAntiAliasingMode;
+ private int lastAnisotropicFilteringLevel = -1;
- private int unorderedModels;
+ private GpuFloatBuffer uniformBuffer;
- /**
- * number of models in small buffer
- */
- private int smallModels;
+ private int cameraX, cameraY, cameraZ;
+ private int cameraYaw, cameraPitch;
+ private int minLevel, level, maxLevel;
+ private Set hideRoofIds;
- /**
- * number of models in large buffer
- */
- private int largeModels;
+ private VAOList vaoO;
+ private VAOList vaoA;
+ private VAOList vaoPO;
- /**
- * offset in the target buffer for model
- */
- private int targetBufferOffset;
+ static class SceneContext
+ {
+ final int sizeX, sizeZ;
+ Zone[][] zones;
- /**
- * offset into the temporary scene vertex buffer
- */
- private int tempOffset;
+ SceneContext(int sizeX, int sizeZ)
+ {
+ this.sizeX = sizeX;
+ this.sizeZ = sizeZ;
+ zones = new Zone[sizeX][sizeZ];
+ for (int x = 0; x < sizeX; ++x)
+ {
+ for (int z = 0; z < sizeZ; ++z)
+ {
+ zones[x][z] = new Zone();
+ }
+ }
+ }
- /**
- * offset into the temporary scene uv buffer
- */
- private int tempUvOffset;
+ void free()
+ {
+ for (int x = 0; x < sizeX; ++x)
+ {
+ for (int z = 0; z < sizeZ; ++z)
+ {
+ zones[x][z].free();
+ }
+ }
+ }
+ }
- private int lastCanvasWidth;
- private int lastCanvasHeight;
- private int lastStretchedCanvasWidth;
- private int lastStretchedCanvasHeight;
- private AntiAliasingMode lastAntiAliasingMode;
- private int lastAnisotropicFilteringLevel = -1;
+ SceneContext context(Scene scene)
+ {
+ int wvid = scene.getWorldViewId();
+ if (wvid == -1)
+ {
+ return root;
+ }
+ return subs[wvid];
+ }
- private double cameraX, cameraY, cameraZ;
- private double cameraYaw, cameraPitch;
+ SceneContext context(WorldView wv)
+ {
+ int wvid = wv.getId();
+ if (wvid == -1)
+ {
+ return root;
+ }
+ return subs[wvid];
+ }
- private int viewportOffsetX;
- private int viewportOffsetY;
- private int viewportWidth;
- private int viewportHeight;
+ private SceneContext root;
+ private SceneContext[] subs;
+ private Zone[][] nextZones;
+ private Map nextRoofChanges;
// Uniforms
- private int uniColorBlindMode;
- private int uniUiColorBlindMode;
private int uniUseFog;
private int uniFogColor;
private int uniFogDepth;
private int uniDrawDistance;
private int uniExpandedMapLoadingChunks;
- private int uniProjectionMatrix;
+ private int uniWorldProj;
+ private static int uniEntityProj;
+ static int uniEntityTint;
private int uniBrightness;
private int uniTex;
- private int uniTexSamplingMode;
private int uniTexSourceDimensions;
private int uniTexTargetDimensions;
private int uniUiAlphaOverlay;
private int uniTextures;
private int uniTextureAnimations;
- private int uniBlockSmall;
- private int uniBlockLarge;
private int uniBlockMain;
- private int uniSmoothBanding;
private int uniTextureLightMode;
private int uniTick;
+ static int uniBase;
- private boolean lwjglInitted = false;
-
- private int sceneId;
- private int nextSceneId;
- private GpuIntBuffer nextSceneVertexBuffer;
- private GpuFloatBuffer nextSceneTexBuffer;
+ private static Projection lastProjection;
@Override
protected void startUp()
{
+ root = new SceneContext(NUM_ZONES, NUM_ZONES);
+ subs = new SceneContext[MAX_WORLDVIEWS];
clientThread.invoke(() ->
{
try
{
- fboScene = rboColorBuffer = -1;
- targetBufferOffset = 0;
- unorderedModels = smallModels = largeModels = 0;
+ fboScene = -1;
+ lastAnisotropicFilteringLevel = -1;
AWTContext.loadNatives();
@@ -294,35 +275,20 @@ protected void startUp()
canvas.setIgnoreRepaint(true);
- computeMode = config.useComputeShaders()
- ? (OSType.getOSType() == OSType.MacOS ? ComputeMode.OPENCL : ComputeMode.OPENGL)
- : ComputeMode.NONE;
-
// lwjgl defaults to lwjgl- + user.name, but this breaks if the username would cause an invalid path
// to be created.
Configuration.SHARED_LIBRARY_EXTRACT_DIRECTORY.set("lwjgl-rl");
glCapabilities = GL.createCapabilities();
- log.info("Using device: {}", GL43C.glGetString(GL43C.GL_RENDERER));
- log.info("Using driver: {}", GL43C.glGetString(GL43C.GL_VERSION));
+ log.info("Using device: {}", glGetString(GL_RENDERER));
+ log.info("Using driver: {}", glGetString(GL_VERSION));
if (!glCapabilities.OpenGL31)
{
throw new RuntimeException("OpenGL 3.1 is required but not available");
}
- if (!glCapabilities.OpenGL43 && computeMode == ComputeMode.OPENGL)
- {
- log.info("disabling compute shaders because OpenGL 4.3 is not available");
- computeMode = ComputeMode.NONE;
- }
-
- if (computeMode == ComputeMode.NONE)
- {
- sceneUploader.initSortingBuffers();
- }
-
lwjglInitted = true;
checkGLErrors();
@@ -331,52 +297,43 @@ protected void startUp()
debugCallback = GLUtil.setupDebugMessageCallback();
if (debugCallback != null)
{
- // GLDebugEvent[ id 0x20071
- // type Warning: generic
- // severity Unknown (0x826b)
- // source GL API
- // msg Buffer detailed info: Buffer object 11 (bound to GL_ARRAY_BUFFER_ARB, and GL_SHADER_STORAGE_BUFFER (4), usage hint is GL_STREAM_DRAW) will use VIDEO memory as the source for buffer object operations.
- GL43C.glDebugMessageControl(GL43C.GL_DEBUG_SOURCE_API, GL43C.GL_DEBUG_TYPE_OTHER,
- GL43C.GL_DONT_CARE, 0x20071, false);
-
- // GLDebugMessageHandler: GLDebugEvent[ id 0x20052
- // type Warning: implementation dependent performance
- // severity Medium: Severe performance/deprecation/other warnings
- // source GL API
- // msg Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering.
- GL43C.glDebugMessageControl(GL43C.GL_DEBUG_SOURCE_API, GL43C.GL_DEBUG_TYPE_PERFORMANCE,
- GL43C.GL_DONT_CARE, 0x20052, false);
+ // [LWJGL] OpenGL debug message
+ // ID: 0x20071
+ // Source: API
+ // Type: OTHER
+ // Severity: NOTIFICATION
+ // Message: Buffer detailed info: Buffer object 2 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STREAM_DRAW) has been mapped WRITE_ONLY in SYSTEM HEAP memory (fast).
+ glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_OTHER,
+ GL_DONT_CARE, 0x20071, false);
+
+ // [LWJGL] OpenGL debug message
+ // ID: 0x20052
+ // Source: API
+ // Type: PERFORMANCE
+ // Severity: MEDIUM
+ // Message: Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering.
+ glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_PERFORMANCE,
+ GL_DONT_CARE, 0x20052, false);
}
}
- vertexBuffer = new GpuIntBuffer();
- uvBuffer = new GpuFloatBuffer();
-
- modelBufferUnordered = new GpuIntBuffer();
- modelBufferSmall = new GpuIntBuffer();
- modelBuffer = new GpuIntBuffer();
-
setupSyncMode();
initBuffers();
initVao();
- try
- {
- initProgram();
- }
- catch (ShaderException ex)
+ initProgram();
+ initInterfaceTexture();
+ if (glCapabilities.OpenGL45)
{
- throw new RuntimeException(ex);
+ glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); // 1 near 0 far
}
- initInterfaceTexture();
- initUniformBuffer();
client.setDrawCallbacks(this);
client.setGpuFlags(DrawCallbacks.GPU
- | (computeMode == ComputeMode.NONE ? 0 : DrawCallbacks.HILLSKEW)
| (config.removeVertexSnapping() ? DrawCallbacks.NO_VERTEX_SNAPPING : 0)
+ | DrawCallbacks.ZBUF
);
- client.setExpandedMapLoading(config.expandedMapLoadingChunks());
+ client.setExpandedMapLoading(config.expandedMapLoadingZones());
// force rebuild of main buffer provider to enable alpha channel
client.resizeCanvas();
@@ -389,9 +346,7 @@ protected void startUp()
if (client.getGameState() == GameState.LOGGED_IN)
{
- Scene scene = client.getScene();
- loadScene(scene);
- swapScene(scene);
+ startupWorldLoad();
}
checkGLErrors();
@@ -419,6 +374,22 @@ protected void startUp()
});
}
+ private void startupWorldLoad()
+ {
+ WorldView root = client.getTopLevelWorldView();
+ Scene scene = root.getScene();
+ loadScene(root, scene);
+ swapScene(scene);
+
+ for (WorldEntity subEntity : root.worldEntities())
+ {
+ WorldView sub = subEntity.getWorldView();
+ log.debug("WorldView loading: {}", sub.getId());
+ loadSubScene(sub, sub.getScene());
+ swapSub(sub.getScene());
+ }
+ }
+
@Override
protected void shutDown()
{
@@ -429,8 +400,6 @@ protected void shutDown()
client.setUnlockedFps(false);
client.setExpandedMapLoading(0);
- sceneUploader.releaseSortingBuffers();
-
if (lwjglInitted)
{
if (textureArrayId != -1)
@@ -439,13 +408,7 @@ protected void shutDown()
textureArrayId = -1;
}
- if (tileHeightTex != 0)
- {
- GL43C.glDeleteTextures(tileHeightTex);
- tileHeightTex = 0;
- }
-
- destroyGlBuffer(uniformBuffer);
+ root.free();
shutdownInterfaceTexture();
shutdownProgram();
@@ -454,9 +417,6 @@ protected void shutDown()
shutdownFbo();
}
- // this must shutdown after the clgl buffers are freed
- openCLManager.cleanup();
-
if (awtContext != null)
{
awtContext.destroy();
@@ -471,15 +431,6 @@ protected void shutDown()
glCapabilities = null;
- vertexBuffer = null;
- uvBuffer = null;
-
- modelBufferSmall = null;
- modelBuffer = null;
- modelBufferUnordered = null;
-
- lastAnisotropicFilteringLevel = -1;
-
// force main buffer provider rebuild to turn off alpha channel
client.resizeCanvas();
});
@@ -507,7 +458,7 @@ else if (configChanged.getKey().equals("expandedMapLoadingChunks"))
{
clientThread.invokeLater(() ->
{
- client.setExpandedMapLoading(config.expandedMapLoadingChunks());
+ client.setExpandedMapLoading(config.expandedMapLoadingZones());
if (client.getGameState() == GameState.LOGGED_IN)
{
client.setGameState(GameState.LOADING);
@@ -518,10 +469,19 @@ else if (configChanged.getKey().equals("removeVertexSnapping"))
{
log.debug("Toggle {}", configChanged.getKey());
client.setGpuFlags(DrawCallbacks.GPU
- | (computeMode == ComputeMode.NONE ? 0 : DrawCallbacks.HILLSKEW)
| (config.removeVertexSnapping() ? DrawCallbacks.NO_VERTEX_SNAPPING : 0)
+ | DrawCallbacks.ZBUF
);
}
+ else if (configChanged.getKey().equals("uiScalingMode") || configChanged.getKey().equals("colorBlindMode"))
+ {
+ clientThread.invokeLater(() ->
+ {
+ log.debug("Recompiling shaders");
+ shutdownProgram();
+ initProgram();
+ });
+ }
}
}
@@ -559,24 +519,19 @@ private void setupSyncMode()
checkGLErrors();
}
- private Template createTemplate(int threadCount, int facesPerThread)
+ private Template createTemplate()
{
- String versionHeader = OSType.getOSType() == OSType.Linux ? LINUX_VERSION_HEADER : WINDOWS_VERSION_HEADER;
Template template = new Template();
template.add(key ->
{
- if ("version_header".equals(key))
- {
- return versionHeader;
- }
- if ("thread_config".equals(key))
+ switch (key)
{
- return "#define THREAD_COUNT " + threadCount + "\n" +
- "#define FACES_PER_THREAD " + facesPerThread + "\n";
- }
- if ("texture_config".equals(key))
- {
- return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n";
+ case "texture_config":
+ return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n";
+ case "sampling_mode":
+ return "#define SAMPLING_MODE " + config.uiScalingMode().ordinal() + "\n";
+ case "colorblind_mode":
+ return "#define COLORBLIND_MODE " + config.colorBlindMode().ordinal() + "\n";
}
return null;
});
@@ -586,228 +541,151 @@ private Template createTemplate(int threadCount, int facesPerThread)
private void initProgram() throws ShaderException
{
- Template template = createTemplate(-1, -1);
+ // macOS core profile has no default VAO, so the shaders won't validate unless a VAO is bound
+ glBindVertexArray(vaoUiHandle);
+
+ Template template = createTemplate();
glProgram = PROGRAM.compile(template);
glUiProgram = UI_PROGRAM.compile(template);
- if (computeMode == ComputeMode.OPENGL)
- {
- glComputeProgram = COMPUTE_PROGRAM.compile(createTemplate(1024, 6));
- glSmallComputeProgram = SMALL_COMPUTE_PROGRAM.compile(createTemplate(512, 1));
- glUnorderedComputeProgram = UNORDERED_COMPUTE_PROGRAM.compile(template);
- }
- else if (computeMode == ComputeMode.OPENCL)
- {
- openCLManager.init(awtContext);
- }
+ glBindVertexArray(0);
initUniforms();
}
private void initUniforms()
{
- uniProjectionMatrix = GL43C.glGetUniformLocation(glProgram, "projectionMatrix");
- uniBrightness = GL43C.glGetUniformLocation(glProgram, "brightness");
- uniSmoothBanding = GL43C.glGetUniformLocation(glProgram, "smoothBanding");
- uniUseFog = GL43C.glGetUniformLocation(glProgram, "useFog");
- uniFogColor = GL43C.glGetUniformLocation(glProgram, "fogColor");
- uniFogDepth = GL43C.glGetUniformLocation(glProgram, "fogDepth");
- uniDrawDistance = GL43C.glGetUniformLocation(glProgram, "drawDistance");
- uniExpandedMapLoadingChunks = GL43C.glGetUniformLocation(glProgram, "expandedMapLoadingChunks");
- uniColorBlindMode = GL43C.glGetUniformLocation(glProgram, "colorBlindMode");
- uniTextureLightMode = GL43C.glGetUniformLocation(glProgram, "textureLightMode");
- uniTick = GL43C.glGetUniformLocation(glProgram, "tick");
- uniBlockMain = GL43C.glGetUniformBlockIndex(glProgram, "uniforms");
- uniTextures = GL43C.glGetUniformLocation(glProgram, "textures");
- uniTextureAnimations = GL43C.glGetUniformLocation(glProgram, "textureAnimations");
-
- uniTex = GL43C.glGetUniformLocation(glUiProgram, "tex");
- uniTexSamplingMode = GL43C.glGetUniformLocation(glUiProgram, "samplingMode");
- uniTexTargetDimensions = GL43C.glGetUniformLocation(glUiProgram, "targetDimensions");
- uniTexSourceDimensions = GL43C.glGetUniformLocation(glUiProgram, "sourceDimensions");
- uniUiColorBlindMode = GL43C.glGetUniformLocation(glUiProgram, "colorBlindMode");
- uniUiAlphaOverlay = GL43C.glGetUniformLocation(glUiProgram, "alphaOverlay");
-
- if (computeMode == ComputeMode.OPENGL)
- {
- uniBlockSmall = GL43C.glGetUniformBlockIndex(glSmallComputeProgram, "uniforms");
- uniBlockLarge = GL43C.glGetUniformBlockIndex(glComputeProgram, "uniforms");
- }
+ uniWorldProj = glGetUniformLocation(glProgram, "worldProj");
+ uniEntityProj = glGetUniformLocation(glProgram, "entityProj");
+ uniEntityTint = glGetUniformLocation(glProgram, "entityTint");
+ uniBrightness = glGetUniformLocation(glProgram, "brightness");
+ uniUseFog = glGetUniformLocation(glProgram, "useFog");
+ uniFogColor = glGetUniformLocation(glProgram, "fogColor");
+ uniFogDepth = glGetUniformLocation(glProgram, "fogDepth");
+ uniDrawDistance = glGetUniformLocation(glProgram, "drawDistance");
+ uniExpandedMapLoadingChunks = glGetUniformLocation(glProgram, "expandedMapLoadingChunks");
+ uniTextureLightMode = glGetUniformLocation(glProgram, "textureLightMode");
+ uniTick = glGetUniformLocation(glProgram, "tick");
+ uniBlockMain = glGetUniformBlockIndex(glProgram, "uniforms");
+ uniTextures = glGetUniformLocation(glProgram, "textures");
+ uniTextureAnimations = glGetUniformLocation(glProgram, "textureAnimations");
+ uniBase = glGetUniformLocation(glProgram, "base");
+
+ uniTex = glGetUniformLocation(glUiProgram, "tex");
+ uniTexTargetDimensions = glGetUniformLocation(glUiProgram, "targetDimensions");
+ uniTexSourceDimensions = glGetUniformLocation(glUiProgram, "sourceDimensions");
+ uniUiAlphaOverlay = glGetUniformLocation(glUiProgram, "alphaOverlay");
}
private void shutdownProgram()
{
- GL43C.glDeleteProgram(glProgram);
- glProgram = -1;
+ glDeleteProgram(glProgram);
+ glProgram = 0;
- GL43C.glDeleteProgram(glComputeProgram);
- glComputeProgram = -1;
-
- GL43C.glDeleteProgram(glSmallComputeProgram);
- glSmallComputeProgram = -1;
-
- GL43C.glDeleteProgram(glUnorderedComputeProgram);
- glUnorderedComputeProgram = -1;
-
- GL43C.glDeleteProgram(glUiProgram);
- glUiProgram = -1;
+ glDeleteProgram(glUiProgram);
+ glUiProgram = 0;
}
private void initVao()
{
- // Create compute VAO
- vaoCompute = GL43C.glGenVertexArrays();
- GL43C.glBindVertexArray(vaoCompute);
-
- GL43C.glEnableVertexAttribArray(0);
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpOutBuffer.glBufferId);
- GL43C.glVertexAttribPointer(0, 3, GL43C.GL_FLOAT, false, 16, 0);
-
- GL43C.glEnableVertexAttribArray(1);
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpOutBuffer.glBufferId);
- GL43C.glVertexAttribIPointer(1, 1, GL43C.GL_INT, 16, 12);
-
- GL43C.glEnableVertexAttribArray(2);
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpOutUvBuffer.glBufferId);
- GL43C.glVertexAttribPointer(2, 4, GL43C.GL_FLOAT, false, 0, 0);
-
- // Create temp VAO
- vaoTemp = GL43C.glGenVertexArrays();
- GL43C.glBindVertexArray(vaoTemp);
-
- GL43C.glEnableVertexAttribArray(0);
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpVertexBuffer.glBufferId);
- GL43C.glVertexAttribPointer(0, 3, GL43C.GL_FLOAT, false, 16, 0);
-
- GL43C.glEnableVertexAttribArray(1);
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpVertexBuffer.glBufferId);
- GL43C.glVertexAttribIPointer(1, 1, GL43C.GL_INT, 16, 12);
-
- GL43C.glEnableVertexAttribArray(2);
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpUvBuffer.glBufferId);
- GL43C.glVertexAttribPointer(2, 4, GL43C.GL_FLOAT, false, 0, 0);
-
// Create UI VAO
- vaoUiHandle = GL43C.glGenVertexArrays();
+ vaoUiHandle = glGenVertexArrays();
// Create UI buffer
- vboUiHandle = GL43C.glGenBuffers();
- GL43C.glBindVertexArray(vaoUiHandle);
+ vboUiHandle = glGenBuffers();
+ 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
+ 1f, 1f, 0f, 1f, 0f, // top right
+ 1f, -1f, 0f, 1f, 1f, // bottom right
+ -1f, -1f, 0f, 0f, 1f, // bottom left
+ -1f, 1f, 0f, 0f, 0f // top left
});
vboUiBuf.rewind();
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, vboUiHandle);
- GL43C.glBufferData(GL43C.GL_ARRAY_BUFFER, vboUiBuf, GL43C.GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, vboUiHandle);
+ glBufferData(GL_ARRAY_BUFFER, vboUiBuf, GL_STATIC_DRAW);
// position attribute
- GL43C.glVertexAttribPointer(0, 3, GL43C.GL_FLOAT, false, 5 * Float.BYTES, 0);
- GL43C.glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * Float.BYTES, 0);
+ glEnableVertexAttribArray(0);
// texture coord attribute
- GL43C.glVertexAttribPointer(1, 2, GL43C.GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES);
- GL43C.glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES);
+ glEnableVertexAttribArray(1);
- // unbind VBO
- GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, 0);
+ // unbind VAO/VBO
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
}
private void shutdownVao()
{
- GL43C.glDeleteVertexArrays(vaoCompute);
- vaoCompute = -1;
-
- GL43C.glDeleteVertexArrays(vaoTemp);
- vaoTemp = -1;
+ glDeleteBuffers(vboUiHandle);
+ vboUiHandle = 0;
- GL43C.glDeleteBuffers(vboUiHandle);
- vboUiHandle = -1;
-
- GL43C.glDeleteVertexArrays(vaoUiHandle);
- vaoUiHandle = -1;
+ glDeleteVertexArrays(vaoUiHandle);
+ vaoUiHandle = 0;
}
private void initBuffers()
{
- initGlBuffer(sceneVertexBuffer);
- initGlBuffer(sceneUvBuffer);
- initGlBuffer(tmpVertexBuffer);
- initGlBuffer(tmpUvBuffer);
- initGlBuffer(tmpModelBufferLarge);
- initGlBuffer(tmpModelBufferSmall);
- initGlBuffer(tmpModelBufferUnordered);
- initGlBuffer(tmpOutBuffer);
- initGlBuffer(tmpOutUvBuffer);
+ uniformBuffer = new GpuFloatBuffer(UNIFORM_BUFFER_SIZE);
+ initGlBuffer(glUniformBuffer);
+ Zone.initBuffer();
+
+ vaoO = new VAOList();
+ vaoA = new VAOList();
+ vaoPO = new VAOList();
}
private void initGlBuffer(GLBuffer glBuffer)
{
- glBuffer.glBufferId = GL43C.glGenBuffers();
+ glBuffer.glBufferId = glGenBuffers();
}
private void shutdownBuffers()
{
- destroyGlBuffer(sceneVertexBuffer);
- destroyGlBuffer(sceneUvBuffer);
-
- destroyGlBuffer(tmpVertexBuffer);
- destroyGlBuffer(tmpUvBuffer);
- destroyGlBuffer(tmpModelBufferLarge);
- destroyGlBuffer(tmpModelBufferSmall);
- destroyGlBuffer(tmpModelBufferUnordered);
- destroyGlBuffer(tmpOutBuffer);
- destroyGlBuffer(tmpOutUvBuffer);
+ destroyGlBuffer(glUniformBuffer);
+ uniformBuffer = null;
+ Zone.freeBuffer();
+
+ vaoO.free();
+ vaoA.free();
+ vaoPO.free();
+ vaoO = vaoA = vaoPO = null;
}
private void destroyGlBuffer(GLBuffer glBuffer)
{
if (glBuffer.glBufferId != -1)
{
- GL43C.glDeleteBuffers(glBuffer.glBufferId);
+ glDeleteBuffers(glBuffer.glBufferId);
glBuffer.glBufferId = -1;
}
glBuffer.size = -1;
-
- if (glBuffer.clBuffer != -1)
- {
- CL12.clReleaseMemObject(glBuffer.clBuffer);
- glBuffer.clBuffer = -1;
- }
}
private void initInterfaceTexture()
{
- interfacePbo = GL43C.glGenBuffers();
-
- interfaceTexture = GL43C.glGenTextures();
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_WRAP_S, GL43C.GL_CLAMP_TO_EDGE);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_WRAP_T, GL43C.GL_CLAMP_TO_EDGE);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_LINEAR);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAG_FILTER, GL43C.GL_LINEAR);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
+ interfacePbo = glGenBuffers();
+
+ interfaceTexture = glGenTextures();
+ glBindTexture(GL_TEXTURE_2D, interfaceTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, 0);
}
private void shutdownInterfaceTexture()
{
- GL43C.glDeleteBuffers(interfacePbo);
- GL43C.glDeleteTextures(interfaceTexture);
+ glDeleteBuffers(interfacePbo);
+ glDeleteTextures(interfaceTexture);
interfaceTexture = -1;
}
- private void initUniformBuffer()
- {
- initGlBuffer(uniformBuffer);
-
- updateBuffer(uniformBuffer, GL43C.GL_UNIFORM_BUFFER, 8 * Integer.BYTES, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
- GL43C.glBindBuffer(GL43C.GL_UNIFORM_BUFFER, 0);
- }
-
private void initFbo(int width, int height, int aaSamples)
{
final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration();
@@ -818,335 +696,128 @@ private void initFbo(int width, int height, int aaSamples)
if (aaSamples > 0)
{
- GL43C.glEnable(GL43C.GL_MULTISAMPLE);
+ glEnable(GL_MULTISAMPLE);
}
else
{
- GL43C.glDisable(GL43C.GL_MULTISAMPLE);
+ glDisable(GL_MULTISAMPLE);
}
// Create and bind the FBO
- fboScene = GL43C.glGenFramebuffers();
- GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, fboScene);
-
- // Create color render buffer
- rboColorBuffer = GL43C.glGenRenderbuffers();
- GL43C.glBindRenderbuffer(GL43C.GL_RENDERBUFFER, rboColorBuffer);
- GL43C.glRenderbufferStorageMultisample(GL43C.GL_RENDERBUFFER, aaSamples, GL43C.GL_RGBA, width, height);
- GL43C.glFramebufferRenderbuffer(GL43C.GL_FRAMEBUFFER, GL43C.GL_COLOR_ATTACHMENT0, GL43C.GL_RENDERBUFFER, rboColorBuffer);
-
- int status = GL43C.glCheckFramebufferStatus(GL43C.GL_FRAMEBUFFER);
- if (status != GL43C.GL_FRAMEBUFFER_COMPLETE)
+ fboScene = glGenFramebuffers();
+ glBindFramebuffer(GL_FRAMEBUFFER, fboScene);
+
+ // Color render buffer
+ rboColorBuffer = glGenRenderbuffers();
+ glBindRenderbuffer(GL_RENDERBUFFER, rboColorBuffer);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, aaSamples, GL_RGBA, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColorBuffer);
+
+ // Depth render buffer
+ rboDepthBuffer = glGenRenderbuffers();
+ glBindRenderbuffer(GL_RENDERBUFFER, rboDepthBuffer);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, aaSamples, GL_DEPTH_COMPONENT32F, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepthBuffer);
+
+ int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
{
throw new RuntimeException("FBO is incomplete. status: " + status);
}
// Reset
- GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, awtContext.getFramebuffer(false));
- GL43C.glBindRenderbuffer(GL43C.GL_RENDERBUFFER, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false));
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
private void shutdownFbo()
{
if (fboScene != -1)
{
- GL43C.glDeleteFramebuffers(fboScene);
+ glDeleteFramebuffers(fboScene);
fboScene = -1;
}
- if (rboColorBuffer != -1)
+ if (rboColorBuffer != 0)
{
- GL43C.glDeleteRenderbuffers(rboColorBuffer);
- rboColorBuffer = -1;
+ glDeleteRenderbuffers(rboColorBuffer);
+ rboColorBuffer = 0;
}
- }
-
- @Override
- public void drawScene(double cameraX, double cameraY, double cameraZ, double cameraPitch, double cameraYaw, int plane)
- {
- this.cameraX = cameraX;
- this.cameraY = cameraY;
- this.cameraZ = cameraZ;
- this.cameraPitch = cameraPitch;
- this.cameraYaw = cameraYaw;
- viewportOffsetX = client.getViewportXOffset();
- viewportOffsetY = client.getViewportYOffset();
- viewportWidth = client.getViewportWidth();
- viewportHeight = client.getViewportHeight();
-
- final Scene scene = client.getScene();
- scene.setDrawDistance(getDrawDistance());
- // Only reset the target buffer offset right before drawing the scene. That way if there are frames
- // after this that don't involve a scene draw, like during LOADING/HOPPING/CONNECTION_LOST, we can
- // still redraw the previous frame's scene to emulate the client behavior of not painting over the
- // viewport buffer.
- targetBufferOffset = 0;
-
- // UBO.
- // We can reuse the vertex buffer since it isn't used yet.
- vertexBuffer.clear();
- vertexBuffer.ensureCapacity(32);
- IntBuffer uniformBuf = vertexBuffer.getBuffer();
- uniformBuf
- .put(Float.floatToIntBits((float) cameraYaw))
- .put(Float.floatToIntBits((float) cameraPitch))
- .put(client.getCenterX())
- .put(client.getCenterY())
- .put(client.getScale())
- .put(Float.floatToIntBits((float) cameraX))
- .put(Float.floatToIntBits((float) cameraY))
- .put(Float.floatToIntBits((float) cameraZ));
- uniformBuf.flip();
-
- updateBuffer(uniformBuffer, GL43C.GL_UNIFORM_BUFFER, uniformBuf, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
- GL43C.glBindBuffer(GL43C.GL_UNIFORM_BUFFER, 0);
-
- GL43C.glBindBufferBase(GL43C.GL_UNIFORM_BUFFER, 0, uniformBuffer.glBufferId);
- uniformBuf.clear();
-
- checkGLErrors();
- }
-
- @Override
- public void postDrawScene()
- {
- if (computeMode == ComputeMode.NONE)
+ if (rboDepthBuffer != 0)
{
- // Upload buffers
- vertexBuffer.flip();
- uvBuffer.flip();
-
- IntBuffer vertexBuffer = this.vertexBuffer.getBuffer();
- FloatBuffer uvBuffer = this.uvBuffer.getBuffer();
-
- updateBuffer(tmpVertexBuffer, GL43C.GL_ARRAY_BUFFER, vertexBuffer, GL43C.GL_DYNAMIC_DRAW, 0L);
- updateBuffer(tmpUvBuffer, GL43C.GL_ARRAY_BUFFER, uvBuffer, GL43C.GL_DYNAMIC_DRAW, 0L);
-
- checkGLErrors();
- return;
- }
-
- // Upload buffers
- vertexBuffer.flip();
- uvBuffer.flip();
- modelBuffer.flip();
- modelBufferSmall.flip();
- modelBufferUnordered.flip();
-
- IntBuffer vertexBuffer = this.vertexBuffer.getBuffer();
- FloatBuffer uvBuffer = this.uvBuffer.getBuffer();
- IntBuffer modelBuffer = this.modelBuffer.getBuffer();
- IntBuffer modelBufferSmall = this.modelBufferSmall.getBuffer();
- IntBuffer modelBufferUnordered = this.modelBufferUnordered.getBuffer();
-
- // temp buffers
- updateBuffer(tmpVertexBuffer, GL43C.GL_ARRAY_BUFFER, vertexBuffer, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
- updateBuffer(tmpUvBuffer, GL43C.GL_ARRAY_BUFFER, uvBuffer, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
-
- // model buffers
- updateBuffer(tmpModelBufferLarge, GL43C.GL_ARRAY_BUFFER, modelBuffer, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
- updateBuffer(tmpModelBufferSmall, GL43C.GL_ARRAY_BUFFER, modelBufferSmall, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
- updateBuffer(tmpModelBufferUnordered, GL43C.GL_ARRAY_BUFFER, modelBufferUnordered, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY);
-
- // Output buffers
- updateBuffer(tmpOutBuffer,
- GL43C.GL_ARRAY_BUFFER,
- targetBufferOffset * 16, // each element is an ivec4, which is 16 bytes
- GL43C.GL_STREAM_DRAW,
- CL12.CL_MEM_WRITE_ONLY);
- updateBuffer(tmpOutUvBuffer,
- GL43C.GL_ARRAY_BUFFER,
- targetBufferOffset * 16, // each element is a vec4, which is 16 bytes
- GL43C.GL_STREAM_DRAW,
- CL12.CL_MEM_WRITE_ONLY);
-
- if (computeMode == ComputeMode.OPENCL)
- {
- // The docs for clEnqueueAcquireGLObjects say all pending GL operations must be completed before calling
- // clEnqueueAcquireGLObjects, and recommends calling glFinish() as the only portable way to do that.
- // However no issues have been observed from not calling it, and so will leave disabled for now.
- // GL43C.glFinish();
-
- openCLManager.compute(
- unorderedModels, smallModels, largeModels,
- sceneVertexBuffer, sceneUvBuffer,
- tmpVertexBuffer, tmpUvBuffer,
- tmpModelBufferUnordered, tmpModelBufferSmall, tmpModelBufferLarge,
- tmpOutBuffer, tmpOutUvBuffer,
- uniformBuffer);
-
- checkGLErrors();
- return;
+ glDeleteRenderbuffers(rboDepthBuffer);
+ rboDepthBuffer = 0;
}
-
- /*
- * Compute is split into three separate programs: 'unordered', 'small', and 'large'
- * to save on GPU resources. Small will sort <= 512 faces, large will do <= 6144.
- */
-
- // Bind UBO to compute programs
- GL43C.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0);
- GL43C.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0);
-
- // unordered
- GL43C.glUseProgram(glUnorderedComputeProgram);
-
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferUnordered.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId);
-
- GL43C.glDispatchCompute(unorderedModels, 1, 1);
-
- // small
- GL43C.glUseProgram(glSmallComputeProgram);
-
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferSmall.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId);
-
- GL43C.glDispatchCompute(smallModels, 1, 1);
-
- // large
- GL43C.glUseProgram(glComputeProgram);
-
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferLarge.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId);
- GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId);
-
- GL43C.glDispatchCompute(largeModels, 1, 1);
-
- checkGLErrors();
}
- @Override
- public void drawScenePaint(Scene scene, SceneTilePaint paint, int plane, int tileX, int tileY)
+ static void updateEntityProjection(Projection projection)
{
- if (computeMode == ComputeMode.NONE)
- {
- targetBufferOffset += sceneUploader.upload(scene, paint,
- plane, tileX, tileY,
- vertexBuffer, uvBuffer,
- tileX << Perspective.LOCAL_COORD_BITS,
- tileY << Perspective.LOCAL_COORD_BITS,
- true
- );
- }
- else if (paint.getBufferLen() > 0)
+ if (lastProjection != projection)
{
- final int localX = tileX << Perspective.LOCAL_COORD_BITS;
- final int localY = 0;
- final int localZ = tileY << Perspective.LOCAL_COORD_BITS;
-
- GpuIntBuffer b = modelBufferUnordered;
- ++unorderedModels;
-
- 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(localX).put(localY).put(localZ);
-
- targetBufferOffset += 2 * 3;
+ float[] p = projection instanceof FloatProjection ? ((FloatProjection) projection).getProjection() : Mat4.identity();
+ glUniformMatrix4fv(uniEntityProj, false, p);
+ lastProjection = projection;
}
}
@Override
- public void drawSceneTileModel(Scene scene, SceneTileModel model, int tileX, int tileY)
+ public void preSceneDraw(Scene scene,
+ float cameraX, float cameraY, float cameraZ, float cameraPitch, float cameraYaw,
+ int minLevel, int level, int maxLevel, Set hideRoofIds)
{
- if (computeMode == ComputeMode.NONE)
+ this.cameraX = (int) cameraX;
+ this.cameraY = (int) cameraY;
+ this.cameraZ = (int) cameraZ;
+ this.cameraYaw = client.getCameraYaw();
+ this.cameraPitch = client.getCameraPitch();
+ this.minLevel = minLevel;
+ this.level = level;
+ this.maxLevel = maxLevel;
+ this.hideRoofIds = hideRoofIds;
+
+ if (scene.getWorldViewId() == WorldView.TOPLEVEL)
{
- targetBufferOffset += sceneUploader.upload(model,
- 0, 0,
- vertexBuffer, uvBuffer,
- true);
+ preSceneDrawToplevel(scene, cameraX, cameraY, cameraZ, cameraPitch, cameraYaw);
}
- else if (model.getBufferLen() > 0)
+ else
{
- final int localX = tileX << Perspective.LOCAL_COORD_BITS;
- final int localY = 0;
- final int localZ = tileY << Perspective.LOCAL_COORD_BITS;
-
- GpuIntBuffer b = modelBufferUnordered;
- ++unorderedModels;
-
- 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(localX).put(localY).put(localZ);
-
- targetBufferOffset += model.getBufferLen();
+ Scene toplevel = client.getScene();
+ vaoO.addRange(null, toplevel);
+ vaoPO.addRange(null, toplevel);
+ glUniform4i(uniEntityTint, scene.getOverrideHue(), scene.getOverrideSaturation(), scene.getOverrideLuminance(), scene.getOverrideAmount());
}
}
- private void prepareInterfaceTexture(int canvasWidth, int canvasHeight)
+ private void preSceneDrawToplevel(Scene scene,
+ float cameraX, float cameraY, float cameraZ, float cameraPitch, float cameraYaw)
{
- if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight)
- {
- lastCanvasWidth = canvasWidth;
- lastCanvasHeight = canvasHeight;
-
- GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, interfacePbo);
- GL43C.glBufferData(GL43C.GL_PIXEL_UNPACK_BUFFER, canvasWidth * canvasHeight * 4L, GL43C.GL_STREAM_DRAW);
- GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, 0);
+ scene.setDrawDistance(getDrawDistance());
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture);
- GL43C.glTexImage2D(GL43C.GL_TEXTURE_2D, 0, GL43C.GL_RGBA, canvasWidth, canvasHeight, 0, GL43C.GL_BGRA, GL43C.GL_UNSIGNED_BYTE, 0);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
- }
+ // UBO
+ uniformBuffer.clear();
+ uniformBuffer
+ .put(cameraYaw)
+ .put(cameraPitch)
+ .put(cameraX)
+ .put(cameraY)
+ .put(cameraZ);
+ uniformBuffer.flip();
- final BufferProvider bufferProvider = client.getBufferProvider();
- final int[] pixels = bufferProvider.getPixels();
- final int width = bufferProvider.getWidth();
- final int height = bufferProvider.getHeight();
+ glBindBuffer(GL_UNIFORM_BUFFER, glUniformBuffer.glBufferId);
+ glBufferData(GL_UNIFORM_BUFFER, uniformBuffer.getBuffer(), GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ uniformBuffer.clear();
- GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, interfacePbo);
- ByteBuffer interfaceBuf = GL43C.glMapBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, GL43C.GL_WRITE_ONLY);
- if (interfaceBuf != null)
- {
- interfaceBuf
- .asIntBuffer()
- .put(pixels, 0, width * height);
- GL43C.glUnmapBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER);
- }
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture);
- GL43C.glTexSubImage2D(GL43C.GL_TEXTURE_2D, 0, 0, 0, width, height, GL43C.GL_BGRA, GL43C.GL_UNSIGNED_INT_8_8_8_8_REV, 0);
- GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, 0);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
- }
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, glUniformBuffer.glBufferId);
- @Override
- public void draw(int overlayColor)
- {
- final GameState gameState = client.getGameState();
- if (gameState == GameState.STARTING)
- {
- return;
- }
+ checkGLErrors();
final int canvasHeight = client.getCanvasHeight();
final int canvasWidth = client.getCanvasWidth();
- prepareInterfaceTexture(canvasWidth, canvasHeight);
+ final int viewportHeight = client.getViewportHeight();
+ final int viewportWidth = client.getViewportWidth();
// Setup FBO and anti-aliasing
{
@@ -1164,9 +835,9 @@ public void draw(int overlayColor)
shutdownFbo();
// Bind default FBO to check whether anti-aliasing is forced
- GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, awtContext.getFramebuffer(false));
- final int forcedAASamples = GL43C.glGetInteger(GL43C.GL_SAMPLES);
- final int maxSamples = GL43C.glGetInteger(GL43C.GL_MAX_SAMPLES);
+ glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false));
+ final int forcedAASamples = glGetInteger(GL_SAMPLES);
+ final int maxSamples = glGetInteger(GL_MAX_SAMPLES);
final int samples = forcedAASamples != 0 ? forcedAASamples :
Math.min(antiAliasingMode.getSamples(), maxSamples);
@@ -1179,171 +850,512 @@ public void draw(int overlayColor)
lastAntiAliasingMode = antiAliasingMode;
}
- GL43C.glBindFramebuffer(GL43C.GL_DRAW_FRAMEBUFFER, fboScene);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboScene);
}
// Clear scene
int sky = client.getSkyboxColor();
- GL43C.glClearColor((sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f);
- GL43C.glClear(GL43C.GL_COLOR_BUFFER_BIT);
+ glClearColor((sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f);
+ glClearDepthf(0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // Setup anisotropic filtering
+ final int anisotropicFilteringLevel = config.anisotropicFilteringLevel();
- // Draw 3d scene
- if (gameState.getState() >= GameState.LOADING.getState())
+ if (textureArrayId != -1 && lastAnisotropicFilteringLevel != anisotropicFilteringLevel)
{
- final TextureProvider textureProvider = client.getTextureProvider();
- 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);
- if (textureArrayId > -1)
- {
- // if texture upload is successful, compute and set texture animations
- float[] texAnims = textureManager.computeTextureAnimations(textureProvider);
- GL43C.glUseProgram(glProgram);
- GL43C.glUniform2fv(uniTextureAnimations, texAnims);
- GL43C.glUseProgram(0);
- }
- }
+ textureManager.setAnisotropicFilteringLevel(textureArrayId, anisotropicFilteringLevel);
+ lastAnisotropicFilteringLevel = anisotropicFilteringLevel;
+ }
- int renderWidthOff = viewportOffsetX;
- int renderHeightOff = viewportOffsetY;
- int renderCanvasHeight = canvasHeight;
- int renderViewportHeight = viewportHeight;
- int renderViewportWidth = viewportWidth;
+ // Setup viewport
+ int renderWidthOff = client.getViewportXOffset();
+ int renderHeightOff = client.getViewportYOffset();
+ int renderCanvasHeight = canvasHeight;
+ int renderViewportHeight = viewportHeight;
+ int renderViewportWidth = viewportWidth;
+ if (client.isStretchedEnabled())
+ {
+ Dimension dim = client.getStretchedDimensions();
+ renderCanvasHeight = dim.height;
- // Setup anisotropic filtering
- final int anisotropicFilteringLevel = config.anisotropicFilteringLevel();
+ double scaleFactorY = dim.getHeight() / canvasHeight;
+ double scaleFactorX = dim.getWidth() / canvasWidth;
- if (textureArrayId != -1 && lastAnisotropicFilteringLevel != anisotropicFilteringLevel)
- {
- textureManager.setAnisotropicFilteringLevel(textureArrayId, anisotropicFilteringLevel);
- lastAnisotropicFilteringLevel = anisotropicFilteringLevel;
- }
+ // Pad the viewport a little because having ints for our viewport dimensions can introduce off-by-one errors.
+ final int padding = 1;
- if (client.isStretchedEnabled())
- {
- Dimension dim = client.getStretchedDimensions();
- renderCanvasHeight = dim.height;
+ // Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel).
+ renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2;
+ renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth)) + padding * 2;
- double scaleFactorY = dim.getHeight() / canvasHeight;
- double scaleFactorX = dim.getWidth() / canvasWidth;
+ // Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway.
+ renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding;
+ renderWidthOff = (int) Math.floor(scaleFactorX * (renderWidthOff)) - padding;
+ }
- // Pad the viewport a little because having ints for our viewport dimensions can introduce off-by-one errors.
- final int padding = 1;
+ glDpiAwareViewport(renderWidthOff, renderCanvasHeight - renderViewportHeight - renderHeightOff, renderViewportWidth, renderViewportHeight);
- // Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel).
- renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2;
- renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth)) + padding * 2;
+ glUseProgram(glProgram);
- // Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway.
- renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding;
- renderWidthOff = (int) Math.floor(scaleFactorX * (renderWidthOff)) - padding;
- }
+ // Setup uniforms
+ final int drawDistance = getDrawDistance();
+ final int fogDepth = config.fogDepth();
+ glUniform1i(uniUseFog, fogDepth > 0 ? 1 : 0);
+ glUniform4f(uniFogColor, (sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f);
+ glUniform1i(uniFogDepth, fogDepth);
+ glUniform1i(uniDrawDistance, drawDistance * Perspective.LOCAL_TILE_SIZE);
+ glUniform1i(uniExpandedMapLoadingChunks, client.getExpandedMapLoading());
- glDpiAwareViewport(renderWidthOff, renderCanvasHeight - renderViewportHeight - renderHeightOff, renderViewportWidth, renderViewportHeight);
+ // Brightness happens to also be stored in the texture provider, so we use that
+ TextureProvider textureProvider = client.getTextureProvider();
+ glUniform1f(uniBrightness, (float) textureProvider.getBrightness());
+ glUniform1f(uniTextureLightMode, config.brightTextures() ? 1f : 0f);
+ if (client.getGameState() == GameState.LOGGED_IN)
+ {
+ // avoid textures animating during loading
+ glUniform1i(uniTick, client.getGameCycle() & 127);
+ }
- GL43C.glUseProgram(glProgram);
+ // Calculate projection matrix
+ float[] projectionMatrix = Mat4.scale(client.getScale(), client.getScale(), 1);
+ Mat4.mul(projectionMatrix, Mat4.projection(viewportWidth, viewportHeight, 50));
+ Mat4.mul(projectionMatrix, Mat4.rotateX(cameraPitch));
+ Mat4.mul(projectionMatrix, Mat4.rotateY(cameraYaw));
+ Mat4.mul(projectionMatrix, Mat4.translate(-cameraX, -cameraY, -cameraZ));
+ glUniformMatrix4fv(uniWorldProj, false, projectionMatrix);
- final int drawDistance = getDrawDistance();
- final int fogDepth = config.fogDepth();
- GL43C.glUniform1i(uniUseFog, fogDepth > 0 ? 1 : 0);
- GL43C.glUniform4f(uniFogColor, (sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f);
- GL43C.glUniform1i(uniFogDepth, fogDepth);
- GL43C.glUniform1i(uniDrawDistance, drawDistance * Perspective.LOCAL_TILE_SIZE);
- GL43C.glUniform1i(uniExpandedMapLoadingChunks, client.getExpandedMapLoading());
+ projectionMatrix = Mat4.identity();
+ glUniformMatrix4fv(uniEntityProj, false, projectionMatrix);
- // Brightness happens to also be stored in the texture provider, so we use that
- GL43C.glUniform1f(uniBrightness, (float) textureProvider.getBrightness());
- GL43C.glUniform1f(uniSmoothBanding, config.smoothBanding() ? 0f : 1f);
- GL43C.glUniform1i(uniColorBlindMode, config.colorBlindMode().ordinal());
- GL43C.glUniform1f(uniTextureLightMode, config.brightTextures() ? 1f : 0f);
- if (gameState == GameState.LOGGED_IN)
- {
- // avoid textures animating during loading
- GL43C.glUniform1i(uniTick, client.getGameCycle() & 127);
- }
+ glUniform4i(uniEntityTint, 0, 0, 0, 0);
- // Calculate projection matrix
- float[] projectionMatrix = Mat4.scale(client.getScale(), client.getScale(), 1);
- Mat4.mul(projectionMatrix, Mat4.projection(viewportWidth, viewportHeight, 50));
- Mat4.mul(projectionMatrix, Mat4.rotateX((float) cameraPitch));
- Mat4.mul(projectionMatrix, Mat4.rotateY((float) cameraYaw));
- Mat4.mul(projectionMatrix, Mat4.translate((float) -cameraX, (float) -cameraY, (float) -cameraZ));
- GL43C.glUniformMatrix4fv(uniProjectionMatrix, false, projectionMatrix);
+ // Bind uniforms
+ glUniformBlockBinding(glProgram, uniBlockMain, 0);
+ glUniform1i(uniTextures, 1); // texture sampler array is bound to texture1
- // Bind uniforms
- GL43C.glUniformBlockBinding(glProgram, uniBlockMain, 0);
- GL43C.glUniform1i(uniTextures, 1); // texture sampler array is bound to texture1
+ // Enable face culling
+ glEnable(GL_CULL_FACE);
- // 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.
- GL43C.glEnable(GL43C.GL_CULL_FACE);
+ // Enable blending
+ glEnable(GL_BLEND);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
- // Enable blending for alpha
- GL43C.glEnable(GL43C.GL_BLEND);
- GL43C.glBlendFuncSeparate(GL43C.GL_SRC_ALPHA, GL43C.GL_ONE_MINUS_SRC_ALPHA, GL43C.GL_ONE, GL43C.GL_ONE);
+ // Enable depth testing
+ glDepthFunc(GL_GREATER);
+ glEnable(GL_DEPTH_TEST);
- // Draw buffers
- if (computeMode != ComputeMode.NONE)
- {
- if (computeMode == ComputeMode.OPENGL)
- {
- // Before reading the SSBOs written to from postDrawScene() we must insert a barrier
- GL43C.glMemoryBarrier(GL43C.GL_SHADER_STORAGE_BARRIER_BIT);
- }
- else
- {
- // Wait for the command queue to finish, so that we know the compute is done
- openCLManager.finish();
- }
+ checkGLErrors();
+ }
- // Draw using the output buffer of the compute
- GL43C.glBindVertexArray(vaoCompute);
- }
- else
- {
- // Only use the temporary buffers, which will contain the full scene
- GL43C.glBindVertexArray(vaoTemp);
- }
+ @Override
+ public void postSceneDraw(Scene scene)
+ {
+ if (scene.getWorldViewId() == WorldView.TOPLEVEL)
+ {
+ postDrawToplevel();
+ }
+ else
+ {
+ glUniform4i(uniEntityTint, 0, 0, 0, 0);
+ }
+ }
- GL43C.glDrawArrays(GL43C.GL_TRIANGLES, 0, targetBufferOffset);
+ private void postDrawToplevel()
+ {
+ glDisable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
- GL43C.glDisable(GL43C.GL_BLEND);
- GL43C.glDisable(GL43C.GL_CULL_FACE);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, awtContext.getFramebuffer(false));
+ sceneFboValid = true;
+ }
- GL43C.glUseProgram(0);
- }
+ private void blitSceneFbo()
+ {
+ int width = lastStretchedCanvasWidth;
+ int height = lastStretchedCanvasHeight;
- // Blit FBO
- {
- int width = lastStretchedCanvasWidth;
- int height = lastStretchedCanvasHeight;
+ final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration();
+ final AffineTransform transform = graphicsConfiguration.getDefaultTransform();
- final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration();
- final AffineTransform transform = graphicsConfiguration.getDefaultTransform();
+ width = getScaledValue(transform.getScaleX(), width);
+ height = getScaledValue(transform.getScaleY(), height);
- width = getScaledValue(transform.getScaleX(), width);
- height = getScaledValue(transform.getScaleY(), height);
+ int defaultFbo = awtContext.getFramebuffer(false);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fboScene);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFbo);
+ glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
- GL43C.glBindFramebuffer(GL43C.GL_READ_FRAMEBUFFER, fboScene);
- GL43C.glBindFramebuffer(GL43C.GL_DRAW_FRAMEBUFFER, awtContext.getFramebuffer(false));
- GL43C.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
- GL43C.GL_COLOR_BUFFER_BIT, GL43C.GL_NEAREST);
+ // Reset
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFbo);
- // Reset
- GL43C.glBindFramebuffer(GL43C.GL_READ_FRAMEBUFFER, awtContext.getFramebuffer(false));
- }
+ checkGLErrors();
+ }
- vertexBuffer.clear();
- uvBuffer.clear();
- modelBuffer.clear();
- modelBufferSmall.clear();
- modelBufferUnordered.clear();
+ @Override
+ public void drawZoneOpaque(Projection entityProjection, Scene scene, int zx, int zz)
+ {
+ updateEntityProjection(entityProjection);
- smallModels = largeModels = unorderedModels = 0;
- tempOffset = 0;
- tempUvOffset = 0;
+ SceneContext ctx = context(scene);
+ if (ctx == null)
+ {
+ return;
+ }
+
+ Zone z = ctx.zones[zx][zz];
+ if (!z.initialized)
+ {
+ return;
+ }
+
+ int offset = scene.getWorldViewId() == -1 ? (SCENE_OFFSET >> 3) : 0;
+ z.renderOpaque(zx - offset, zz - offset, minLevel, level, maxLevel, hideRoofIds);
+
+ checkGLErrors();
+ }
+
+ @Override
+ public void drawZoneAlpha(Projection entityProjection, Scene scene, int level, int zx, int zz)
+ {
+ updateEntityProjection(entityProjection);
+
+ SceneContext ctx = context(scene);
+ if (ctx == null)
+ {
+ return;
+ }
+
+ // this is a noop after the first zone
+ vaoA.unmap();
+
+ Zone z = ctx.zones[zx][zz];
+ if (!z.initialized)
+ {
+ return;
+ }
+
+ int offset = scene.getWorldViewId() == -1 ? (SCENE_OFFSET >> 3) : 0;
+ if (level == 0)
+ {
+ z.alphaSort(zx - offset, zz - offset, cameraX, cameraY, cameraZ);
+ z.multizoneLocs(scene, zx - offset, zz - offset, cameraX, cameraZ, ctx.zones);
+ }
+
+ glDepthMask(false);
+ z.renderAlpha(zx - offset, zz - offset, cameraYaw, cameraPitch, minLevel, this.level, maxLevel, level, hideRoofIds);
+ glDepthMask(true);
+
+ checkGLErrors();
+ }
+
+ @Override
+ public void drawPass(Projection projection, Scene scene, int pass)
+ {
+ SceneContext ctx = context(scene);
+ if (ctx == null)
+ {
+ return;
+ }
+
+ updateEntityProjection(projection);
+
+ if (pass == DrawCallbacks.PASS_OPAQUE)
+ {
+ vaoO.addRange(projection, scene);
+ vaoPO.addRange(projection, scene);
+
+ if (scene.getWorldViewId() == -1)
+ {
+ glProgramUniform3i(glProgram, uniBase, 0, 0, 0);
+
+ var vaos = vaoO.unmap();
+ for (VAO vao : vaos)
+ {
+ vao.draw();
+ vao.reset();
+ }
+
+ vaos = vaoPO.unmap();
+ glDepthMask(false);
+ for (VAO vao : vaos)
+ {
+ vao.draw();
+ }
+ glDepthMask(true);
+
+ glColorMask(false, false, false, false);
+ for (VAO vao : vaos)
+ {
+ vao.draw();
+ vao.reset();
+ }
+ glColorMask(true, true, true, true);
+ }
+ }
+ else if (pass == DrawCallbacks.PASS_ALPHA)
+ {
+ for (int x = 0; x < ctx.sizeX; ++x)
+ {
+ for (int z = 0; z < ctx.sizeZ; ++z)
+ {
+ Zone zone = ctx.zones[x][z];
+ zone.removeTemp();
+ }
+ }
+ }
+
+ checkGLErrors();
+ }
+
+ @Override
+ public void drawDynamic(Projection worldProjection, Scene scene, TileObject tileObject, Renderable r, Model m, int orient, int x, int y, int z)
+ {
+ SceneContext ctx = context(scene);
+ if (ctx == null)
+ {
+ return;
+ }
+
+ if (!renderCallbackManager.drawObject(scene, tileObject))
+ {
+ return;
+ }
+
+ int size = m.getFaceCount() * 3 * VAO.VERT_SIZE;
+ if (m.getFaceTransparencies() == null)
+ {
+ VAO o = vaoO.get(size);
+ SceneUploader.uploadTempModel(m, orient, x, y, z, o.vbo.vb);
+ }
+ else
+ {
+ m.calculateBoundsCylinder();
+ VAO o = vaoO.get(size), a = vaoA.get(size);
+ int start = a.vbo.vb.position();
+ facePrioritySorter.uploadSortedModel(worldProjection, m, orient, x, y, z, o.vbo.vb, a.vbo.vb);
+ int end = a.vbo.vb.position();
+
+ if (end > start)
+ {
+ int offset = scene.getWorldViewId() == -1 ? SCENE_OFFSET : 0;
+ int zx = (x >> 10) + (offset >> 3);
+ int zz = (z >> 10) + (offset >> 3);
+ Zone zone = ctx.zones[zx][zz];
+
+ // level is checked prior to this callback being run, in order to cull clickboxes, but
+ // tileObject.getPlane()>maxLevel if visbelow is set - lower the object to the max level
+ int plane = Math.min(maxLevel, tileObject.getPlane());
+ // renderable modelheight is typically not set here because DynamicObject doesn't compute it on the returned model
+ zone.addTempAlphaModel(a.vao, start, end, plane, x & 1023, y, z & 1023);
+ }
+ }
+ }
+
+ @Override
+ public void drawTemp(Projection worldProjection, Scene scene, GameObject gameObject, Model m)
+ {
+ SceneContext ctx = context(scene);
+ if (ctx == null)
+ {
+ return;
+ }
+
+ int size = m.getFaceCount() * 3 * VAO.VERT_SIZE;
+ if (gameObject.getRenderable() instanceof Player || m.getFaceTransparencies() != null)
+ {
+ // opaque player faces have their own vao and are drawn in a separate pass from normal opaque faces
+ // because they are not depth tested. transparent player faces don't need their own vao because normal
+ // transparent faces are already not depth tested
+ VAO o = gameObject.getRenderable() instanceof Player ? vaoPO.get(size) : vaoO.get(size);
+ VAO a = vaoA.get(size);
+
+ int start = a.vbo.vb.position();
+ m.calculateBoundsCylinder();
+ try
+ {
+ facePrioritySorter.uploadSortedModel(worldProjection, m, gameObject.getModelOrientation(), gameObject.getX(), gameObject.getZ(), gameObject.getY(), o.vbo.vb, a.vbo.vb);
+ }
+ catch (Exception ex)
+ {
+ log.debug("error drawing entity", ex);
+ }
+ int end = a.vbo.vb.position();
+
+ if (end > start)
+ {
+ int offset = scene.getWorldViewId() == -1 ? (SCENE_OFFSET >> 3) : 0;
+ int zx = (gameObject.getX() >> 10) + offset;
+ int zz = (gameObject.getY() >> 10) + offset;
+ Zone zone = ctx.zones[zx][zz];
+ zone.addTempAlphaModel(a.vao, start, end, gameObject.getPlane(), gameObject.getX() & 1023, gameObject.getZ() - gameObject.getRenderable().getModelHeight() /* to render players over locs */, gameObject.getY() & 1023);
+ }
+ }
+ else
+ {
+ VAO o = vaoO.get(size);
+ SceneUploader.uploadTempModel(m, gameObject.getModelOrientation(), gameObject.getX(), gameObject.getZ(), gameObject.getY(), o.vbo.vb);
+ }
+ }
+
+ @Override
+ public void invalidateZone(Scene scene, int zx, int zz)
+ {
+ SceneContext ctx = context(scene);
+ Zone z = ctx.zones[zx][zz];
+ if (!z.invalidate)
+ {
+ z.invalidate = true;
+ log.debug("Zone invalidated: wx={} x={} z={}", scene.getWorldViewId(), zx, zz);
+ }
+ }
+
+ @Subscribe
+ public void onPostClientTick(PostClientTick event)
+ {
+ WorldView wv = client.getTopLevelWorldView();
+ if (wv == null)
+ {
+ return;
+ }
+
+ rebuild(wv);
+ for (WorldEntity we : wv.worldEntities())
+ {
+ wv = we.getWorldView();
+ rebuild(wv);
+ }
+ }
+
+ private void rebuild(WorldView wv)
+ {
+ SceneContext ctx = context(wv);
+ if (ctx == null)
+ {
+ return;
+ }
+
+ for (int x = 0; x < ctx.sizeX; ++x)
+ {
+ for (int z = 0; z < ctx.sizeZ; ++z)
+ {
+ Zone zone = ctx.zones[x][z];
+ if (!zone.invalidate)
+ {
+ continue;
+ }
+
+ assert zone.initialized;
+ zone.free();
+ zone = ctx.zones[x][z] = new Zone();
+
+ Scene scene = wv.getScene();
+ SceneUploader sceneUploader = injector.getInstance(SceneUploader.class);
+ sceneUploader.zoneSize(scene, zone, x, z);
+
+ VBO o = null, a = null;
+ int sz = zone.sizeO * Zone.VERT_SIZE * 3;
+ if (sz > 0)
+ {
+ o = new VBO(sz);
+ o.init(GL_STATIC_DRAW);
+ o.map();
+ }
+
+ sz = zone.sizeA * Zone.VERT_SIZE * 3;
+ if (sz > 0)
+ {
+ a = new VBO(sz);
+ a.init(GL_STATIC_DRAW);
+ a.map();
+ }
+
+ zone.init(o, a);
+
+ sceneUploader.uploadZone(scene, zone, x, z);
+
+ zone.unmap();
+ zone.initialized = true;
+ zone.dirty = true;
+
+ log.debug("Rebuilt zone wv={} x={} z={}", wv.getId(), x, z);
+ }
+ }
+ }
+
+ private void prepareInterfaceTexture(int canvasWidth, int canvasHeight)
+ {
+ if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight)
+ {
+ lastCanvasWidth = canvasWidth;
+ lastCanvasHeight = canvasHeight;
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, interfacePbo);
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, canvasWidth * canvasHeight * 4L, GL_STREAM_DRAW);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ glBindTexture(GL_TEXTURE_2D, interfaceTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, canvasWidth, canvasHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ final BufferProvider bufferProvider = client.getBufferProvider();
+ final int[] pixels = bufferProvider.getPixels();
+ final int width = bufferProvider.getWidth();
+ final int height = bufferProvider.getHeight();
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, interfacePbo);
+ ByteBuffer interfaceBuf = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
+ if (interfaceBuf != null)
+ {
+ interfaceBuf
+ .asIntBuffer()
+ .put(pixels, 0, width * height);
+ glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ }
+ glBindTexture(GL_TEXTURE_2D, interfaceTexture);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ @Override
+ public void draw(int overlayColor)
+ {
+ final GameState gameState = client.getGameState();
+ if (gameState == GameState.STARTING)
+ {
+ return;
+ }
+
+ final TextureProvider textureProvider = client.getTextureProvider();
+ if (textureArrayId == -1 && textureProvider != null)
+ {
+ // 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);
+ if (textureArrayId > -1)
+ {
+ // if texture upload is successful, compute and set texture animations
+ float[] texAnims = textureManager.computeTextureAnimations(textureProvider);
+ glProgramUniform2fv(glProgram, uniTextureAnimations, texAnims);
+ }
+ }
+
+ final int canvasHeight = client.getCanvasHeight();
+ final int canvasWidth = client.getCanvasWidth();
+
+ prepareInterfaceTexture(canvasWidth, canvasHeight);
+
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (sceneFboValid)
+ {
+ blitSceneFbo();
+ }
// Texture on UI
drawUi(overlayColor, canvasHeight, canvasWidth);
@@ -1383,25 +1395,23 @@ public void draw(int overlayColor)
drawManager.processDrawComplete(this::screenshot);
- GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, awtContext.getFramebuffer(false));
+ glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false));
checkGLErrors();
}
private void drawUi(final int overlayColor, final int canvasHeight, final int canvasWidth)
{
- GL43C.glEnable(GL43C.GL_BLEND);
- GL43C.glBlendFunc(GL43C.GL_ONE, GL43C.GL_ONE_MINUS_SRC_ALPHA);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBindTexture(GL_TEXTURE_2D, interfaceTexture);
// Use the texture bound in the first pass
final UIScalingMode uiScalingMode = config.uiScalingMode();
- GL43C.glUseProgram(glUiProgram);
- GL43C.glUniform1i(uniTex, 0);
- GL43C.glUniform1i(uniTexSamplingMode, uiScalingMode.getMode());
- GL43C.glUniform2i(uniTexSourceDimensions, canvasWidth, canvasHeight);
- GL43C.glUniform1i(uniUiColorBlindMode, config.colorBlindMode().ordinal());
- GL43C.glUniform4f(uniUiAlphaOverlay,
+ glUseProgram(glUiProgram);
+ glUniform1i(uniTex, 0);
+ glUniform2i(uniTexSourceDimensions, canvasWidth, canvasHeight);
+ glUniform4f(uniUiAlphaOverlay,
(overlayColor >> 16 & 0xFF) / 255f,
(overlayColor >> 8 & 0xFF) / 255f,
(overlayColor & 0xFF) / 255f,
@@ -1412,32 +1422,32 @@ private void drawUi(final int overlayColor, final int canvasHeight, final int ca
{
Dimension dim = client.getStretchedDimensions();
glDpiAwareViewport(0, 0, dim.width, dim.height);
- GL43C.glUniform2i(uniTexTargetDimensions, dim.width, dim.height);
+ glUniform2i(uniTexTargetDimensions, dim.width, dim.height);
}
else
{
glDpiAwareViewport(0, 0, canvasWidth, canvasHeight);
- GL43C.glUniform2i(uniTexTargetDimensions, canvasWidth, canvasHeight);
+ glUniform2i(uniTexTargetDimensions, canvasWidth, canvasHeight);
}
// Set the sampling function used when stretching the UI.
// This is probably better done with sampler objects instead of texture parameters, but this is easier and likely more portable.
// See https://www.khronos.org/opengl/wiki/Sampler_Object for details.
- // GL_NEAREST makes sampling for bicubic/xBR simpler, so it should be used whenever linear isn't
- final int function = uiScalingMode == UIScalingMode.LINEAR ? GL43C.GL_LINEAR : GL43C.GL_NEAREST;
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MIN_FILTER, function);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAG_FILTER, function);
+ // GL_NEAREST makes sampling for bicubic/xBR simpler, so it should be used whenever linear/hybrid isn't
+ final int function = uiScalingMode == UIScalingMode.LINEAR || uiScalingMode == UIScalingMode.HYBRID ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, function);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, function);
// Texture on UI
- GL43C.glBindVertexArray(vaoUiHandle);
- GL43C.glDrawArrays(GL43C.GL_TRIANGLE_FAN, 0, 4);
+ glBindVertexArray(vaoUiHandle);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// Reset
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
- GL43C.glBindVertexArray(0);
- GL43C.glUseProgram(0);
- GL43C.glBlendFunc(GL43C.GL_SRC_ALPHA, GL43C.GL_ONE_MINUS_SRC_ALPHA);
- GL43C.glDisable(GL43C.GL_BLEND);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindVertexArray(0);
+ glUseProgram(0);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_BLEND);
}
/**
@@ -1465,8 +1475,8 @@ private Image screenshot()
ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4)
.order(ByteOrder.nativeOrder());
- GL43C.glReadBuffer(awtContext.getBufferMode());
- GL43C.glReadPixels(0, 0, width, height, GL43C.GL_RGBA, GL43C.GL_UNSIGNED_BYTE, buffer);
+ glReadBuffer(awtContext.getBufferMode());
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
@@ -1487,21 +1497,17 @@ private Image screenshot()
return image;
}
- @Override
- public void animate(Texture texture, int diff)
- {
- // texture animation happens on gpu
- }
-
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
- if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN)
+ GameState state = gameStateChanged.getGameState();
+ if (state.getState() < GameState.LOADING.getState())
{
- // Avoid drawing the last frame's buffer during LOADING after LOGIN_SCREEN
- targetBufferOffset = 0;
+ // this is to avoid scene fbo blit when going from =loading,
+ // but keep it when doing >loading to loading
+ sceneFboValid = false;
}
- if (gameStateChanged.getGameState() == GameState.STARTING)
+ if (state == GameState.STARTING)
{
if (textureArrayId != -1)
{
@@ -1513,345 +1519,449 @@ public void onGameStateChanged(GameStateChanged gameStateChanged)
}
@Override
- public void loadScene(Scene scene)
+ public void loadScene(WorldView worldView, Scene scene)
{
- if (computeMode == ComputeMode.NONE)
+ if (scene.getWorldViewId() > -1)
{
+ loadSubScene(worldView, scene);
return;
}
- GpuIntBuffer vertexBuffer = new GpuIntBuffer();
- GpuFloatBuffer uvBuffer = new GpuFloatBuffer();
+ assert scene.getWorldViewId() == -1;
+ if (nextZones != null)
+ {
+ throw new RuntimeException("Double zone load!");
+ }
+
+ SceneContext ctx = root;
+ Scene prev = client.getTopLevelWorldView().getScene();
- sceneUploader.upload(scene, vertexBuffer, uvBuffer);
+ regionManager.prepare(scene);
- vertexBuffer.flip();
- uvBuffer.flip();
+ int dx = scene.getBaseX() - prev.getBaseX() >> 3;
+ int dy = scene.getBaseY() - prev.getBaseY() >> 3;
- nextSceneVertexBuffer = vertexBuffer;
- nextSceneTexBuffer = uvBuffer;
- nextSceneId = sceneUploader.sceneId;
- }
+ final int SCENE_ZONES = NUM_ZONES;
- private void uploadTileHeights(Scene scene)
- {
- if (tileHeightTex != 0)
+ // initially mark every zone as needing culled
+ for (int x = 0; x < SCENE_ZONES; ++x)
{
- GL43C.glDeleteTextures(tileHeightTex);
- tileHeightTex = 0;
+ for (int z = 0; z < SCENE_ZONES; ++z)
+ {
+ ctx.zones[x][z].cull = true;
+ }
}
- final int TILEHEIGHT_BUFFER_SIZE = Constants.MAX_Z * Constants.EXTENDED_SCENE_SIZE * Constants.EXTENDED_SCENE_SIZE * Short.BYTES;
- ShortBuffer tileBuffer = ByteBuffer
- .allocateDirect(TILEHEIGHT_BUFFER_SIZE)
- .order(ByteOrder.nativeOrder())
- .asShortBuffer();
-
- int[][][] tileHeights = scene.getTileHeights();
- for (int z = 0; z < Constants.MAX_Z; ++z)
+ // find zones which overlap and copy them
+ Zone[][] newZones = new Zone[SCENE_ZONES][SCENE_ZONES];
+ final GameState gameState = client.getGameState();
+ if (prev.isInstance() == scene.isInstance()
+ && prev.getRoofRemovalMode() == scene.getRoofRemovalMode()
+ && gameState == GameState.LOGGED_IN)
{
- for (int y = 0; y < Constants.EXTENDED_SCENE_SIZE; ++y)
+ int[][][] prevTemplates = prev.getInstanceTemplateChunks();
+ int[][][] curTemplates = scene.getInstanceTemplateChunks();
+
+ for (int x = 0; x < SCENE_ZONES; ++x)
{
- for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x)
+ next:
+ for (int z = 0; z < SCENE_ZONES; ++z)
{
- int h = tileHeights[z][x][y];
- assert (h & 0b111) == 0;
- h >>= 3;
- tileBuffer.put((short) h);
+ int ox = x + dx;
+ int oz = z + dy;
+
+ // Reused the old zone if it is also in the new scene, except for the edges, to work around
+ // tile blending, (edge) shadows, sharelight, etc.
+ if (canReuse(ctx.zones, ox, oz))
+ {
+ if (scene.isInstance())
+ {
+ // Convert from modified chunk coordinates to Jagex chunk coordinates
+ int jx = x - (SCENE_OFFSET / 8);
+ int jz = z - (SCENE_OFFSET / 8);
+ int jox = ox - (SCENE_OFFSET / 8);
+ int joz = oz - (SCENE_OFFSET / 8);
+ // Check Jagex chunk coordinates are within the Jagex scene
+ if (jx >= 0 && jx < Constants.SCENE_SIZE / 8 && jz >= 0 && jz < Constants.SCENE_SIZE / 8)
+ {
+ if (jox >= 0 && jox < Constants.SCENE_SIZE / 8 && joz >= 0 && joz < Constants.SCENE_SIZE / 8)
+ {
+ for (int level = 0; level < 4; ++level)
+ {
+ int prevTemplate = prevTemplates[level][jox][joz];
+ int curTemplate = curTemplates[level][jx][jz];
+ if (prevTemplate != curTemplate)
+ {
+ log.error("Instance template reuse mismatch! prev={} cur={}", prevTemplate, curTemplate);
+ continue next;
+ }
+ }
+ }
+ }
+ }
+
+ Zone old = ctx.zones[ox][oz];
+ assert old.initialized;
+
+ if (old.dirty)
+ {
+ continue;
+ }
+
+ assert old.sizeO > 0 || old.sizeA > 0;
+
+ assert old.cull;
+ old.cull = false;
+
+ newZones[x][z] = old;
+ }
}
}
}
- tileBuffer.flip();
-
- tileHeightTex = GL43C.glGenTextures();
- GL43C.glBindTexture(GL43C.GL_TEXTURE_3D, tileHeightTex);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_MAG_FILTER, GL43C.GL_NEAREST);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_WRAP_S, GL43C.GL_CLAMP_TO_EDGE);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_WRAP_T, GL43C.GL_CLAMP_TO_EDGE);
- GL43C.glTexImage3D(GL43C.GL_TEXTURE_3D, 0, GL43C.GL_R16I,
- Constants.EXTENDED_SCENE_SIZE, Constants.EXTENDED_SCENE_SIZE, Constants.MAX_Z,
- 0, GL43C.GL_RED_INTEGER, GL43C.GL_SHORT, tileBuffer);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_3D, 0);
-
- // bind to texture 2
- GL43C.glActiveTexture(GL43C.GL_TEXTURE2);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_3D, tileHeightTex); // binding = 2 in the shader
- GL43C.glActiveTexture(GL43C.GL_TEXTURE0);
- }
- @Override
- public void swapScene(Scene scene)
- {
- if (computeMode == ComputeMode.NONE)
+ // Fill out any zones that weren't copied
+ for (int x = 0; x < SCENE_ZONES; ++x)
{
- return;
+ for (int z = 0; z < SCENE_ZONES; ++z)
+ {
+ if (newZones[x][z] == null)
+ {
+ newZones[x][z] = new Zone();
+ }
+ }
}
- if (computeMode == ComputeMode.OPENCL)
- {
- openCLManager.uploadTileHeights(scene);
- }
- else
+ // size the zones which require upload
+ SceneUploader sceneUploader = injector.getInstance(SceneUploader.class);
+ Stopwatch sw = Stopwatch.createStarted();
+ int len = 0, lena = 0;
+ int reused = 0, newzones = 0;
+ for (int x = 0; x < NUM_ZONES; ++x)
{
- assert computeMode == ComputeMode.OPENGL;
- uploadTileHeights(scene);
- }
-
- sceneId = nextSceneId;
- updateBuffer(sceneVertexBuffer, GL43C.GL_ARRAY_BUFFER, nextSceneVertexBuffer.getBuffer(), GL43C.GL_STATIC_COPY, CL12.CL_MEM_READ_ONLY);
- updateBuffer(sceneUvBuffer, GL43C.GL_ARRAY_BUFFER, nextSceneTexBuffer.getBuffer(), GL43C.GL_STATIC_COPY, CL12.CL_MEM_READ_ONLY);
-
- nextSceneVertexBuffer = null;
- nextSceneTexBuffer = null;
- nextSceneId = -1;
-
- checkGLErrors();
- }
-
- @Override
- public boolean tileInFrustum(Scene scene, float pitchSin, float pitchCos, float yawSin, float yawCos, int cameraX, int cameraY, int cameraZ, int plane, int msx, int msy)
- {
- int[][][] tileHeights = scene.getTileHeights();
- int x = ((msx - SCENE_OFFSET) << Perspective.LOCAL_COORD_BITS) + 64 - cameraX;
- int z = ((msy - SCENE_OFFSET) << Perspective.LOCAL_COORD_BITS) + 64 - cameraZ;
- int y = Math.max(
- Math.max(tileHeights[plane][msx][msy], tileHeights[plane][msx][msy + 1]),
- Math.max(tileHeights[plane][msx + 1][msy], tileHeights[plane][msx + 1][msy + 1])
- ) + GROUND_MIN_Y - cameraY;
-
- int radius = 96; // ~ 64 * sqrt(2)
-
- int zoom = client.get3dZoom();
- int Rasterizer3D_clipMidX2 = client.getRasterizer3D_clipMidX2();
- int Rasterizer3D_clipNegativeMidX = client.getRasterizer3D_clipNegativeMidX();
- int Rasterizer3D_clipNegativeMidY = client.getRasterizer3D_clipNegativeMidY();
-
- float var11 = yawCos * z - yawSin * x;
- float var12 = pitchSin * y + pitchCos * var11;
- float var13 = pitchCos * radius;
- float depth = var12 + var13;
- if (depth > 50)
- {
- float rx = z * yawSin + yawCos * x;
- float var16 = (rx - radius) * zoom;
- float var17 = (rx + radius) * zoom;
- // left && right
- if (var16 < Rasterizer3D_clipMidX2 * depth && var17 > Rasterizer3D_clipNegativeMidX * depth)
+ for (int z = 0; z < NUM_ZONES; ++z)
{
- float ry = pitchCos * y - var11 * pitchSin;
- float ybottom = pitchSin * radius;
- float var20 = (ry + ybottom) * zoom;
- // top
- if (var20 > Rasterizer3D_clipNegativeMidY * depth)
+ Zone zone = newZones[x][z];
+ if (!zone.initialized)
+ {
+ assert zone.glVao == 0;
+ assert zone.glVaoA == 0;
+ sceneUploader.zoneSize(scene, zone, x, z);
+ len += zone.sizeO;
+ lena += zone.sizeA;
+ newzones++;
+ }
+ else
{
- // we don't test the bottom so we don't have to find the height of all the models on the tile
- return true;
+ reused++;
}
}
}
- return false;
- }
+ log.debug("Scene size time {} reused {} new {} len opaque {} size opaque {}kb len alpha {} size alpha {}kb",
+ sw, reused, newzones,
+ len, (len * Zone.VERT_SIZE * 3) / 1024,
+ lena, (lena * Zone.VERT_SIZE * 3) / 1024);
- /**
- * Check is a model is visible and should be drawn.
- */
- private boolean isVisible(Model model, float pitchSin, float pitchCos, float yawSin, float yawCos, int x, int y, int z)
- {
- final int xzMag = model.getXYZMag();
- final int bottomY = model.getBottomY();
- final int zoom = client.get3dZoom();
- final int modelHeight = model.getModelHeight();
-
- int Rasterizer3D_clipMidX2 = client.getRasterizer3D_clipMidX2(); // width / 2
- int Rasterizer3D_clipNegativeMidX = client.getRasterizer3D_clipNegativeMidX(); // -width / 2
- int Rasterizer3D_clipNegativeMidY = client.getRasterizer3D_clipNegativeMidY(); // -height / 2
- int Rasterizer3D_clipMidY2 = client.getRasterizer3D_clipMidY2(); // height / 2
-
- float var11 = yawCos * z - yawSin * x;
- float var12 = pitchSin * y + pitchCos * var11;
- float var13 = pitchCos * xzMag;
- float depth = var12 + var13;
- if (depth > 50)
- {
- float rx = z * yawSin + yawCos * x;
- float var16 = (rx - xzMag) * zoom;
- if (var16 / depth < Rasterizer3D_clipMidX2)
+ // allocate buffers for zones which require upload
+ CountDownLatch latch = new CountDownLatch(1);
+ clientThread.invoke(() ->
+ {
+ for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE >> 3; ++x)
{
- float var17 = (rx + xzMag) * zoom;
- if (var17 / depth > Rasterizer3D_clipNegativeMidX)
+ for (int z = 0; z < Constants.EXTENDED_SCENE_SIZE >> 3; ++z)
{
- float ry = pitchCos * y - var11 * pitchSin;
- float yheight = pitchSin * xzMag;
- float ybottom = (pitchCos * bottomY) + yheight; // use bottom height instead of y pos for height
- float var20 = (ry + ybottom) * zoom;
- if (var20 / depth > Rasterizer3D_clipNegativeMidY)
+ Zone zone = newZones[x][z];
+
+ if (zone.initialized)
+ {
+ continue;
+ }
+
+ VBO o = null, a = null;
+ int sz = zone.sizeO * Zone.VERT_SIZE * 3;
+ if (sz > 0)
+ {
+ o = new VBO(sz);
+ o.init(GL_STATIC_DRAW);
+ o.map();
+ }
+
+ sz = zone.sizeA * Zone.VERT_SIZE * 3;
+ if (sz > 0)
{
- float ytop = (pitchCos * modelHeight) + yheight;
- float var22 = (ry - ytop) * zoom;
- return var22 / depth < Rasterizer3D_clipMidY2;
+ a = new VBO(sz);
+ a.init(GL_STATIC_DRAW);
+ a.map();
}
+
+ zone.init(o, a);
}
}
+
+ latch.countDown();
+ });
+ try
+ {
+ latch.await();
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
}
- return false;
- }
- /**
- * Draw a renderable in the scene
- */
- @Override
- public void draw(Projection projection, Scene scene, Renderable renderable, int orientation, int x, int y, int z, long hash)
- {
- Model model, offsetModel;
- if (renderable instanceof Model)
+ // upload zones
+ sw = Stopwatch.createStarted();
+ for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE >> 3; ++x)
{
- model = (Model) renderable;
- offsetModel = model.getUnskewedModel();
- if (offsetModel == null)
+ for (int z = 0; z < Constants.EXTENDED_SCENE_SIZE >> 3; ++z)
{
- offsetModel = model;
+ Zone zone = newZones[x][z];
+
+ if (!zone.initialized)
+ {
+ sceneUploader.uploadZone(scene, zone, x, z);
+ }
}
}
- else
+ log.debug("Scene upload time {}", sw);
+
+ // Roof ids aren't consistent between scenes, so build a mapping of old -> new roof ids
+ Map roofChanges;
{
- model = renderable.getModel();
- if (model == null)
+ int[][][] prids = prev.getRoofs();
+ int[][][] nrids = scene.getRoofs();
+ dx <<= 3;
+ dy <<= 3;
+ roofChanges = new HashMap<>();
+
+ sw = Stopwatch.createStarted();
+ for (int level = 0; level < 4; ++level)
{
- return;
+ for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x)
+ {
+ for (int z = 0; z < Constants.EXTENDED_SCENE_SIZE; ++z)
+ {
+ int ox = x + dx;
+ int oz = z + dy;
+
+ // old zone still in scene?
+ if (ox >= 0 && oz >= 0 && ox < Constants.EXTENDED_SCENE_SIZE && oz < Constants.EXTENDED_SCENE_SIZE)
+ {
+ int prid = prids[level][ox][oz];
+ int nrid = nrids[level][x][z];
+ if (prid > 0 && nrid > 0 && prid != nrid)
+ {
+ Integer old = roofChanges.putIfAbsent(prid, nrid);
+ if (old == null)
+ {
+ log.trace("Roof change: {} -> {}", prid, nrid);
+ }
+ else if (old != nrid)
+ {
+ log.debug("Roof change mismatch: {} -> {} vs {}", prid, nrid, old);
+ }
+ }
+ }
+ }
+ }
}
- offsetModel = model;
+ sw.stop();
+
+ log.debug("Roof remapping time {}", sw);
}
- if (computeMode == ComputeMode.NONE)
+ nextZones = newZones;
+ nextRoofChanges = roofChanges;
+ }
+
+ private static boolean canReuse(Zone[][] zones, int zx, int zz)
+ {
+ // For tile blending, sharelight, and shadows to work correctly, the zones surrounding
+ // the zone must be valid.
+ for (int x = zx - 1; x <= zx + 1; ++x)
{
- // Apply height to renderable from the model
- if (model != renderable)
+ if (x < 0 || x >= NUM_ZONES)
{
- renderable.setModelHeight(model.getModelHeight());
+ return false;
}
-
- model.calculateBoundsCylinder();
-
- if (projection instanceof IntProjection)
+ for (int z = zz - 1; z <= zz + 1; ++z)
{
- IntProjection p = (IntProjection) projection;
- if (!isVisible(model, p.getPitchSin(), p.getPitchCos(), p.getYawSin(), p.getYawCos(), x - p.getCameraX(), y - p.getCameraY(), z - p.getCameraZ()))
+ if (z < 0 || z >= NUM_ZONES)
+ {
+ return false;
+ }
+ Zone zone = zones[x][z];
+ if (!zone.initialized)
+ {
+ return false;
+ }
+ if (zone.sizeO == 0 && zone.sizeA == 0)
{
- return;
+ return false;
}
}
+ }
+ return true;
+ }
- client.checkClickbox(projection, model, orientation, x, y, z, hash);
+ private void loadSubScene(WorldView worldView, Scene scene)
+ {
+ int worldViewId = scene.getWorldViewId();
+ assert worldViewId != -1;
- targetBufferOffset += sceneUploader.pushSortedModel(
- projection,
- model, orientation,
- x, y, z,
- vertexBuffer, uvBuffer);
- }
- // Model may be in the scene buffer
- else if (offsetModel.getSceneId() == sceneId)
+ log.debug("Loading world view {}", worldViewId);
+
+ SceneContext ctx0 = subs[worldViewId];
+ if (ctx0 != null)
{
- assert model == renderable;
+ throw new RuntimeException("Reload of an already loaded worldview?");
+ }
- model.calculateBoundsCylinder();
+ final SceneContext ctx = new SceneContext(worldView.getSizeX() >> 3, worldView.getSizeY() >> 3);
+ subs[worldViewId] = ctx;
- if (projection instanceof IntProjection)
+ SceneUploader sceneUploader = injector.getInstance(SceneUploader.class);
+ for (int x = 0; x < ctx.sizeX; ++x)
+ {
+ for (int z = 0; z < ctx.sizeZ; ++z)
{
- IntProjection p = (IntProjection) projection;
- if (!isVisible(model, p.getPitchSin(), p.getPitchCos(), p.getYawSin(), p.getYawCos(), x - p.getCameraX(), y - p.getCameraY(), z - p.getCameraZ()))
- {
- return;
- }
+ Zone zone = ctx.zones[x][z];
+ sceneUploader.zoneSize(scene, zone, x, z);
}
+ }
- client.checkClickbox(projection, model, orientation, x, y, z, hash);
+ // allocate buffers for zones which require upload
+ CountDownLatch latch = new CountDownLatch(1);
+ clientThread.invoke(() ->
+ {
+ for (int x = 0; x < ctx.sizeX; ++x)
+ {
+ for (int z = 0; z < ctx.sizeZ; ++z)
+ {
+ Zone zone = ctx.zones[x][z];
- int tc = Math.min(MAX_TRIANGLE, offsetModel.getFaceCount());
- int uvOffset = offsetModel.getUvBufferOffset();
- int plane = (int) ((hash >> TileObject.HASH_PLANE_SHIFT) & 3);
- boolean hillskew = offsetModel != model;
+ VBO o = null, a = null;
+ int sz = zone.sizeO * Zone.VERT_SIZE * 3;
+ if (sz > 0)
+ {
+ o = new VBO(sz);
+ o.init(GL_STATIC_DRAW);
+ o.map();
+ }
- GpuIntBuffer b = bufferForTriangles(tc);
+ sz = zone.sizeA * Zone.VERT_SIZE * 3;
+ if (sz > 0)
+ {
+ a = new VBO(sz);
+ a.init(GL_STATIC_DRAW);
+ a.map();
+ }
- b.ensureCapacity(8);
- IntBuffer buffer = b.getBuffer();
- buffer.put(offsetModel.getBufferOffset());
- buffer.put(uvOffset);
- buffer.put(tc);
- buffer.put(targetBufferOffset);
- buffer.put(FLAG_SCENE_BUFFER | (hillskew ? (1 << 26) : 0) | (plane << 24) | orientation);
- buffer.put(x).put(y).put(z);
+ zone.init(o, a);
+ }
+ }
- targetBufferOffset += tc * 3;
+ latch.countDown();
+ });
+ try
+ {
+ latch.await();
}
- else
+ catch (InterruptedException e)
{
- // Temporary model (animated or otherwise not a static Model on the scene)
+ throw new RuntimeException(e);
+ }
- // Apply height to renderable from the model
- if (model != renderable)
+ for (int x = 0; x < ctx.sizeX; ++x)
+ {
+ for (int z = 0; z < ctx.sizeZ; ++z)
{
- renderable.setModelHeight(model.getModelHeight());
+ Zone zone = ctx.zones[x][z];
+
+ sceneUploader.uploadZone(scene, zone, x, z);
}
+ }
+ }
- model.calculateBoundsCylinder();
+ @Override
+ public void despawnWorldView(WorldView worldView)
+ {
+ int worldViewId = worldView.getId();
+ if (worldViewId > -1)
+ {
+ log.debug("WorldView despawn: {}", worldViewId);
+ subs[worldViewId].free();
+ subs[worldViewId] = null;
+ }
+ }
+
+ @Override
+ public void swapScene(Scene scene)
+ {
+ if (scene.getWorldViewId() > -1)
+ {
+ swapSub(scene);
+ return;
+ }
- if (projection instanceof IntProjection)
+ SceneContext ctx = root;
+ for (int x = 0; x < ctx.sizeX; ++x)
+ {
+ for (int z = 0; z < ctx.sizeZ; ++z)
{
- IntProjection p = (IntProjection) projection;
- if (!isVisible(model, p.getPitchSin(), p.getPitchCos(), p.getYawSin(), p.getYawCos(), x - p.getCameraX(), y - p.getCameraY(), z - p.getCameraZ()))
+ Zone zone = ctx.zones[x][z];
+
+ if (zone.cull)
+ {
+ zone.free();
+ }
+ else
{
- return;
+ // reused zone
+ zone.updateRoofs(nextRoofChanges);
}
}
+ }
+ nextRoofChanges = null;
- client.checkClickbox(projection, model, orientation, x, y, z, hash);
-
- boolean hasUv = model.getFaceTextures() != null;
-
- int len = sceneUploader.pushModel(model, vertexBuffer, uvBuffer);
-
- GpuIntBuffer b = bufferForTriangles(len / 3);
-
- b.ensureCapacity(8);
- IntBuffer buffer = b.getBuffer();
- buffer.put(tempOffset);
- buffer.put(hasUv ? tempUvOffset : -1);
- buffer.put(len / 3);
- buffer.put(targetBufferOffset);
- buffer.put(orientation);
- buffer.put(x).put(y).put(z);
+ ctx.zones = nextZones;
+ nextZones = null;
- tempOffset += len;
- if (hasUv)
+ // setup vaos
+ for (int x = 0; x < ctx.zones.length; ++x) // NOPMD: ForLoopCanBeForeach
+ {
+ for (int z = 0; z < ctx.zones[0].length; ++z)
{
- tempUvOffset += len;
- }
+ Zone zone = ctx.zones[x][z];
- targetBufferOffset += len;
+ if (!zone.initialized)
+ {
+ zone.unmap();
+ zone.initialized = true;
+ }
+ }
}
+
+ checkGLErrors();
}
- /**
- * returns the correct buffer based on triangle count and updates model count
- *
- * @param triangles
- * @return
- */
- private GpuIntBuffer bufferForTriangles(int triangles)
+ private void swapSub(Scene scene)
{
- if (triangles <= SMALL_TRIANGLE_COUNT)
+ SceneContext ctx = context(scene);
+ // setup vaos
+ for (int x = 0; x < ctx.sizeX; ++x)
{
- ++smallModels;
- return modelBufferSmall;
- }
- else
- {
- ++largeModels;
- return modelBuffer;
+ for (int z = 0; z < ctx.sizeZ; ++z)
+ {
+ Zone zone = ctx.zones[x][z];
+
+ if (!zone.initialized)
+ {
+ zone.unmap();
+ zone.initialized = true;
+ }
+ }
}
+ log.debug("WorldView ready: {}", scene.getWorldViewId());
}
private int getScaledValue(final double scale, final int value)
@@ -1863,7 +1973,7 @@ private void glDpiAwareViewport(final int x, final int y, final int width, final
{
final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration();
final AffineTransform t = graphicsConfiguration.getDefaultTransform();
- GL43C.glViewport(
+ glViewport(
getScaledValue(t.getScaleX(), x),
getScaledValue(t.getScaleY(), y),
getScaledValue(t.getScaleX(), width),
@@ -1875,71 +1985,6 @@ private int getDrawDistance()
return Ints.constrainToRange(config.drawDistance(), 0, MAX_DISTANCE);
}
- private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, @Nonnull IntBuffer data, int usage, long clFlags)
- {
- int size = data.remaining() << 2;
- updateBuffer(glBuffer, target, size, usage, clFlags);
- GL43C.glBufferSubData(target, 0, data);
- }
-
- private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, @Nonnull FloatBuffer data, int usage, long clFlags)
- {
- int size = data.remaining() << 2;
- updateBuffer(glBuffer, target, size, usage, clFlags);
- GL43C.glBufferSubData(target, 0, data);
- }
-
- private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, int size, int usage, long clFlags)
- {
- GL43C.glBindBuffer(target, glBuffer.glBufferId);
- if (glCapabilities.glInvalidateBufferData != 0L)
- {
- // https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming suggests buffer re-specification is useful
- // to avoid implicit synching. We always need to trash the whole buffer anyway so this can't hurt.
- GL43C.glInvalidateBufferData(glBuffer.glBufferId);
- }
- if (size > glBuffer.size)
- {
- int newSize = Math.max(1024, nextPowerOfTwo(size));
- log.trace("Buffer resize: {} {} -> {}", glBuffer.name, glBuffer.size, newSize);
-
- glBuffer.size = newSize;
- GL43C.glBufferData(target, newSize, usage);
- recreateCLBuffer(glBuffer, clFlags);
- }
- }
-
- private static int nextPowerOfTwo(int v)
- {
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- v++;
- return v;
- }
-
- private void recreateCLBuffer(GLBuffer glBuffer, long clFlags)
- {
- if (computeMode == ComputeMode.OPENCL)
- {
- if (glBuffer.clBuffer != -1)
- {
- CL10.clReleaseMemObject(glBuffer.clBuffer);
- }
- if (glBuffer.size == 0)
- {
- glBuffer.clBuffer = -1;
- }
- else
- {
- glBuffer.clBuffer = CL10GL.clCreateFromGLBuffer(openCLManager.context, clFlags, glBuffer.glBufferId, (int[]) null);
- }
- }
- }
-
private void checkGLErrors()
{
if (!log.isDebugEnabled())
@@ -1949,8 +1994,8 @@ private void checkGLErrors()
for (; ; )
{
- int err = GL43C.glGetError();
- if (err == GL43C.GL_NO_ERROR)
+ int err = glGetError();
+ if (err == GL_NO_ERROR)
{
return;
}
@@ -1958,16 +2003,16 @@ private void checkGLErrors()
String errStr;
switch (err)
{
- case GL43C.GL_INVALID_ENUM:
+ case GL_INVALID_ENUM:
errStr = "INVALID_ENUM";
break;
- case GL43C.GL_INVALID_VALUE:
+ case GL_INVALID_VALUE:
errStr = "INVALID_VALUE";
break;
- case GL43C.GL_INVALID_OPERATION:
+ case GL_INVALID_OPERATION:
errStr = "INVALID_OPERATION";
break;
- case GL43C.GL_INVALID_FRAMEBUFFER_OPERATION:
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
errStr = "INVALID_FRAMEBUFFER_OPERATION";
break;
default:
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
index e39057ec1d5..2f34fa8cf3f 100644
--- 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
@@ -73,22 +73,11 @@ default boolean hideUnrelatedMaps()
description = "Extra map area to load, in 8 tile chunks.",
position = 1
)
- default int expandedMapLoadingChunks()
+ default int expandedMapLoadingZones()
{
return 3;
}
- @ConfigItem(
- keyName = "smoothBanding",
- name = "Remove color banding",
- description = "Smooths out the color banding that is present in the CPU renderer.",
- position = 2
- )
- default boolean smoothBanding()
- {
- return true;
- }
-
@ConfigItem(
keyName = "antiAliasingMode",
name = "Anti aliasing",
@@ -108,7 +97,7 @@ default AntiAliasingMode antiAliasingMode()
)
default UIScalingMode uiScalingMode()
{
- return UIScalingMode.LINEAR;
+ return UIScalingMode.HYBRID;
}
@Range(
@@ -125,18 +114,6 @@ default int fogDepth()
return 0;
}
- @ConfigItem(
- keyName = "useComputeShaders",
- name = "Compute shaders",
- description = "Offloads face sorting to GPU. Requires plugin restart.",
- warning = "This feature requires OpenGL 4.3 to use. Please check that your GPU supports this.\nRestart the plugin for changes to take effect.",
- position = 6
- )
- default boolean useComputeShaders()
- {
- return true;
- }
-
@Range(
min = 0,
max = 16
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java
deleted file mode 100644
index 4d12d88ef1c..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java
+++ /dev/null
@@ -1,661 +0,0 @@
-/*
- * Copyright (c) 2021, 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.IntBuffer;
-import java.nio.LongBuffer;
-import java.nio.ShortBuffer;
-import javax.inject.Singleton;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Constants;
-import net.runelite.api.Scene;
-import net.runelite.client.plugins.gpu.template.Template;
-import net.runelite.client.util.OSType;
-import net.runelite.rlawt.AWTContext;
-import org.lwjgl.PointerBuffer;
-import org.lwjgl.opencl.APPLEGLSharing;
-import static org.lwjgl.opencl.APPLEGLSharing.CL_CGL_DEVICE_FOR_CURRENT_VIRTUAL_SCREEN_APPLE;
-import static org.lwjgl.opencl.APPLEGLSharing.clGetGLContextInfoAPPLE;
-import org.lwjgl.opencl.CL;
-import static org.lwjgl.opencl.CL10.CL_CONTEXT_PLATFORM;
-import static org.lwjgl.opencl.CL10.CL_DEVICE_EXTENSIONS;
-import static org.lwjgl.opencl.CL10.CL_DEVICE_NAME;
-import static org.lwjgl.opencl.CL10.CL_DEVICE_PROFILE;
-import static org.lwjgl.opencl.CL10.CL_DEVICE_VENDOR;
-import static org.lwjgl.opencl.CL10.CL_DEVICE_VERSION;
-import static org.lwjgl.opencl.CL10.CL_DRIVER_VERSION;
-import static org.lwjgl.opencl.CL10.CL_PROGRAM_BUILD_LOG;
-import static org.lwjgl.opencl.CL10.CL_PROGRAM_BUILD_OPTIONS;
-import static org.lwjgl.opencl.CL10.CL_PROGRAM_BUILD_STATUS;
-import static org.lwjgl.opencl.CL10.CL_SUCCESS;
-import static org.lwjgl.opencl.CL10.clGetPlatformInfo;
-import static org.lwjgl.opencl.CL10.clGetProgramBuildInfo;
-import org.lwjgl.opencl.CL10GL;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_ADDRESS_BITS;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_AVAILABLE;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_COMPILER_AVAILABLE;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_CLOCK_FREQUENCY;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_COMPUTE_UNITS;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_WORK_GROUP_SIZE;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_QUEUE_PROPERTIES;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_TYPE;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_TYPE_GPU;
-import static org.lwjgl.opencl.CL11.CL_DEVICE_VENDOR_ID;
-import static org.lwjgl.opencl.CL11.CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE;
-import static org.lwjgl.opencl.CL11.clCreateContext;
-import static org.lwjgl.opencl.CL11.clGetDeviceIDs;
-import static org.lwjgl.opencl.CL11.clGetDeviceInfo;
-import static org.lwjgl.opencl.CL11.clGetPlatformIDs;
-import org.lwjgl.opencl.CL12;
-import static org.lwjgl.opencl.CL12.CL_PROGRAM_BINARY_TYPE;
-import org.lwjgl.opencl.CLCapabilities;
-import org.lwjgl.opencl.CLContextCallback;
-import org.lwjgl.opencl.CLImageFormat;
-import static org.lwjgl.opencl.KHRGLSharing.CL_GLX_DISPLAY_KHR;
-import static org.lwjgl.opencl.KHRGLSharing.CL_GL_CONTEXT_KHR;
-import static org.lwjgl.opencl.KHRGLSharing.CL_WGL_HDC_KHR;
-import org.lwjgl.system.Configuration;
-import org.lwjgl.system.MemoryStack;
-import org.lwjgl.system.MemoryUtil;
-import static org.lwjgl.system.MemoryUtil.NULL;
-import static org.lwjgl.system.MemoryUtil.memASCII;
-import static org.lwjgl.system.MemoryUtil.memUTF8;
-
-@Singleton
-@Slf4j
-class OpenCLManager
-{
- private static final String KERNEL_NAME_UNORDERED = "computeUnordered";
- private static final String KERNEL_NAME_LARGE = "computeLarge";
-
- private static final int MIN_WORK_GROUP_SIZE = 256;
- private static final int SMALL_SIZE = GpuPlugin.SMALL_TRIANGLE_COUNT;
- private static final int LARGE_SIZE = GpuPlugin.MAX_TRIANGLE;
- // struct shared_data {
- // int totalNum[12];
- // int totalDistance[12];
- // int totalMappedNum[18];
- // int min10;
- // int renderPris[0];
- // };
- private static final int SHARED_SIZE = 12 + 12 + 18 + 1; // in ints
-
- private boolean initialized;
-
- // The number of faces each worker processes in the two kernels
- private int largeFaceCount;
- private int smallFaceCount;
-
- private long device;
- long context;
- private long commandQueue;
-
- private long programUnordered;
- private long programSmall;
- private long programLarge;
-
- private long kernelUnordered;
- private long kernelSmall;
- private long kernelLarge;
-
- private long tileHeightImage;
-
- static
- {
- Configuration.OPENCL_EXPLICIT_INIT.set(true);
- }
-
- void init(AWTContext awtContext)
- {
- device = context = commandQueue = 0L;
- programUnordered = programSmall = programLarge = 0L;
- kernelUnordered = kernelSmall = kernelLarge = 0L;
- tileHeightImage = 0L;
-
- CL.create();
- initialized = true;
-
- try (var stack = MemoryStack.stackPush())
- {
- if (OSType.getOSType() == OSType.MacOS)
- {
- initContextMacOS(awtContext, stack);
- }
- else
- {
- initContext(awtContext, stack);
- }
-
- ensureMinWorkGroupSize();
- initQueue();
- compilePrograms(stack);
- }
- }
-
- void cleanup()
- {
- if (!initialized)
- {
- return;
- }
-
- try
- {
- if (tileHeightImage != 0L)
- {
- CL12.clReleaseMemObject(tileHeightImage);
- }
-
- CL12.clReleaseKernel(kernelUnordered);
- CL12.clReleaseKernel(kernelSmall);
- CL12.clReleaseKernel(kernelLarge);
-
- CL12.clReleaseProgram(programUnordered);
- CL12.clReleaseProgram(programSmall);
- CL12.clReleaseProgram(programLarge);
-
- CL12.clReleaseCommandQueue(commandQueue);
-
- CL12.clReleaseContext(context);
-
- CL12.clReleaseDevice(device);
- }
- finally
- {
- CL.destroy();
- initialized = false;
- }
- }
-
- private void initContext(AWTContext awtContext, MemoryStack stack)
- {
- IntBuffer pi = stack.mallocInt(1);
- checkCLError(clGetPlatformIDs(null, pi));
- if (pi.get(0) == 0)
- {
- throw new RuntimeException("No OpenCL platforms found.");
- }
-
- PointerBuffer platforms = stack.mallocPointer(pi.get(0));
- checkCLError(clGetPlatformIDs(platforms, (IntBuffer) null));
-
- PointerBuffer ctxProps = stack.mallocPointer(7);
- if (OSType.getOSType() == OSType.Windows)
- {
- ctxProps
- .put(CL_CONTEXT_PLATFORM)
- .put(0)
- .put(CL_GL_CONTEXT_KHR)
- .put(awtContext.getGLContext())
- .put(CL_WGL_HDC_KHR)
- .put(awtContext.getWGLHDC())
- .put(0)
- .flip();
- }
- else if (OSType.getOSType() == OSType.Linux)
- {
- ctxProps
- .put(CL_CONTEXT_PLATFORM)
- .put(0)
- .put(CL_GL_CONTEXT_KHR)
- .put(awtContext.getGLContext())
- .put(CL_GLX_DISPLAY_KHR)
- .put(awtContext.getGLXDisplay())
- .put(0)
- .flip();
- }
- else
- {
- throw new RuntimeException("unsupported platform");
- }
-
- for (int p = 0; p < platforms.capacity(); p++)
- {
- long platform = platforms.get(p);
- ctxProps.put(1, platform);
-
- try
- {
- CLCapabilities platformCaps = CL.createPlatformCapabilities(platform);
-
- log.debug("Platform profile: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_PROFILE));
- log.debug("Platform version: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_VERSION));
- log.debug("Platform name: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_NAME));
- log.debug("Platform vendor: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_VENDOR));
- log.debug("Platform extensions: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_EXTENSIONS));
-
- checkCLError(clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, null, pi));
-
- PointerBuffer devices = stack.mallocPointer(pi.get(0));
- checkCLError(clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, devices, (IntBuffer) null));
-
- for (int d = 0; d < devices.capacity(); d++)
- {
- long device = devices.get(d);
-
- try
- {
- CLCapabilities deviceCaps = CL.createDeviceCapabilities(device, platformCaps);
-
- log.debug("Device id {}", device);
- log.debug("\tCL_DEVICE_NAME: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_NAME));
- log.debug("\tCL_DEVICE_VENDOR: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_VENDOR));
- log.debug("\tCL_DRIVER_VERSION: {}", getDeviceInfoStringUTF8(device, CL_DRIVER_VERSION));
- log.debug("\tCL_DEVICE_PROFILE: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_PROFILE));
- log.debug("\tCL_DEVICE_VERSION: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_VERSION));
- log.debug("\tCL_DEVICE_EXTENSIONS: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_EXTENSIONS));
- log.debug("\tCL_DEVICE_TYPE: {}", getDeviceInfoLong(device, CL_DEVICE_TYPE));
- log.debug("\tCL_DEVICE_VENDOR_ID: {}", getDeviceInfoInt(device, CL_DEVICE_VENDOR_ID));
- log.debug("\tCL_DEVICE_MAX_COMPUTE_UNITS: {}", getDeviceInfoInt(device, CL_DEVICE_MAX_COMPUTE_UNITS));
- log.debug("\tCL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: {}", getDeviceInfoInt(device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS));
- log.debug("\tCL_DEVICE_MAX_WORK_GROUP_SIZE: {}", getDeviceInfoPointer(device, CL_DEVICE_MAX_WORK_GROUP_SIZE));
- log.debug("\tCL_DEVICE_MAX_CLOCK_FREQUENCY: {}", getDeviceInfoInt(device, CL_DEVICE_MAX_CLOCK_FREQUENCY));
- log.debug("\tCL_DEVICE_ADDRESS_BITS: {}", getDeviceInfoInt(device, CL_DEVICE_ADDRESS_BITS));
- log.debug("\tCL_DEVICE_AVAILABLE: {}", getDeviceInfoInt(device, CL_DEVICE_AVAILABLE) != 0);
- log.debug("\tCL_DEVICE_COMPILER_AVAILABLE: {}", getDeviceInfoInt(device, CL_DEVICE_COMPILER_AVAILABLE) != 0);
-
- if (!deviceCaps.cl_khr_gl_sharing && !deviceCaps.cl_APPLE_gl_sharing)
- {
- continue;
- }
-
- IntBuffer errcode_ret = stack.callocInt(1);
- long context = clCreateContext(ctxProps, device, CLContextCallback.create((errinfo, private_info, cb, user_data) ->
- log.error("[LWJGL] cl_context_callback: {}", memUTF8(errinfo))), NULL, errcode_ret);
- checkCLError(errcode_ret);
-
- this.device = device;
- this.context = context;
- return;
- }
- catch (Exception ex)
- {
- log.error("error checking device", ex);
- }
- }
- }
- catch (Exception ex)
- {
- log.error("error checking platform", ex);
- }
- }
-
- throw new RuntimeException("Unable to find compute platform");
- }
-
- private void initContextMacOS(AWTContext awtContext, MemoryStack stack)
- {
- PointerBuffer ctxProps = stack.mallocPointer(3);
- ctxProps
- .put(APPLEGLSharing.CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE)
- .put(awtContext.getCGLShareGroup())
- .put(0)
- .flip();
-
- IntBuffer errcode_ret = stack.callocInt(1);
- var devices = stack.mallocPointer(0);
- long context = clCreateContext(ctxProps, devices, CLContextCallback.create((errinfo, private_info, cb, user_data) ->
- log.error("[LWJGL] cl_context_callback: {}", memUTF8(errinfo))), NULL, errcode_ret);
- checkCLError(errcode_ret);
-
- var deviceBuf = stack.mallocPointer(1);
- checkCLError(clGetGLContextInfoAPPLE(context, awtContext.getGLContext(), CL_CGL_DEVICE_FOR_CURRENT_VIRTUAL_SCREEN_APPLE, deviceBuf, null));
- long device = deviceBuf.get(0);
-
- log.debug("Got macOS CLGL compute device {}", device);
- this.context = context;
- this.device = device;
- }
-
- private void ensureMinWorkGroupSize()
- {
- long[] maxWorkGroupSize = new long[1];
- CL12.clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, maxWorkGroupSize, null);
- log.debug("Device CL_DEVICE_MAX_WORK_GROUP_SIZE: {}", maxWorkGroupSize[0]);
-
- if (maxWorkGroupSize[0] < MIN_WORK_GROUP_SIZE)
- {
- throw new RuntimeException("Compute device does not support min work group size " + MIN_WORK_GROUP_SIZE);
- }
-
- // Largest power of 2 less than or equal to maxWorkGroupSize
- int groupSize = 0x80000000 >>> Integer.numberOfLeadingZeros((int) maxWorkGroupSize[0]);
- largeFaceCount = LARGE_SIZE / (Math.min(groupSize, LARGE_SIZE));
- smallFaceCount = SMALL_SIZE / (Math.min(groupSize, SMALL_SIZE));
-
- log.debug("Face counts: small: {}, large: {}", smallFaceCount, largeFaceCount);
- }
-
- private void initQueue()
- {
- long[] l = new long[1];
- CL12.clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, l, null);
-
- commandQueue = CL12.clCreateCommandQueue(context, device, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, (int[]) null);
- log.debug("Created command_queue {}, properties {}", commandQueue, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE);
- }
-
- private long compileProgram(MemoryStack stack, String programSource)
- {
- log.trace("Compiling program:\n {}", programSource);
- IntBuffer errcode_ret = stack.callocInt(1);
- long program = CL12.clCreateProgramWithSource(context, programSource, errcode_ret);
- checkCLError(errcode_ret);
-
- int err = CL12.clBuildProgram(program, device, "", null, 0);
- if (err != CL_SUCCESS)
- {
- String errstr = getProgramBuildInfoStringASCII(program, device, CL_PROGRAM_BUILD_LOG);
- throw new RuntimeException(errstr);
- }
-
- log.debug("Build status: {}", getProgramBuildInfoInt(program, device, CL_PROGRAM_BUILD_STATUS));
- log.debug("Binary type: {}", getProgramBuildInfoInt(program, device, CL_PROGRAM_BINARY_TYPE));
- log.debug("Build options: {}", getProgramBuildInfoStringASCII(program, device, CL_PROGRAM_BUILD_OPTIONS));
- log.debug("Build log: {}", getProgramBuildInfoStringASCII(program, device, CL_PROGRAM_BUILD_LOG));
- return program;
- }
-
- private long getKernel(MemoryStack stack, long program, String kernelName)
- {
- IntBuffer errcode_ret = stack.callocInt(1);
- long kernel = CL12.clCreateKernel(program, kernelName, errcode_ret);
- checkCLError(errcode_ret);
- log.debug("Loaded kernel {} for program {}", kernelName, program);
- return kernel;
- }
-
- private void compilePrograms(MemoryStack stack)
- {
- Template templateSmall = new Template()
- .addInclude(OpenCLManager.class)
- .add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + smallFaceCount) : null);
- Template templateLarge = new Template()
- .addInclude(OpenCLManager.class)
- .add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + largeFaceCount) : null);
-
- String unordered = new Template()
- .addInclude(OpenCLManager.class)
- .load("comp_unordered.cl");
- String small = templateSmall.load("comp.cl");
- String large = templateLarge.load("comp.cl");
-
- programUnordered = compileProgram(stack, unordered);
- programSmall = compileProgram(stack, small);
- programLarge = compileProgram(stack, large);
-
- kernelUnordered = getKernel(stack, programUnordered, KERNEL_NAME_UNORDERED);
- kernelSmall = getKernel(stack, programSmall, KERNEL_NAME_LARGE);
- kernelLarge = getKernel(stack, programLarge, KERNEL_NAME_LARGE);
- }
-
- void uploadTileHeights(Scene scene)
- {
- if (tileHeightImage != 0L)
- {
- CL12.clReleaseMemObject(tileHeightImage);
- tileHeightImage = 0L;
- }
-
- final int TILEHEIGHT_BUFFER_SIZE = Constants.MAX_Z * Constants.EXTENDED_SCENE_SIZE * Constants.EXTENDED_SCENE_SIZE * Short.BYTES;
- ShortBuffer tileBuffer = MemoryUtil.memAllocShort(TILEHEIGHT_BUFFER_SIZE);
- int[][][] tileHeights = scene.getTileHeights();
- for (int z = 0; z < Constants.MAX_Z; ++z)
- {
- for (int y = 0; y < Constants.EXTENDED_SCENE_SIZE; ++y)
- {
- for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x)
- {
- int h = tileHeights[z][x][y];
- assert (h & 0b111) == 0;
- h >>= 3;
- tileBuffer.put((short) h);
- }
- }
- }
- tileBuffer.flip();
-
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- CLImageFormat imageFormat = CLImageFormat.calloc(stack);
- imageFormat.image_channel_order(CL12.CL_R);
- imageFormat.image_channel_data_type(CL12.CL_SIGNED_INT16);
-
- IntBuffer errcode_ret = stack.callocInt(1);
- tileHeightImage = CL12.clCreateImage3D(context, CL12.CL_MEM_READ_ONLY | CL12.CL_MEM_COPY_HOST_PTR, imageFormat,
- Constants.EXTENDED_SCENE_SIZE, Constants.EXTENDED_SCENE_SIZE, Constants.MAX_Z,
- 0L, 0L,
- tileBuffer,
- errcode_ret);
- checkCLError(errcode_ret);
- }
-
- MemoryUtil.memFree(tileBuffer);
- }
-
- void compute(int unorderedModels, int smallModels, int largeModels,
- GLBuffer sceneVertexBuffer,
- GLBuffer sceneUvBuffer,
- GLBuffer vertexBuffer,
- GLBuffer uvBuffer,
- GLBuffer unorderedBuffer,
- GLBuffer smallBuffer,
- GLBuffer largeBuffer,
- GLBuffer outVertexBuffer,
- GLBuffer outUvBuffer,
- GLBuffer uniformBuffer
- )
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- PointerBuffer glBuffers = stack.mallocPointer(10);
- glBuffers.put(sceneVertexBuffer.clBuffer);
- glBuffers.put(sceneUvBuffer.clBuffer);
- glBuffers.put(unorderedBuffer.clBuffer);
- glBuffers.put(smallBuffer.clBuffer);
- glBuffers.put(largeBuffer.clBuffer);
- glBuffers.put(vertexBuffer.clBuffer);
- glBuffers.put(uvBuffer.clBuffer);
- glBuffers.put(outVertexBuffer.clBuffer);
- glBuffers.put(outUvBuffer.clBuffer);
- glBuffers.put(uniformBuffer.clBuffer);
- glBuffers.flip();
-
- PointerBuffer acquireEvent = stack.mallocPointer(1);
- CL10GL.clEnqueueAcquireGLObjects(commandQueue, glBuffers, null, acquireEvent);
-
- var computeEvents = stack.mallocPointer(3);
- if (unorderedModels > 0)
- {
- CL12.clSetKernelArg1p(kernelUnordered, 0, unorderedBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelUnordered, 1, sceneVertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelUnordered, 2, vertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelUnordered, 3, sceneUvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelUnordered, 4, uvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelUnordered, 5, outVertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelUnordered, 6, outUvBuffer.clBuffer);
-
- // queue compute call after acquireGLBuffers
- CL12.clEnqueueNDRangeKernel(commandQueue, kernelUnordered, 1, null,
- stack.pointers(unorderedModels * 6L), stack.pointers(6),
- acquireEvent, computeEvents);
- computeEvents.position(computeEvents.position() + 1);
- }
-
- if (smallModels > 0)
- {
- CL12.clSetKernelArg(kernelSmall, 0, (SHARED_SIZE + SMALL_SIZE) * Integer.BYTES);
- CL12.clSetKernelArg1p(kernelSmall, 1, smallBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 2, sceneVertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 3, vertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 4, sceneUvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 5, uvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 6, outVertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 7, outUvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelSmall, 8, uniformBuffer.clBuffer);
- CL12.clSetKernelArg1l(kernelSmall, 9, tileHeightImage);
-
- CL12.clEnqueueNDRangeKernel(commandQueue, kernelSmall, 1, null,
- stack.pointers(smallModels * (SMALL_SIZE / smallFaceCount)), stack.pointers(SMALL_SIZE / smallFaceCount),
- acquireEvent, computeEvents);
- computeEvents.position(computeEvents.position() + 1);
- }
-
- if (largeModels > 0)
- {
- CL12.clSetKernelArg(kernelLarge, 0, (SHARED_SIZE + LARGE_SIZE) * Integer.BYTES);
- CL12.clSetKernelArg1p(kernelLarge, 1, largeBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 2, sceneVertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 3, vertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 4, sceneUvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 5, uvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 6, outVertexBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 7, outUvBuffer.clBuffer);
- CL12.clSetKernelArg1p(kernelLarge, 8, uniformBuffer.clBuffer);
- CL12.clSetKernelArg1l(kernelLarge, 9, tileHeightImage);
-
- CL12.clEnqueueNDRangeKernel(commandQueue, kernelLarge, 1, null,
- stack.pointers(largeModels * (LARGE_SIZE / largeFaceCount)), stack.pointers(LARGE_SIZE / largeFaceCount),
- acquireEvent, computeEvents);
- computeEvents.position(computeEvents.position() + 1);
- }
-
- if (computeEvents.position() == 0)
- {
- CL10GL.clEnqueueReleaseGLObjects(commandQueue, glBuffers, null, null);
- }
- else
- {
- computeEvents.flip();
- CL10GL.clEnqueueReleaseGLObjects(commandQueue, glBuffers, computeEvents, null);
- }
- }
- }
-
- void finish()
- {
- CL12.clFinish(commandQueue);
- }
-
- private static String getPlatformInfoStringUTF8(long cl_platform_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- PointerBuffer pp = stack.mallocPointer(1);
- checkCLError(clGetPlatformInfo(cl_platform_id, param_name, (ByteBuffer) null, pp));
- int bytes = (int) pp.get(0);
-
- ByteBuffer buffer = stack.malloc(bytes);
- checkCLError(clGetPlatformInfo(cl_platform_id, param_name, buffer, null));
-
- return memUTF8(buffer, bytes - 1);
- }
- }
-
- private static long getDeviceInfoLong(long cl_device_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- LongBuffer pl = stack.mallocLong(1);
- checkCLError(clGetDeviceInfo(cl_device_id, param_name, pl, null));
- return pl.get(0);
- }
- }
-
- private static int getDeviceInfoInt(long cl_device_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- IntBuffer pl = stack.mallocInt(1);
- checkCLError(clGetDeviceInfo(cl_device_id, param_name, pl, null));
- return pl.get(0);
- }
- }
-
- private static long getDeviceInfoPointer(long cl_device_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- PointerBuffer pp = stack.mallocPointer(1);
- checkCLError(clGetDeviceInfo(cl_device_id, param_name, pp, null));
- return pp.get(0);
- }
- }
-
- private static String getDeviceInfoStringUTF8(long cl_device_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- PointerBuffer pp = stack.mallocPointer(1);
- checkCLError(clGetDeviceInfo(cl_device_id, param_name, (ByteBuffer) null, pp));
- int bytes = (int) pp.get(0);
-
- ByteBuffer buffer = stack.malloc(bytes);
- checkCLError(clGetDeviceInfo(cl_device_id, param_name, buffer, null));
-
- return memUTF8(buffer, bytes - 1);
- }
- }
-
- private static int getProgramBuildInfoInt(long cl_program_id, long cl_device_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- IntBuffer pl = stack.mallocInt(1);
- checkCLError(clGetProgramBuildInfo(cl_program_id, cl_device_id, param_name, pl, null));
- return pl.get(0);
- }
- }
-
- private static String getProgramBuildInfoStringASCII(long cl_program_id, long cl_device_id, int param_name)
- {
- try (MemoryStack stack = MemoryStack.stackPush())
- {
- PointerBuffer pp = stack.mallocPointer(1);
- checkCLError(clGetProgramBuildInfo(cl_program_id, cl_device_id, param_name, (ByteBuffer) null, pp));
- int bytes = (int) pp.get(0);
-
- ByteBuffer buffer = stack.malloc(bytes);
- checkCLError(clGetProgramBuildInfo(cl_program_id, cl_device_id, param_name, buffer, null));
-
- return memASCII(buffer, bytes - 1);
- }
- }
-
- private static void checkCLError(IntBuffer errcode)
- {
- checkCLError(errcode.get(errcode.position()));
- }
-
- private static void checkCLError(int errcode)
- {
- if (errcode != CL_SUCCESS)
- {
- throw new RuntimeException(String.format("OpenCL error [%d]", errcode));
- }
- }
-}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/RegionManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/RegionManager.java
new file mode 100644
index 00000000000..9d72380782a
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/RegionManager.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2025, 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.io.IOException;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import net.runelite.api.Constants;
+import net.runelite.api.Scene;
+import net.runelite.api.Tile;
+import net.runelite.client.plugins.gpu.regions.Regions;
+
+@Singleton
+class RegionManager
+{
+ private final GpuPluginConfig gpuConfig;
+ private final Regions regions;
+
+ @Inject
+ RegionManager(
+ GpuPluginConfig config
+ )
+ {
+ this.gpuConfig = config;
+
+ try (var in = SceneUploader.class.getResourceAsStream("regions/regions.txt"))
+ {
+ regions = new Regions(in, "regions.txt");
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ // remove tiles from the scene that are outside the current region
+ void prepare(Scene scene)
+ {
+ if (scene.isInstance() || !gpuConfig.hideUnrelatedMaps())
+ {
+ return;
+ }
+
+ int baseX = scene.getBaseX() / 8;
+ int baseY = scene.getBaseY() / 8;
+ int centerX = baseX + 6;
+ int centerY = baseY + 6;
+ int centerId = regions.getRegionId(centerX, centerY);
+
+ int r = Constants.EXTENDED_SCENE_SIZE / 16;
+ for (int offx = -r; offx <= r; ++offx)
+ {
+ for (int offy = -r; offy <= r; ++offy)
+ {
+ int cx = centerX + offx;
+ int cy = centerY + offy;
+ int id = regions.getRegionId(cx, cy);
+ if (id != centerId)
+ {
+ removeZone(scene, cx, cy);
+ }
+ }
+ }
+ }
+
+ private static void removeZone(Scene scene, int cx, int cy)
+ {
+ int wx = cx * 8;
+ int wy = cy * 8;
+ int sx = wx - scene.getBaseX();
+ int sy = wy - scene.getBaseY();
+ int cmsx = sx + GpuPlugin.SCENE_OFFSET;
+ int cmsy = sy + GpuPlugin.SCENE_OFFSET;
+ Tile[][][] tiles = scene.getExtendedTiles();
+ for (int x = 0; x < 8; ++x)
+ {
+ for (int y = 0; y < 8; ++y)
+ {
+ int msx = cmsx + x;
+ int msy = cmsy + y;
+ if (msx >= 0 && msx < Constants.EXTENDED_SCENE_SIZE && msy >= 0 && msy < Constants.EXTENDED_SCENE_SIZE)
+ {
+ for (int z = 0; z < Constants.MAX_Z; ++z)
+ {
+ Tile tile = tiles[z][msx][msy];
+ if (tile != null)
+ {
+ scene.removeTile(tile);
+ }
+ }
+ }
+ }
+ }
+ }
+}
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
index eee5eea53aa..2054466a695 100644
--- 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
@@ -24,196 +24,301 @@
*/
package net.runelite.client.plugins.gpu;
-import com.google.common.base.Stopwatch;
-import java.io.IOException;
-import java.util.Arrays;
+import java.nio.IntBuffer;
+import java.util.HashSet;
+import java.util.Set;
import javax.inject.Inject;
-import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.api.DecorativeObject;
+import net.runelite.api.DynamicObject;
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.Projection;
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;
-import net.runelite.client.plugins.gpu.regions.Regions;
+import net.runelite.client.callback.RenderCallbackManager;
-@Singleton
@Slf4j
class SceneUploader
{
- private final Client client;
- private final GpuPluginConfig gpuConfig;
-
- private final Regions regions;
-
- int sceneId = (int) System.nanoTime();
- private int offset;
- private int uvoffset;
- private int uniqueModels;
+ private final RenderCallbackManager renderCallbackManager;
+ private int basex, basez, rid, level;
@Inject
- SceneUploader(
- Client client,
- GpuPluginConfig config
- )
+ SceneUploader(RenderCallbackManager renderCallbackManager)
{
- this.client = client;
- this.gpuConfig = config;
-
- try (var in = SceneUploader.class.getResourceAsStream("regions/regions.txt"))
- {
- regions = new Regions(in, "regions.txt");
- }
- catch (IOException ex)
- {
- throw new RuntimeException(ex);
- }
+ this.renderCallbackManager = renderCallbackManager;
}
- void upload(Scene scene, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
+ void zoneSize(Scene scene, Zone zone, int mzx, int mzz)
{
- ++sceneId;
- offset = 0;
- uvoffset = 0;
- uniqueModels = 0;
- vertexBuffer.clear();
- uvBuffer.clear();
-
- Stopwatch stopwatch = Stopwatch.createStarted();
- prepare(scene);
- stopwatch.stop();
- log.debug("Scene preparation time: {}", stopwatch);
-
- stopwatch = Stopwatch.createStarted();
- for (int z = 0; z < Constants.MAX_Z; ++z)
+ Tile[][][] tiles = scene.getExtendedTiles();
+
+ for (int z = 3; z >= 0; --z)
{
- for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x)
+ for (int xoff = 0; xoff < 8; ++xoff)
{
- for (int y = 0; y < Constants.EXTENDED_SCENE_SIZE; ++y)
+ for (int zoff = 0; zoff < 8; ++zoff)
{
- Tile tile = scene.getExtendedTiles()[z][x][y];
- if (tile != null)
+ Tile t = tiles[z][(mzx << 3) + xoff][(mzz << 3) + zoff];
+ if (t != null)
{
- upload(scene, tile, vertexBuffer, uvBuffer);
+ zoneSize(zone, t);
}
}
}
}
-
- stopwatch.stop();
- log.debug("Scene upload time: {} unique models: {} length: {}KB", stopwatch, uniqueModels, (offset * 16) / 1024);
}
- private void upload(Scene scene, Tile tile, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
+ void uploadZone(Scene scene, Zone zone, int mzx, int mzz)
{
- Tile bridge = tile.getBridge();
- if (bridge != null)
+ int[][][] roofs = scene.getRoofs();
+ Set roofIds = new HashSet<>();
+
+ var vb = zone.vboO != null ? new GpuIntBuffer(zone.vboO.vb) : null;
+ var ab = zone.vboA != null ? new GpuIntBuffer(zone.vboA.vb) : null;
+
+ for (int level = 0; level <= 3; ++level)
{
- upload(scene, bridge, vertexBuffer, uvBuffer);
+ for (int xoff = 0; xoff < 8; ++xoff)
+ {
+ for (int zoff = 0; zoff < 8; ++zoff)
+ {
+ int rid = roofs[level][(mzx << 3) + xoff][(mzz << 3) + zoff];
+ if (rid > 0)
+ {
+ roofIds.add(rid);
+ }
+ }
+ }
}
- SceneTilePaint sceneTilePaint = tile.getSceneTilePaint();
- if (sceneTilePaint != null)
+ zone.rids = new int[4][roofIds.size()];
+ zone.roofStart = new int[4][roofIds.size()];
+ zone.roofEnd = new int[4][roofIds.size()];
+
+ for (int level = 0; level <= 3; ++level)
{
- sceneTilePaint.setBufferOffset(offset);
- if (sceneTilePaint.getTexture() != -1)
+ this.level = level;
+
+ if (level == 0)
{
- sceneTilePaint.setUvBufferOffset(uvoffset);
+ uploadZoneLevel(scene, zone, mzx, mzz, level, false, roofIds, vb, ab);
+ uploadZoneLevel(scene, zone, mzx, mzz, level, true, roofIds, vb, ab);
+ uploadZoneLevel(scene, zone, mzx, mzz, 1, true, roofIds, vb, ab);
+ uploadZoneLevel(scene, zone, mzx, mzz, 2, true, roofIds, vb, ab);
+ uploadZoneLevel(scene, zone, mzx, mzz, 3, true, roofIds, vb, ab);
}
else
{
- sceneTilePaint.setUvBufferOffset(-1);
+ uploadZoneLevel(scene, zone, mzx, mzz, level, false, roofIds, vb, ab);
}
- Point tilePoint = tile.getSceneLocation();
- int len = upload(scene, sceneTilePaint,
- tile.getRenderLevel(), tilePoint.getX(), tilePoint.getY(),
- vertexBuffer, uvBuffer,
- 0, 0, false);
- sceneTilePaint.setBufferLen(len);
- offset += len;
- if (sceneTilePaint.getTexture() != -1)
+
+ if (zone.vboO != null)
{
- uvoffset += len;
+ int pos = zone.vboO.vb.position();
+ zone.levelOffsets[level] = pos;
}
}
+ }
+
+ private void uploadZoneLevel(Scene scene, Zone zone, int mzx, int mzz, int level, boolean visbelow, Set roofIds, GpuIntBuffer vb, GpuIntBuffer ab)
+ {
+ int ridx = 0;
- SceneTileModel sceneTileModel = tile.getSceneTileModel();
- if (sceneTileModel != null)
+ // upload the roofs and save their positions
+ for (int id : roofIds)
{
- sceneTileModel.setBufferOffset(offset);
- if (sceneTileModel.getTriangleTextureId() != null)
- {
- sceneTileModel.setUvBufferOffset(uvoffset);
- }
- else
+ int pos = zone.vboO != null ? zone.vboO.vb.position() : 0;
+
+ uploadZoneLevelRoof(scene, zone, mzx, mzz, level, id, visbelow, vb, ab);
+
+ int endpos = zone.vboO != null ? zone.vboO.vb.position() : 0;
+
+ if (endpos > pos)
{
- sceneTileModel.setUvBufferOffset(-1);
+ zone.rids[level][ridx] = id;
+ zone.roofStart[level][ridx] = pos;
+ zone.roofEnd[level][ridx] = endpos;
+ ++ridx;
}
- Point tilePoint = tile.getSceneLocation();
- int len = upload(sceneTileModel,
- tilePoint.getX() << Perspective.LOCAL_COORD_BITS, tilePoint.getY() << Perspective.LOCAL_COORD_BITS,
- vertexBuffer, uvBuffer, false);
- sceneTileModel.setBufferLen(len);
- offset += len;
- if (sceneTileModel.getTriangleTextureId() != null)
+ }
+
+ // upload everything else
+ uploadZoneLevelRoof(scene, zone, mzx, mzz, level, 0, visbelow, vb, ab);
+ }
+
+ private void uploadZoneLevelRoof(Scene scene, Zone zone, int mzx, int mzz, int level, int roofId, boolean visbelow, GpuIntBuffer vb, GpuIntBuffer ab)
+ {
+ byte[][][] settings = scene.getExtendedTileSettings();
+ int[][][] roofs = scene.getRoofs();
+ Tile[][][] tiles = scene.getExtendedTiles();
+
+ int offset = scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET >> 3 : 0;
+ this.basex = (mzx - offset) << 10;
+ this.basez = (mzz - offset) << 10;
+
+ for (int xoff = 0; xoff < 8; ++xoff)
+ {
+ for (int zoff = 0; zoff < 8; ++zoff)
{
- uvoffset += len;
+ int msx = (mzx << 3) + xoff;
+ int msz = (mzz << 3) + zoff;
+
+ boolean isbridge = (settings[1][msx][msz] & Constants.TILE_FLAG_BRIDGE) != 0;
+ int maplevel = level;
+ if (isbridge)
+ {
+ ++maplevel;
+ }
+
+ boolean isvisbelow = maplevel <= 3 && (settings[maplevel][msx][msz] & Constants.TILE_FLAG_VIS_BELOW) != 0;
+ if (isvisbelow != visbelow)
+ {
+ continue;
+ }
+
+ int rid;
+ if (isvisbelow || maplevel == 0)
+ {
+ rid = 0;
+ }
+ else
+ {
+ rid = roofs[maplevel - 1][msx][msz];
+ }
+
+ if (rid == roofId)
+ {
+ Tile t = tiles[level][msx][msz];
+ if (t != null)
+ {
+ this.rid = rid;
+ uploadZoneTile(scene, zone, t, vb, ab);
+ }
+ }
}
}
+ }
- WallObject wallObject = tile.getWallObject();
+ private void zoneSize(Zone z, Tile t)
+ {
+ SceneTilePaint paint = t.getSceneTilePaint();
+ if (paint != null)
+ {
+ z.sizeO += 2;
+ }
+
+ SceneTileModel model = t.getSceneTileModel();
+ if (model != null)
+ {
+ z.sizeO += model.getFaceX().length;
+ }
+
+ WallObject wallObject = t.getWallObject();
if (wallObject != null)
{
- Renderable renderable1 = wallObject.getRenderable1();
- if (renderable1 instanceof Model)
+ zoneRenderableSize(z, wallObject.getRenderable1());
+ zoneRenderableSize(z, wallObject.getRenderable2());
+ }
+
+ DecorativeObject decorativeObject = t.getDecorativeObject();
+ if (decorativeObject != null)
+ {
+ zoneRenderableSize(z, decorativeObject.getRenderable());
+ zoneRenderableSize(z, decorativeObject.getRenderable2());
+ }
+
+ GroundObject groundObject = t.getGroundObject();
+ if (groundObject != null)
+ {
+ zoneRenderableSize(z, groundObject.getRenderable());
+ }
+
+ GameObject[] gameObjects = t.getGameObjects();
+ for (GameObject gameObject : gameObjects)
+ {
+ if (gameObject == null)
{
- uploadSceneModel((Model) renderable1, vertexBuffer, uvBuffer);
+ continue;
}
- Renderable renderable2 = wallObject.getRenderable2();
- if (renderable2 instanceof Model)
+ if (!gameObject.getSceneMinLocation().equals(t.getSceneLocation()))
{
- uploadSceneModel((Model) renderable2, vertexBuffer, uvBuffer);
+ continue;
}
+
+ Renderable renderable = gameObject.getRenderable();
+ zoneRenderableSize(z, renderable);
}
- GroundObject groundObject = tile.getGroundObject();
- if (groundObject != null)
+ Tile bridge = t.getBridge();
+ if (bridge != null)
{
- Renderable renderable = groundObject.getRenderable();
- if (renderable instanceof Model)
- {
- uploadSceneModel((Model) renderable, vertexBuffer, uvBuffer);
- }
+ zoneSize(z, bridge);
}
+ }
- DecorativeObject decorativeObject = tile.getDecorativeObject();
- if (decorativeObject != null)
+ private int uploadZoneTile(Scene scene, Zone zone, Tile t, GpuIntBuffer vertexBuffer, GpuIntBuffer ab)
+ {
+ int len = 0;
+ boolean drawTile = renderCallbackManager.drawTile(scene, t);
+
+ SceneTilePaint paint = t.getSceneTilePaint();
+ if (paint != null && drawTile)
+ {
+ Point tilePoint = t.getSceneLocation();
+ len = upload(scene, paint,
+ t.getRenderLevel(), tilePoint.getX(), tilePoint.getY(),
+ vertexBuffer,
+ tilePoint.getX() * 128 - basex, tilePoint.getY() * 128 - basez
+ );
+ }
+
+ SceneTileModel model = t.getSceneTileModel();
+ if (model != null && drawTile)
+ {
+ len += upload(model, basex, basez, vertexBuffer);
+ }
+
+ WallObject wallObject = t.getWallObject();
+ if (wallObject != null && renderCallbackManager.drawObject(scene, wallObject))
+ {
+ Renderable renderable1 = wallObject.getRenderable1();
+ uploadZoneRenderable(renderable1, zone, 0, wallObject.getX(), wallObject.getZ(), wallObject.getY(), -1, -1, -1, -1, wallObject.getId(), vertexBuffer, ab);
+
+ Renderable renderable2 = wallObject.getRenderable2();
+ uploadZoneRenderable(renderable2, zone, 0, wallObject.getX(), wallObject.getZ(), wallObject.getY(), -1, -1, -1, -1, wallObject.getId(), vertexBuffer, ab);
+ }
+
+ DecorativeObject decorativeObject = t.getDecorativeObject();
+ if (decorativeObject != null && renderCallbackManager.drawObject(scene, decorativeObject))
{
Renderable renderable = decorativeObject.getRenderable();
- if (renderable instanceof Model)
- {
- uploadSceneModel((Model) renderable, vertexBuffer, uvBuffer);
- }
+ uploadZoneRenderable(renderable, zone, 0, decorativeObject.getX() + decorativeObject.getXOffset(), decorativeObject.getZ(), decorativeObject.getY() + decorativeObject.getYOffset(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab);
Renderable renderable2 = decorativeObject.getRenderable2();
- if (renderable2 instanceof Model)
- {
- uploadSceneModel((Model) renderable2, vertexBuffer, uvBuffer);
- }
+ uploadZoneRenderable(renderable2, zone, 0, decorativeObject.getX(), decorativeObject.getZ(), decorativeObject.getY(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab);
}
- GameObject[] gameObjects = tile.getGameObjects();
+ GroundObject groundObject = t.getGroundObject();
+ if (groundObject != null && renderCallbackManager.drawObject(scene, groundObject))
+ {
+ Renderable renderable = groundObject.getRenderable();
+ uploadZoneRenderable(renderable, zone, 0, groundObject.getX(), groundObject.getZ(), groundObject.getY(),
+ -1, -1, -1, -1,
+ groundObject.getId(),
+ vertexBuffer, ab);
+ }
+
+ GameObject[] gameObjects = t.getGameObjects();
for (GameObject gameObject : gameObjects)
{
if (gameObject == null)
@@ -221,19 +326,114 @@ private void upload(Scene scene, Tile tile, GpuIntBuffer vertexBuffer, GpuFloatB
continue;
}
+ Point min = gameObject.getSceneMinLocation(), max = gameObject.getSceneMaxLocation();
+
+ if (!min.equals(t.getSceneLocation()))
+ {
+ continue;
+ }
+
+ if (!renderCallbackManager.drawObject(scene, gameObject))
+ {
+ continue;
+ }
+
Renderable renderable = gameObject.getRenderable();
- if (renderable instanceof Model)
+ uploadZoneRenderable(renderable, zone, gameObject.getModelOrientation(), gameObject.getX(), gameObject.getZ(), gameObject.getY(),
+ min.getX(), min.getY(), max.getX(), max.getY(),
+ gameObject.getId(),
+ vertexBuffer, ab);
+ }
+
+ Tile bridge = t.getBridge();
+ if (bridge != null)
+ {
+ len += uploadZoneTile(scene, zone, bridge, vertexBuffer, ab);
+ }
+
+ return len;
+ }
+
+ private void zoneRenderableSize(Zone z, Renderable r)
+ {
+ Model m = null;
+ if (r instanceof Model)
+ {
+ m = (Model) r;
+ }
+ else if (r instanceof DynamicObject)
+ {
+ m = ((DynamicObject) r).getModelZbuf();
+ }
+ if (m == null)
+ {
+ return;
+ }
+
+ byte[] transparencies = m.getFaceTransparencies();
+ int faceCount = m.getFaceCount();
+ if (transparencies != null)
+ {
+ for (int face = 0; face < faceCount; ++face)
{
- uploadSceneModel((Model) gameObject.getRenderable(), vertexBuffer, uvBuffer);
+ boolean alpha = transparencies[face] != 0;
+ if (alpha)
+ {
+ z.sizeA++;
+ }
+ else
+ {
+ z.sizeO++;
+ }
}
+ return;
}
+ z.sizeO += faceCount;
}
- int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer,
- int lx, int lz, boolean stream)
+ private void uploadZoneRenderable(Renderable r, Zone zone, int orient, int x, int y, int z, int lx, int lz, int ux, int uz, int id, GpuIntBuffer vb, GpuIntBuffer ab)
{
- tileX += GpuPlugin.SCENE_OFFSET;
- tileY += GpuPlugin.SCENE_OFFSET;
+ int pos = zone.vboA != null ? zone.vboA.vb.position() : 0;
+ Model model = null;
+ if (r instanceof Model)
+ {
+ model = (Model) r;
+ uploadStaticModel(model, orient, x - basex, y, z - basez, vb, ab);
+ }
+ else if (r instanceof DynamicObject)
+ {
+ model = ((DynamicObject) r).getModelZbuf();
+ if (model != null)
+ {
+ uploadStaticModel(model, orient, x - basex, y, z - basez, vb, ab);
+ }
+ }
+ int endpos = zone.vboA != null ? zone.vboA.vb.position() : 0;
+ if (endpos > pos)
+ {
+ assert model != null;
+ if (lx > -1)
+ {
+ lx -= basex >> 7;
+ lz -= basez >> 7;
+ ux -= basex >> 7;
+ uz -= basez >> 7;
+ assert lx >= 0 : lx;
+ assert lz >= 0 : lz;
+ assert ux < 25 : ux; // largest object?
+ assert uz < 25 : uz;
+ }
+ zone.addAlphaModel(zone.glVaoA, model, pos, endpos,
+ x - basex, y, z - basez,
+ lx, lz, ux, uz,
+ rid, level, id);
+ }
+ }
+
+ private int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, GpuIntBuffer vertexBuffer, int lx, int lz)
+ {
+ tileX += scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET : 0;
+ tileY += scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET : 0;
final int[][][] tileHeights = scene.getTileHeights();
final int swHeight = tileHeights[tileZ][tileX][tileY];
@@ -251,9 +451,6 @@ int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, Gp
return 0;
}
- vertexBuffer.ensureCapacity(24);
- uvBuffer.ensureCapacity(24);
-
// 0,0
final int lx0 = lx;
final int ly0 = swHeight;
@@ -278,40 +475,52 @@ int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, Gp
final int lz3 = lz + Perspective.LOCAL_TILE_SIZE;
final int hsl3 = nwColor;
- vertexBuffer.put((float) lx2, ly2, lz2, hsl2);
- vertexBuffer.put((float) lx3, ly3, lz3, hsl3);
- vertexBuffer.put((float) lx1, ly1, lz1, hsl1);
+ int tex = tile.getTexture() + 1;
- vertexBuffer.put((float) lx0, ly0, lz0, hsl0);
- vertexBuffer.put((float) lx1, ly1, lz1, hsl1);
- vertexBuffer.put((float) lx3, ly3, lz3, hsl3);
+ vertexBuffer.put22224(lx2, ly2, lz2, hsl2);
+ if (tile.isFlat())
+ {
+ vertexBuffer.put2222(tex, lx0 - lx2, ly0 - ly2, lz0 - lz2);
+ }
+ else
+ {
+ vertexBuffer.put2222(tex, lx2 - lx2, ly2 - ly2, lz2 - lz2);
+ }
- if (stream || tile.getTexture() != -1)
+ vertexBuffer.put22224(lx3, ly3, lz3, hsl3);
+ if (tile.isFlat())
{
- int tex = tile.getTexture() + 1;
- if (tile.isFlat())
- {
- uvBuffer.put(tex, lx0, ly0, lz0);
- uvBuffer.put(tex, lx1, ly1, lz1);
- uvBuffer.put(tex, lx3, ly3, lz3);
- }
- else
- {
- uvBuffer.put(tex, lx2, ly2, lz2);
- uvBuffer.put(tex, lx3, ly3, lz3);
- uvBuffer.put(tex, lx1, ly1, lz1);
- }
+ vertexBuffer.put2222(tex, lx1 - lx3, ly1 - ly3, lz1 - lz3);
+ }
+ else
+ {
+ vertexBuffer.put2222(tex, lx3 - lx3, ly3 - ly3, lz3 - lz3);
+ }
+
- uvBuffer.put(tex, lx0, ly0, lz0);
- uvBuffer.put(tex, lx1, ly1, lz1);
- uvBuffer.put(tex, lx3, ly3, lz3);
+ vertexBuffer.put22224(lx1, ly1, lz1, hsl1);
+ if (tile.isFlat())
+ {
+ vertexBuffer.put2222(tex, lx3 - lx1, ly3 - ly1, lz3 - lz1);
+ }
+ else
+ {
+ vertexBuffer.put2222(tex, lx1 - lx1, ly1 - ly1, lz1 - lz1);
}
+ vertexBuffer.put22224(lx0, ly0, lz0, hsl0);
+ vertexBuffer.put2222(tex, lx0 - lx0, ly0 - ly0, lz0 - lz0);
+
+ vertexBuffer.put22224(lx1, ly1, lz1, hsl1);
+ vertexBuffer.put2222(tex, lx1 - lx1, ly1 - ly1, lz1 - lz1);
+
+ vertexBuffer.put22224(lx3, ly3, lz3, hsl3);
+ vertexBuffer.put2222(tex, lx3 - lx3, ly3 - ly3, lz3 - lz3);
+
return 6;
}
- int upload(SceneTileModel sceneTileModel, int lx, int lz,
- GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer, boolean stream)
+ private int upload(SceneTileModel sceneTileModel, int lx, int lz, GpuIntBuffer vertexBuffer)
{
final int[] faceX = sceneTileModel.getFaceX();
final int[] faceY = sceneTileModel.getFaceY();
@@ -329,9 +538,6 @@ int upload(SceneTileModel sceneTileModel, int lx, int lz,
final int faceCount = faceX.length;
- vertexBuffer.ensureCapacity(faceCount * 12);
- uvBuffer.ensureCapacity(faceCount * 12);
-
int cnt = 0;
for (int i = 0; i < faceCount; ++i)
{
@@ -363,80 +569,46 @@ int upload(SceneTileModel sceneTileModel, int lx, int lz,
int ly2 = vertexY[vertex2];
int lz2 = vertexZ[vertex2] - lz;
- vertexBuffer.put((float) lx0, ly0, lz0, hsl0);
- vertexBuffer.put((float) lx1, ly1, lz1, hsl1);
- vertexBuffer.put((float) lx2, ly2, lz2, hsl2);
-
- if (stream || triangleTextures != null)
+ int tex = triangleTextures != null ? triangleTextures[i] + 1 : 0;
+ vertexBuffer.put22224(lx0, ly0, lz0, hsl0);
+ if (sceneTileModel.isFlat())
{
- if (triangleTextures != null && triangleTextures[i] != -1)
- {
- int tex = triangleTextures[i] + 1;
- if (sceneTileModel.isFlat())
- {
- uvBuffer.put(tex, vertexX[0] - lx, vertexY[0], vertexZ[0] - lz);
- uvBuffer.put(tex, vertexX[1] - lx, vertexY[1], vertexZ[1] - lz);
- uvBuffer.put(tex, vertexX[3] - lx, vertexY[3], vertexZ[3] - lz);
- }
- else
- {
- uvBuffer.put(tex, vertexX[vertex0] - lx, vertexY[vertex0], vertexZ[vertex0] - lz);
- uvBuffer.put(tex, vertexX[vertex1] - lx, vertexY[vertex1], vertexZ[vertex1] - lz);
- uvBuffer.put(tex, vertexX[vertex2] - lx, vertexY[vertex2], vertexZ[vertex2] - lz);
- }
- }
- else
- {
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- }
+ vertexBuffer.put2222(tex, vertexX[0] - lx - lx0, vertexY[0] - ly0, vertexZ[0] - lz - lz0);
+ }
+ else
+ {
+ vertexBuffer.put2222(tex, vertexX[vertex0] - lx - lx0, vertexY[vertex0] - ly0, vertexZ[vertex0] - lz - lz0);
}
- }
-
- return cnt;
- }
-
- private void uploadSceneModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
- {
- // deduplicate hillskewed models
- if (model.getUnskewedModel() != null)
- {
- model = model.getUnskewedModel();
- }
- if (model.getSceneId() == sceneId)
- {
- return; // model has already been uploaded
- }
+ vertexBuffer.put22224(lx1, ly1, lz1, hsl1);
+ if (sceneTileModel.isFlat())
+ {
+ vertexBuffer.put2222(tex, vertexX[1] - lx - lx1, vertexY[1] - ly1, vertexZ[1] - lz - lz1);
+ }
+ else
+ {
+ vertexBuffer.put2222(tex, vertexX[vertex1] - lx - lx1, vertexY[vertex1] - ly1, vertexZ[vertex1] - lz - lz1);
+ }
- model.setBufferOffset(offset);
- if (model.getFaceTextures() != null)
- {
- model.setUvBufferOffset(uvoffset);
- }
- else
- {
- model.setUvBufferOffset(-1);
+ vertexBuffer.put22224(lx2, ly2, lz2, hsl2);
+ if (sceneTileModel.isFlat())
+ {
+ vertexBuffer.put2222(tex, vertexX[3] - lx - lx2, vertexY[3] - ly2, vertexZ[3] - lz - lz2);
+ }
+ else
+ {
+ vertexBuffer.put2222(tex, vertexX[vertex2] - lx - lx2, vertexY[vertex2] - ly2, vertexZ[vertex2] - lz - lz2);
+ }
}
- model.setSceneId(sceneId);
- ++uniqueModels;
-
- int len = pushModel(model, vertexBuffer, uvBuffer);
- offset += len;
- if (model.getFaceTextures() != null)
- {
- uvoffset += len;
- }
+ return cnt;
}
- public int pushModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
+ // scene upload
+ private int uploadStaticModel(Model model, int orient, int x, int y, int z, GpuIntBuffer vb, GpuIntBuffer ab)
{
- final int triangleCount = Math.min(model.getFaceCount(), GpuPlugin.MAX_TRIANGLE);
-
- vertexBuffer.ensureCapacity(triangleCount * 12);
- uvBuffer.ensureCapacity(triangleCount * 12);
+ final int vertexCount = model.getVerticesCount();
+ final int triangleCount = model.getFaceCount();
final float[] vertexX = model.getVerticesX();
final float[] vertexY = model.getVerticesY();
@@ -457,12 +629,37 @@ public int pushModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBu
final int[] texIndices3 = model.getTexIndices3();
final byte[] transparencies = model.getFaceTransparencies();
- final byte[] facePriorities = model.getFaceRenderPriorities();
+ final byte[] bias = model.getFaceBias();
- final byte overrideAmount = model.getOverrideAmount();
- final byte overrideHue = model.getOverrideHue();
- final byte overrideSat = model.getOverrideSaturation();
- final byte overrideLum = model.getOverrideLuminance();
+ int orientSin = 0;
+ int orientCos = 0;
+ if (orient != 0)
+ {
+ orientSin = Perspective.SINE[orient];
+ orientCos = Perspective.COSINE[orient];
+ }
+
+ for (int v = 0; v < vertexCount; ++v)
+ {
+ int vx = (int) vertexX[v];
+ int vy = (int) vertexY[v];
+ int vz = (int) vertexZ[v];
+
+ if (orient != 0)
+ {
+ int x0 = vx;
+ vx = vz * orientSin + x0 * orientCos >> 16;
+ vz = vz * orientCos - x0 * orientSin >> 16;
+ }
+
+ vx += x;
+ vy += y;
+ vz += z;
+
+ modelLocalXI[v] = vx;
+ modelLocalYI[v] = vy;
+ modelLocalZI[v] = vz;
+ }
int len = 0;
for (int face = 0; face < triangleCount; ++face)
@@ -471,167 +668,100 @@ public int pushModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBu
int color2 = color2s[face];
int color3 = color3s[face];
+ boolean alpha = (transparencies != null && transparencies[face] != 0);
+
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, 0);
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- }
-
- len += 3;
continue;
}
- // HSL override is not applied to textured faces
- if (faceTextures == null || faceTextures[face] == -1)
- {
- if (overrideAmount > 0)
- {
- color1 = interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount);
- color2 = interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount);
- color3 = interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount);
- }
- }
-
- int packAlphaPriority = packAlphaPriority(faceTextures, transparencies, facePriorities, face);
-
int triangleA = indices1[face];
int triangleB = indices2[face];
int triangleC = indices3[face];
- vertexBuffer.put(vertexX[triangleA], vertexY[triangleA], vertexZ[triangleA], packAlphaPriority | color1);
- vertexBuffer.put(vertexX[triangleB], vertexY[triangleB], vertexZ[triangleB], packAlphaPriority | color2);
- vertexBuffer.put(vertexX[triangleC], vertexY[triangleC], vertexZ[triangleC], packAlphaPriority | color3);
-
- if (faceTextures != null)
- {
- if (faceTextures[face] != -1)
- {
- int texA, texB, texC;
-
- if (textureFaces != null && textureFaces[face] != -1)
- {
- int tface = textureFaces[face] & 0xff;
- texA = texIndices1[tface];
- texB = texIndices2[tface];
- texC = texIndices3[tface];
- }
- else
- {
- texA = triangleA;
- texB = triangleB;
- texC = triangleC;
- }
-
- int texture = faceTextures[face] + 1;
- uvBuffer.put(texture, vertexX[texA], vertexY[texA], vertexZ[texA]);
- uvBuffer.put(texture, vertexX[texB], vertexY[texB], vertexZ[texB]);
- uvBuffer.put(texture, vertexX[texC], vertexY[texC], vertexZ[texC]);
- }
- else
- {
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- }
- }
-
- len += 3;
- }
-
- return len;
- }
+ int vx1 = modelLocalXI[triangleA];
+ int vy1 = modelLocalYI[triangleA];
+ int vz1 = modelLocalZI[triangleA];
- private static int[] distances;
- private static char[] distanceFaceCount;
- private static char[][] distanceToFaces;
+ int vx2 = modelLocalXI[triangleB];
+ int vy2 = modelLocalYI[triangleB];
+ int vz2 = modelLocalZI[triangleB];
- private static float[] modelCanvasX;
- private static float[] modelCanvasY;
+ int vx3 = modelLocalXI[triangleC];
+ int vy3 = modelLocalYI[triangleC];
+ int vz3 = modelLocalZI[triangleC];
- private static float[] modelLocalX;
- private static float[] modelLocalY;
- private static float[] modelLocalZ;
+ int texA, texB, texC;
- private static int[] numOfPriority;
- private static int[] eq10;
- private static int[] eq11;
- private static int[] lt10;
- private static int[][] orderedFaces;
+ if (textureFaces != null && textureFaces[face] != -1)
+ {
+ int tface = textureFaces[face] & 0xff;
+ texA = texIndices1[tface];
+ texB = texIndices2[tface];
+ texC = texIndices3[tface];
+ }
+ else
+ {
+ texA = triangleA;
+ texB = triangleB;
+ texC = triangleC;
+ }
- void initSortingBuffers()
- {
- int MAX_VERTEX_COUNT = 6500;
- int MAX_DIAMETER = 6000;
- int ZSORT_GROUP_SIZE = 1024; // was 512
- int MAX_FACES_PER_PRIORITY = 4000; // was 2000
+ int alphaBias = 0;
+ alphaBias |= transparencies != null ? (transparencies[face] & 0xff) << 24 : 0;
+ alphaBias |= bias != null ? (bias[face] & 0xff) << 16 : 0;
+ int texture = faceTextures != null ? faceTextures[face] + 1 : 0;
+ GpuIntBuffer buf = alpha ? ab : vb;
- distances = new int[MAX_VERTEX_COUNT];
- distanceFaceCount = new char[MAX_DIAMETER];
- distanceToFaces = new char[MAX_DIAMETER][ZSORT_GROUP_SIZE];
+ buf.put22224(vx1, vy1, vz1, alphaBias | color1);
+ buf.put2222(texture, modelLocalXI[texA] - vx1, modelLocalYI[texA] - vy1, modelLocalZI[texA] - vz1);
- modelCanvasX = new float[MAX_VERTEX_COUNT];
- modelCanvasY = new float[MAX_VERTEX_COUNT];
+ buf.put22224(vx2, vy2, vz2, alphaBias | color2);
+ buf.put2222(texture, modelLocalXI[texB] - vx2, modelLocalYI[texB] - vy2, modelLocalZI[texB] - vz2);
- modelLocalX = new float[MAX_VERTEX_COUNT];
- modelLocalY = new float[MAX_VERTEX_COUNT];
- modelLocalZ = new float[MAX_VERTEX_COUNT];
+ buf.put22224(vx3, vy3, vz3, alphaBias | color3);
+ buf.put2222(texture, modelLocalXI[texC] - vx3, modelLocalYI[texC] - vy3, modelLocalZI[texC] - vz3);
- numOfPriority = new int[12];
- eq10 = new int[MAX_FACES_PER_PRIORITY];
- eq11 = new int[MAX_FACES_PER_PRIORITY];
- lt10 = new int[12];
- orderedFaces = new int[12][MAX_FACES_PER_PRIORITY];
- }
+ len += 3;
+ }
- void releaseSortingBuffers()
- {
- distances = null;
- distanceFaceCount = null;
- distanceToFaces = null;
-
- modelCanvasX = null;
- modelCanvasY = null;
-
- modelLocalX = null;
- modelLocalY = null;
- modelLocalZ = null;
-
- numOfPriority = null;
- eq10 = null;
- eq11 = null;
- lt10 = null;
- orderedFaces = null;
+ return len;
}
- int pushSortedModel(Projection proj, Model model, int orientation, int x, int y, int z, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
+ // temp draw
+ static int uploadTempModel(Model model, int orientation, int x, int y, int z, IntBuffer opaqueBuffer)
{
+ final int triangleCount = model.getFaceCount();
final int vertexCount = model.getVerticesCount();
+
final float[] verticesX = model.getVerticesX();
final float[] verticesY = model.getVerticesY();
final float[] verticesZ = model.getVerticesZ();
- final int faceCount = model.getFaceCount();
final int[] indices1 = model.getFaceIndices1();
final int[] indices2 = model.getFaceIndices2();
final int[] indices3 = model.getFaceIndices3();
- final int[] faceColors3 = model.getFaceColors3();
- final byte[] faceRenderPriorities = model.getFaceRenderPriorities();
+ final int[] color1s = model.getFaceColors1();
+ final int[] color2s = model.getFaceColors2();
+ final int[] color3s = model.getFaceColors3();
- final int centerX = client.getCenterX();
- final int centerY = client.getCenterY();
- final int zoom = client.get3dZoom();
+ final short[] faceTextures = model.getFaceTextures();
+ final byte[] textureFaces = model.getTextureFaces();
+ final int[] texIndices1 = model.getTexIndices1();
+ final int[] texIndices2 = model.getTexIndices2();
+ final int[] texIndices3 = model.getTexIndices3();
+
+ final byte[] bias = model.getFaceBias();
+
+ final byte overrideAmount = model.getOverrideAmount();
+ final byte overrideHue = model.getOverrideHue();
+ final byte overrideSat = model.getOverrideSaturation();
+ final byte overrideLum = model.getOverrideLuminance();
float orientSine = 0;
float orientCosine = 0;
@@ -641,9 +771,6 @@ int pushSortedModel(Projection proj, Model model, int orientation, int x, int y,
orientCosine = Perspective.COSINE[orientation] / 65536f;
}
- float[] p = proj.project(x, y, z);
- int zero = (int) p[2];
-
for (int v = 0; v < vertexCount; ++v)
{
float vertexX = verticesX[v];
@@ -657,7 +784,6 @@ int pushSortedModel(Projection proj, Model model, int orientation, int x, int y,
vertexZ = vertexZ * orientCosine - x0 * orientSine;
}
- // move to local position
vertexX += x;
vertexY += y;
vertexZ += z;
@@ -665,354 +791,123 @@ int pushSortedModel(Projection proj, Model model, int orientation, int x, int y,
modelLocalX[v] = vertexX;
modelLocalY[v] = vertexY;
modelLocalZ[v] = vertexZ;
-
- p = proj.project(vertexX, vertexY, vertexZ);
- modelCanvasX[v] = centerX + p[0] * zoom / p[2];
- modelCanvasY[v] = centerY + p[1] * zoom / p[2];
- distances[v] = (int) p[2] - zero;
}
- final int diameter = model.getDiameter();
- final int radius = model.getRadius();
- if (diameter >= 6000)
+ int len = 0;
+ for (int face = 0; face < triangleCount; ++face)
{
- return 0;
- }
-
- Arrays.fill(distanceFaceCount, 0, diameter, (char) 0);
+ int color1 = color1s[face];
+ int color2 = color2s[face];
+ int color3 = color3s[face];
- for (char i = 0; i < faceCount; ++i)
- {
- if (faceColors3[i] != -2)
+ if (color3 == -1)
{
- final int v1 = indices1[i];
- final int v2 = indices2[i];
- final int v3 = indices3[i];
-
- final float
- aX = modelCanvasX[v1],
- aY = modelCanvasY[v1],
- bX = modelCanvasX[v2],
- bY = modelCanvasY[v2],
- cX = modelCanvasX[v3],
- cY = modelCanvasY[v3];
-
- if ((aX - bX) * (cY - bY) - (cX - bX) * (aY - bY) > 0)
- {
- int distance = radius + (distances[v1] + distances[v2] + distances[v3]) / 3;
- assert distance >= 0 && distance < diameter;
- distanceToFaces[distance][distanceFaceCount[distance]++] = i;
- }
+ color2 = color3 = color1;
}
- }
-
- vertexBuffer.ensureCapacity(12 * faceCount);
- uvBuffer.ensureCapacity(12 * faceCount);
-
- int len = 0;
- if (faceRenderPriorities == null)
- {
- for (int i = diameter - 1; i >= 0; --i)
+ else if (color3 == -2)
{
- final int cnt = distanceFaceCount[i];
- if (cnt > 0)
- {
- final char[] faces = distanceToFaces[i];
-
- for (int faceIdx = 0; faceIdx < cnt; ++faceIdx)
- {
- final int face = faces[faceIdx];
- len += pushFace(model, face, vertexBuffer, uvBuffer);
- }
- }
+ continue;
}
- }
- else
- {
- Arrays.fill(numOfPriority, 0);
- Arrays.fill(lt10, 0);
- for (int i = diameter - 1; i >= 0; --i)
+ // HSL override is not applied to textured faces
+ if (faceTextures == null || faceTextures[face] == -1)
{
- final int cnt = distanceFaceCount[i];
- if (cnt > 0)
+ if (overrideAmount > 0)
{
- final char[] faces = distanceToFaces[i];
-
- for (int faceIdx = 0; faceIdx < cnt; ++faceIdx)
- {
- final int face = faces[faceIdx];
- final byte pri = faceRenderPriorities[face];
- final int distIdx = numOfPriority[pri]++;
-
- orderedFaces[pri][distIdx] = face;
- if (pri < 10)
- {
- lt10[pri] += i;
- }
- else if (pri == 10)
- {
- eq10[distIdx] = i;
- }
- else
- {
- eq11[distIdx] = i;
- }
- }
+ color1 = interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount);
+ color2 = interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount);
+ color3 = interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount);
}
}
- int avg12 = 0;
- if (numOfPriority[1] > 0 || numOfPriority[2] > 0)
- {
- avg12 = (lt10[1] + lt10[2]) / (numOfPriority[1] + numOfPriority[2]);
- }
+ int triangleA = indices1[face];
+ int triangleB = indices2[face];
+ int triangleC = indices3[face];
- int avg34 = 0;
- if (numOfPriority[3] > 0 || numOfPriority[4] > 0)
- {
- avg34 = (lt10[3] + lt10[4]) / (numOfPriority[3] + numOfPriority[4]);
- }
+ float vx1 = modelLocalX[triangleA];
+ float vy1 = modelLocalY[triangleA];
+ float vz1 = modelLocalZ[triangleA];
- int avg68 = 0;
- if (numOfPriority[6] > 0 || numOfPriority[8] > 0)
- {
- avg68 = (lt10[8] + lt10[6]) / (numOfPriority[8] + numOfPriority[6]);
- }
+ float vx2 = modelLocalX[triangleB];
+ float vy2 = modelLocalY[triangleB];
+ float vz2 = modelLocalZ[triangleB];
- int drawnFaces = 0;
- int numDynFaces = numOfPriority[10];
- int[] dynFaces = orderedFaces[10];
- int[] dynFaceDistances = eq10;
- if (drawnFaces == numDynFaces)
- {
- drawnFaces = 0;
- numDynFaces = numOfPriority[11];
- dynFaces = orderedFaces[11];
- dynFaceDistances = eq11;
- }
+ float vx3 = modelLocalX[triangleC];
+ float vy3 = modelLocalY[triangleC];
+ float vz3 = modelLocalZ[triangleC];
- int currFaceDistance;
- if (drawnFaces < numDynFaces)
+ int texA, texB, texC;
+
+ if (textureFaces != null && textureFaces[face] != -1)
{
- currFaceDistance = dynFaceDistances[drawnFaces];
+ int tface = textureFaces[face] & 0xff;
+ texA = texIndices1[tface];
+ texB = texIndices2[tface];
+ texC = texIndices3[tface];
}
else
{
- currFaceDistance = -1000;
+ texA = triangleA;
+ texB = triangleB;
+ texC = triangleC;
}
- for (int pri = 0; pri < 10; ++pri)
- {
- while (pri == 0 && currFaceDistance > avg12)
- {
- final int face = dynFaces[drawnFaces++];
- len += pushFace(model, face, vertexBuffer, uvBuffer);
+ int alphaBias = 0;
+ alphaBias |= bias != null ? (bias[face] & 0xff) << 16 : 0;
+ int texture = faceTextures != null ? faceTextures[face] + 1 : 0;
- if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
- {
- drawnFaces = 0;
- numDynFaces = numOfPriority[11];
- dynFaces = orderedFaces[11];
- dynFaceDistances = eq11;
- }
+ putfff4(opaqueBuffer, vx1, vy1, vz1, alphaBias | color1);
+ put2222(opaqueBuffer, texture, (int) modelLocalX[texA] - (int) vx1, (int) modelLocalY[texA] - (int) vy1, (int) modelLocalZ[texA] - (int) vz1);
- if (drawnFaces < numDynFaces)
- {
- currFaceDistance = dynFaceDistances[drawnFaces];
- }
- else
- {
- currFaceDistance = -1000;
- }
- }
-
- while (pri == 3 && currFaceDistance > avg34)
- {
- final int face = dynFaces[drawnFaces++];
- len += pushFace(model, face, vertexBuffer, uvBuffer);
-
- if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
- {
- drawnFaces = 0;
- numDynFaces = numOfPriority[11];
- dynFaces = orderedFaces[11];
- dynFaceDistances = eq11;
- }
-
- if (drawnFaces < numDynFaces)
- {
- currFaceDistance = dynFaceDistances[drawnFaces];
- }
- else
- {
- currFaceDistance = -1000;
- }
- }
-
- while (pri == 5 && currFaceDistance > avg68)
- {
- final int face = dynFaces[drawnFaces++];
- len += pushFace(model, face, vertexBuffer, uvBuffer);
-
- if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
- {
- drawnFaces = 0;
- numDynFaces = numOfPriority[11];
- dynFaces = orderedFaces[11];
- dynFaceDistances = eq11;
- }
-
- if (drawnFaces < numDynFaces)
- {
- currFaceDistance = dynFaceDistances[drawnFaces];
- }
- else
- {
- currFaceDistance = -1000;
- }
- }
-
- final int priNum = numOfPriority[pri];
- final int[] priFaces = orderedFaces[pri];
-
- for (int faceIdx = 0; faceIdx < priNum; ++faceIdx)
- {
- final int face = priFaces[faceIdx];
- len += pushFace(model, face, vertexBuffer, uvBuffer);
- }
- }
+ putfff4(opaqueBuffer, vx2, vy2, vz2, alphaBias | color2);
+ put2222(opaqueBuffer, texture, (int) modelLocalX[texB] - (int) vx2, (int) modelLocalY[texB] - (int) vy2, (int) modelLocalZ[texB] - (int) vz2);
- while (currFaceDistance != -1000)
- {
- final int face = dynFaces[drawnFaces++];
- len += pushFace(model, face, vertexBuffer, uvBuffer);
-
- if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11])
- {
- drawnFaces = 0;
- dynFaces = orderedFaces[11];
- numDynFaces = numOfPriority[11];
- dynFaceDistances = eq11;
- }
+ putfff4(opaqueBuffer, vx3, vy3, vz3, alphaBias | color3);
+ put2222(opaqueBuffer, texture, (int) modelLocalX[texC] - (int) vx3, (int) modelLocalY[texC] - (int) vy3, (int) modelLocalZ[texC] - (int) vz3);
- if (drawnFaces < numDynFaces)
- {
- currFaceDistance = dynFaceDistances[drawnFaces];
- }
- else
- {
- currFaceDistance = -1000;
- }
- }
+ len += 3;
}
return len;
}
- private int pushFace(Model model, int face, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
+ static void put2222(IntBuffer vb, int x, int y, int z, int w)
{
- final int[] indices1 = model.getFaceIndices1();
- final int[] indices2 = model.getFaceIndices2();
- final int[] indices3 = model.getFaceIndices3();
-
- final int[] faceColors1 = model.getFaceColors1();
- final int[] faceColors2 = model.getFaceColors2();
- final int[] faceColors3 = model.getFaceColors3();
-
- final byte overrideAmount = model.getOverrideAmount();
- final byte overrideHue = model.getOverrideHue();
- final byte overrideSat = model.getOverrideSaturation();
- final byte overrideLum = model.getOverrideLuminance();
-
- final short[] faceTextures = model.getFaceTextures();
- final byte[] textureFaces = model.getTextureFaces();
- final int[] texIndices1 = model.getTexIndices1();
- final int[] texIndices2 = model.getTexIndices2();
- final int[] texIndices3 = model.getTexIndices3();
-
- final byte[] faceRenderPriorities = model.getFaceRenderPriorities();
- final byte[] transparencies = model.getFaceTransparencies();
-
- final int packAlphaPriority = packAlphaPriority(faceTextures, transparencies, faceRenderPriorities, face);
-
- final int triangleA = indices1[face];
- final int triangleB = indices2[face];
- final int triangleC = indices3[face];
-
- int color1 = faceColors1[face];
- int color2 = faceColors2[face];
- int color3 = faceColors3[face];
+ vb.put(((y & 0xffff) << 16) | (x & 0xffff));
+ vb.put(((w & 0xffff) << 16) | (z & 0xffff));
+ }
- if (color3 == -1)
- {
- color2 = color3 = color1;
- }
+ static void putfff4(IntBuffer vb, float x, float y, float z, int w)
+ {
+ vb.put(Float.floatToIntBits(x));
+ vb.put(Float.floatToIntBits(y));
+ vb.put(Float.floatToIntBits(z));
+ vb.put(w);
+ }
- // HSL override is not applied to textured faces
- if (faceTextures == null || faceTextures[face] == -1)
- {
- if (overrideAmount > 0)
- {
- color1 = interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount);
- color2 = interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount);
- color3 = interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount);
- }
- }
+ static float[] modelLocalX;
+ static float[] modelLocalY;
+ static float[] modelLocalZ;
- vertexBuffer.put(modelLocalX[triangleA], modelLocalY[triangleA], modelLocalZ[triangleA], packAlphaPriority | color1);
- vertexBuffer.put(modelLocalX[triangleB], modelLocalY[triangleB], modelLocalZ[triangleB], packAlphaPriority | color2);
- vertexBuffer.put(modelLocalX[triangleC], modelLocalY[triangleC], modelLocalZ[triangleC], packAlphaPriority | color3);
+ // uploadStaticModel runs on the maploader thread, so requires its own buffers
+ private final static int[] modelLocalXI;
+ private final static int[] modelLocalYI;
+ private final static int[] modelLocalZI;
- if (faceTextures != null && faceTextures[face] != -1)
- {
- int texA, texB, texC;
+ static final int MAX_VERTEX_COUNT = 6500;
- if (textureFaces != null && textureFaces[face] != -1)
- {
- int tfaceIdx = textureFaces[face] & 0xff;
- texA = texIndices1[tfaceIdx];
- texB = texIndices2[tfaceIdx];
- texC = texIndices3[tfaceIdx];
- }
- else
- {
- texA = triangleA;
- texB = triangleB;
- texC = triangleC;
- }
-
- int texture = faceTextures[face] + 1;
- uvBuffer.put(texture, modelLocalX[texA], modelLocalY[texA], modelLocalZ[texA]);
- uvBuffer.put(texture, modelLocalX[texB], modelLocalY[texB], modelLocalZ[texB]);
- uvBuffer.put(texture, modelLocalX[texC], modelLocalY[texC], modelLocalZ[texC]);
- }
- else
- {
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- uvBuffer.put(0, 0, 0, 0);
- }
-
- return 3;
- }
-
- private static int packAlphaPriority(short[] faceTextures, byte[] faceTransparencies, byte[] facePriorities, int face)
+ static
{
- int alpha = 0;
- if (faceTransparencies != null)
- {
- alpha = (faceTransparencies[face] & 0xFF) << 24;
- }
- int priority = 0;
- if (facePriorities != null)
- {
- priority = (facePriorities[face] & 0xff) << 16;
- }
- return alpha | priority;
+ modelLocalX = new float[MAX_VERTEX_COUNT];
+ modelLocalY = new float[MAX_VERTEX_COUNT];
+ modelLocalZ = new float[MAX_VERTEX_COUNT];
+
+ modelLocalXI = new int[MAX_VERTEX_COUNT];
+ modelLocalYI = new int[MAX_VERTEX_COUNT];
+ modelLocalZI = new int[MAX_VERTEX_COUNT];
}
- private static int interpolateHSL(int hsl, byte hue2, byte sat2, byte lum2, byte lerp)
+ static int interpolateHSL(int hsl, byte hue2, byte sat2, byte lum2, byte lerp)
{
int hue = hsl >> 10 & 63;
int sat = hsl >> 7 & 7;
@@ -1035,64 +930,4 @@ private static int interpolateHSL(int hsl, byte hue2, byte sat2, byte lum2, byte
return (hue << 10 | sat << 7 | lum) & 65535;
}
-
- // remove tiles from the scene that are outside the current region
- private void prepare(Scene scene)
- {
- if (scene.isInstance() || !gpuConfig.hideUnrelatedMaps())
- {
- return;
- }
-
- int baseX = scene.getBaseX() / 8;
- int baseY = scene.getBaseY() / 8;
- int centerX = baseX + 6;
- int centerY = baseY + 6;
- int centerId = regions.getRegionId(centerX, centerY);
-
- int r = Constants.EXTENDED_SCENE_SIZE / 16;
- for (int offx = -r; offx <= r; ++offx)
- {
- for (int offy = -r; offy <= r; ++offy)
- {
- int cx = centerX + offx;
- int cy = centerY + offy;
- int id = regions.getRegionId(cx, cy);
- if (id != centerId)
- {
- removeChunk(scene, cx, cy);
- }
- }
- }
- }
-
- private static void removeChunk(Scene scene, int cx, int cy)
- {
- int wx = cx * 8;
- int wy = cy * 8;
- int sx = wx - scene.getBaseX();
- int sy = wy - scene.getBaseY();
- int cmsx = sx + GpuPlugin.SCENE_OFFSET;
- int cmsy = sy + GpuPlugin.SCENE_OFFSET;
- Tile[][][] tiles = scene.getExtendedTiles();
- for (int x = 0; x < 8; ++x)
- {
- for (int y = 0; y < 8; ++y)
- {
- int msx = cmsx + x;
- int msy = cmsy + y;
- if (msx >= 0 && msx < Constants.EXTENDED_SCENE_SIZE && msy >= 0 && msy < Constants.EXTENDED_SCENE_SIZE)
- {
- for (int z = 0; z < Constants.MAX_Z; ++z)
- {
- Tile tile = tiles[z][msx][msy];
- if (tile != null)
- {
- scene.removeTile(tile);
- }
- }
- }
- }
- }
- }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java
index 559c3ccc0a8..72617a68c90 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java
@@ -30,10 +30,12 @@
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import net.runelite.client.plugins.gpu.template.Template;
-import org.lwjgl.opengl.GL43C;
+import static org.lwjgl.opengl.GL43C.*;
-public class Shader
+@Slf4j
+class Shader
{
@VisibleForTesting
final List units = new ArrayList<>();
@@ -55,9 +57,9 @@ public Shader add(int type, String name)
return this;
}
- public int compile(Template template) throws ShaderException
+ int compile(Template template) throws ShaderException
{
- int program = GL43C.glCreateProgram();
+ int program = glCreateProgram();
int[] shaders = new int[units.size()];
int i = 0;
boolean ok = false;
@@ -66,39 +68,40 @@ public int compile(Template template) throws ShaderException
while (i < shaders.length)
{
Unit unit = units.get(i);
- int shader = GL43C.glCreateShader(unit.type);
+ int shader = glCreateShader(unit.type);
if (shader == 0)
{
throw new ShaderException("Unable to create shader of type " + unit.type);
}
String source = template.load(unit.filename);
- GL43C.glShaderSource(shader, source);
- GL43C.glCompileShader(shader);
+ glShaderSource(shader, source);
+ glCompileShader(shader);
- if (GL43C.glGetShaderi(shader, GL43C.GL_COMPILE_STATUS) != GL43C.GL_TRUE)
+ if (glGetShaderi(shader, GL_COMPILE_STATUS) != GL_TRUE)
{
- String err = GL43C.glGetShaderInfoLog(shader);
- GL43C.glDeleteShader(shader);
+ String err = glGetShaderInfoLog(shader);
+ glDeleteShader(shader);
+ logShaderSource(source);
throw new ShaderException(err);
}
- GL43C.glAttachShader(program, shader);
+ glAttachShader(program, shader);
shaders[i++] = shader;
}
- GL43C.glLinkProgram(program);
+ glLinkProgram(program);
- if (GL43C.glGetProgrami(program, GL43C.GL_LINK_STATUS) == GL43C.GL_FALSE)
+ if (glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE)
{
- String err = GL43C.glGetProgramInfoLog(program);
+ String err = glGetProgramInfoLog(program);
throw new ShaderException(err);
}
- GL43C.glValidateProgram(program);
+ glValidateProgram(program);
- if (GL43C.glGetProgrami(program, GL43C.GL_VALIDATE_STATUS) == GL43C.GL_FALSE)
+ if (glGetProgrami(program, GL_VALIDATE_STATUS) == GL_FALSE)
{
- String err = GL43C.glGetProgramInfoLog(program);
+ String err = glGetProgramInfoLog(program);
throw new ShaderException(err);
}
@@ -109,16 +112,25 @@ public int compile(Template template) throws ShaderException
while (i > 0)
{
int shader = shaders[--i];
- GL43C.glDetachShader(program, shader);
- GL43C.glDeleteShader(shader);
+ glDetachShader(program, shader);
+ glDeleteShader(shader);
}
if (!ok)
{
- GL43C.glDeleteProgram(program);
+ glDeleteProgram(program);
}
}
return program;
}
+
+ private static void logShaderSource(String source)
+ {
+ int lineNum = 1;
+ for (String line : source.split("\n"))
+ {
+ log.error("{}: {}", lineNum++, line);
+ }
+ }
}
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
index 78a36eaa34a..3d59a80cfd2 100644
--- 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
@@ -24,7 +24,7 @@
*/
package net.runelite.client.plugins.gpu;
-class ShaderException extends Exception
+class ShaderException extends RuntimeException
{
ShaderException(String 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
index dc795731207..a9e8784e9d8 100644
--- 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
@@ -31,7 +31,7 @@
import net.runelite.api.TextureProvider;
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
import org.lwjgl.opengl.GL;
-import org.lwjgl.opengl.GL43C;
+import static org.lwjgl.opengl.GL43C.*;
@Singleton
@Slf4j
@@ -49,26 +49,26 @@ int initTextureArray(TextureProvider textureProvider)
Texture[] textures = textureProvider.getTextures();
- int textureArrayId = GL43C.glGenTextures();
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId);
+ int textureArrayId = glGenTextures();
+ glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId);
if (GL.getCapabilities().glTexStorage3D != 0)
{
- GL43C.glTexStorage3D(GL43C.GL_TEXTURE_2D_ARRAY, 8, GL43C.GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length);
+ glTexStorage3D(GL_TEXTURE_2D_ARRAY, 8, GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length);
}
else
{
int size = TEXTURE_SIZE;
for (int i = 0; i < 8; i++)
{
- GL43C.glTexImage3D(GL43C.GL_TEXTURE_2D_ARRAY, i, GL43C.GL_RGBA8, size, size, textures.length, 0, GL43C.GL_RGBA, GL43C.GL_UNSIGNED_BYTE, 0);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA8, size, size, textures.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
size /= 2;
}
}
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MAG_FILTER, GL43C.GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_WRAP_S, GL43C.GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
// Set brightness to 1.0d to upload unmodified textures to GPU
double save = textureProvider.getBrightness();
@@ -78,22 +78,22 @@ int initTextureArray(TextureProvider textureProvider)
textureProvider.setBrightness(save);
- GL43C.glActiveTexture(GL43C.GL_TEXTURE1);
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId);
- GL43C.glGenerateMipmap(GL43C.GL_TEXTURE_2D_ARRAY);
- GL43C.glActiveTexture(GL43C.GL_TEXTURE0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId);
+ glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
+ glActiveTexture(GL_TEXTURE0);
return textureArrayId;
}
void setAnisotropicFilteringLevel(int textureArrayId, int level)
{
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId);
//level = 0 means no mipmaps and no anisotropic filtering
if (level == 0)
{
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
//level = 1 means with mipmaps but without anisotropic filtering GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT defaults to 1.0 which is off
//level > 1 enables anisotropic filtering. It's up to the vendor what the values mean
@@ -102,21 +102,21 @@ void setAnisotropicFilteringLevel(int textureArrayId, int level)
{
// Set on GL_NEAREST_MIPMAP_LINEAR (bilinear filtering with mipmaps) since the pixel nature of the game means that nearest filtering
// looks best for objects up close but allows linear filtering to resolve possible aliasing and noise with mipmaps from far away objects.
- GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
}
if (GL.getCapabilities().GL_EXT_texture_filter_anisotropic)
{
- final float maxSamples = GL43C.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);
+ final float maxSamples = glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);
//Clamp from 1 to max GL says it supports.
final float anisoLevel = Math.max(1, Math.min(maxSamples, level));
- GL43C.glTexParameterf(GL43C.GL_TEXTURE_2D_ARRAY, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel);
+ glTexParameterf(GL_TEXTURE_2D_ARRAY, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel);
}
}
void freeTextureArray(int textureArrayId)
{
- GL43C.glDeleteTextures(textureArrayId);
+ glDeleteTextures(textureArrayId);
}
/**
@@ -153,7 +153,7 @@ private void updateTextures(TextureProvider textureProvider, int textureArrayId)
{
Texture[] textures = textureProvider.getTextures();
- GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId);
int cnt = 0;
for (int textureId = 0; textureId < textures.length; textureId++)
@@ -182,8 +182,8 @@ private void updateTextures(TextureProvider textureProvider, int textureArrayId)
ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(pixels.length);
pixelBuffer.put(pixels);
pixelBuffer.flip();
- GL43C.glTexSubImage3D(GL43C.GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE,
- 1, GL43C.GL_RGBA, GL43C.GL_UNSIGNED_BYTE, pixelBuffer);
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE,
+ 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelBuffer);
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.java
new file mode 100644
index 00000000000..f50a010c4b4
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2025, 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.api.Projection;
+import net.runelite.api.Scene;
+import static net.runelite.client.plugins.gpu.GpuPlugin.uniEntityTint;
+import static net.runelite.client.plugins.gpu.GpuPlugin.updateEntityProjection;
+import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
+import static org.lwjgl.opengl.GL11.glDrawArrays;
+import static org.lwjgl.opengl.GL11C.GL_FLOAT;
+import static org.lwjgl.opengl.GL11C.GL_INT;
+import static org.lwjgl.opengl.GL11C.GL_SHORT;
+import static org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW;
+import static org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER;
+import static org.lwjgl.opengl.GL15C.glBindBuffer;
+import static org.lwjgl.opengl.GL20C.glEnableVertexAttribArray;
+import static org.lwjgl.opengl.GL20C.glUniform4i;
+import static org.lwjgl.opengl.GL20C.glVertexAttribPointer;
+import static org.lwjgl.opengl.GL30C.glBindVertexArray;
+import static org.lwjgl.opengl.GL30C.glDeleteVertexArrays;
+import static org.lwjgl.opengl.GL30C.glGenVertexArrays;
+import static org.lwjgl.opengl.GL30C.glVertexAttribIPointer;
+
+class VAO
+{
+ // Temporary vertex format
+ // index 0: vec3(x, y, z)
+ // index 1: int abhsl
+ // index 2: short vec4(id, x, y, z)
+ static final int VERT_SIZE = 24;
+
+ final VBO vbo;
+ int vao;
+
+ VAO(int size)
+ {
+ vbo = new VBO(size);
+ }
+
+ void init()
+ {
+ vao = glGenVertexArrays();
+ glBindVertexArray(vao);
+
+ vbo.init(GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo.bufId);
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, VERT_SIZE, 0);
+
+ glEnableVertexAttribArray(1);
+ glVertexAttribIPointer(1, 1, GL_INT, VERT_SIZE, 12);
+
+ glEnableVertexAttribArray(2);
+ glVertexAttribIPointer(2, 4, GL_SHORT, VERT_SIZE, 16);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ }
+
+ void destroy()
+ {
+ vbo.destroy();
+ glDeleteVertexArrays(vao);
+ vao = 0;
+ }
+
+ int[] lengths = new int[4];
+ Projection[] projs = new Projection[4];
+ Scene[] scenes = new Scene[4];
+ int off = 0;
+
+ void addRange(Projection projection, Scene scene)
+ {
+ assert vbo.mapped;
+
+ if (off > 0 && lengths[off - 1] == vbo.vb.position())
+ {
+ return;
+ }
+
+ if (lengths.length == off)
+ {
+ int l = lengths.length << 1;
+ lengths = Arrays.copyOf(lengths, l);
+ projs = Arrays.copyOf(projs, l);
+ scenes = Arrays.copyOf(scenes, l);
+ }
+
+ lengths[off] = vbo.vb.position();
+ projs[off] = projection;
+ scenes[off] = scene;
+ off++;
+ }
+
+ void draw()
+ {
+ assert !vbo.mapped;
+
+ int start = 0;
+ for (int i = 0; i < off; ++i)
+ {
+ int end = lengths[i];
+ Projection p = projs[i];
+ Scene scene = scenes[i];
+
+ int count = end - start;
+
+ updateEntityProjection(p);
+ glUniform4i(uniEntityTint, scene.getOverrideHue(), scene.getOverrideSaturation(), scene.getOverrideLuminance(), scene.getOverrideAmount());
+ glBindVertexArray(vao);
+ glDrawArrays(GL_TRIANGLES, start / (VERT_SIZE / 4), count / (VAO.VERT_SIZE / 4));
+
+ start = end;
+ }
+ }
+
+ void reset()
+ {
+ Arrays.fill(projs, 0, off, null);
+ Arrays.fill(scenes, 0, off, null);
+ off = 0;
+ }
+}
+
+@Slf4j
+class VAOList
+{
+ // this needs to be larger than the largest single model
+ private static final int VAO_SIZE = 4 * 1024 * 1024;
+
+ private int curIdx;
+ private final List vaos = new ArrayList<>();
+
+ VAO get(int size)
+ {
+ assert size <= VAO_SIZE;
+
+ while (curIdx < vaos.size())
+ {
+ VAO vao = vaos.get(curIdx);
+ if (!vao.vbo.mapped)
+ {
+ vao.vbo.map();
+ }
+
+ int rem = vao.vbo.vb.remaining() * Integer.BYTES;
+ if (size <= rem)
+ {
+ return vao;
+ }
+
+ curIdx++;
+ }
+
+ VAO vao = new VAO(VAO_SIZE);
+ vao.init();
+ vao.vbo.map();
+ vaos.add(vao);
+ log.debug("Allocated VAO {} request {}", vao.vao, size);
+ return vao;
+ }
+
+ List unmap()
+ {
+ int sz = 0;
+ for (VAO vao : vaos)
+ {
+ if (vao.vbo.mapped)
+ {
+ ++sz;
+ vao.vbo.unmap();
+ }
+ }
+ curIdx = 0;
+ return vaos.subList(0, sz);
+ }
+
+ void free()
+ {
+ for (VAO vao : vaos)
+ {
+ vao.destroy();
+ }
+ vaos.clear();
+ curIdx = 0;
+ }
+
+ void addRange(Projection projection, Scene scene)
+ {
+ for (int i = 0; i <= curIdx && i < vaos.size(); ++i)
+ {
+ VAO vao = vaos.get(i);
+ if (vao.vbo.mapped)
+ {
+ vao.addRange(projection, scene);
+ }
+ }
+ }
+
+ void debug()
+ {
+ log.debug("{} vaos allocated", vaos.size());
+ for (VAO vao : vaos)
+ {
+ log.debug("vao {} mapped: {} num ranges: {} length: {}", vao, vao.vbo.mapped, vao.off, vao.vbo.mapped ? vao.vbo.vb.position() : -1);
+ if (vao.off > 1)
+ {
+ for (int i = 0; i < vao.off; ++i)
+ {
+ log.debug(" {} {} {}", vao.lengths[i], vao.projs[i], vao.scenes[i]);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VBO.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VBO.java
new file mode 100644
index 00000000000..62ed7103b95
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VBO.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2025, 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.IntBuffer;
+import static org.lwjgl.opengl.GL15.GL_WRITE_ONLY;
+import static org.lwjgl.opengl.GL15.glMapBuffer;
+import static org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER;
+import static org.lwjgl.opengl.GL15C.glBindBuffer;
+import static org.lwjgl.opengl.GL15C.glBufferData;
+import static org.lwjgl.opengl.GL15C.glDeleteBuffers;
+import static org.lwjgl.opengl.GL15C.glGenBuffers;
+import static org.lwjgl.opengl.GL15C.glUnmapBuffer;
+
+class VBO
+{
+ final int size;
+ int bufId;
+ private ByteBuffer buffer;
+ IntBuffer vb;
+ int len;
+ boolean mapped;
+
+ VBO(int size)
+ {
+ this.size = size;
+ }
+
+ void init(int usage)
+ {
+ bufId = glGenBuffers();
+
+ glBindBuffer(GL_ARRAY_BUFFER, bufId);
+ glBufferData(GL_ARRAY_BUFFER, size, usage);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ void destroy()
+ {
+ if (mapped)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, bufId);
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ mapped = false;
+ }
+ glDeleteBuffers(bufId);
+ bufId = 0;
+ }
+
+ void map()
+ {
+ assert !mapped;
+ glBindBuffer(GL_ARRAY_BUFFER, bufId);
+ buffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, buffer);
+ if (buffer == null)
+ {
+ throw new RuntimeException("unable to map GL buffer " + bufId + " size " + size);
+ }
+ this.vb = buffer.asIntBuffer();
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ mapped = true;
+ }
+
+ void unmap()
+ {
+ assert mapped;
+ len = vb.position();
+ vb = null;
+
+ glBindBuffer(GL_ARRAY_BUFFER, bufId);
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ mapped = false;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java
new file mode 100644
index 00000000000..c3db9ee0f1a
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (c) 2025, 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.IntBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.api.Model;
+import net.runelite.api.Perspective;
+import net.runelite.api.Scene;
+import static net.runelite.client.plugins.gpu.FacePrioritySorter.distanceFaceCount;
+import static net.runelite.client.plugins.gpu.FacePrioritySorter.distanceToFaces;
+import static net.runelite.client.plugins.gpu.GpuPlugin.glProgram; // NOPMD: UnnecessaryImport
+import static net.runelite.client.plugins.gpu.GpuPlugin.uniBase;
+import org.lwjgl.BufferUtils;
+import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
+import static org.lwjgl.opengl.GL11.glDrawElements;
+import static org.lwjgl.opengl.GL11C.GL_TRIANGLES;
+import static org.lwjgl.opengl.GL14.glMultiDrawArrays;
+import static org.lwjgl.opengl.GL15C.GL_ELEMENT_ARRAY_BUFFER;
+import static org.lwjgl.opengl.GL15C.GL_STREAM_DRAW;
+import static org.lwjgl.opengl.GL15C.glBufferData;
+import static org.lwjgl.opengl.GL15C.glDeleteBuffers;
+import static org.lwjgl.opengl.GL15C.glGenBuffers;
+import static org.lwjgl.opengl.GL20C.glVertexAttribPointer;
+import static org.lwjgl.opengl.GL30C.GL_ARRAY_BUFFER;
+import static org.lwjgl.opengl.GL30C.GL_INT;
+import static org.lwjgl.opengl.GL30C.GL_SHORT;
+import static org.lwjgl.opengl.GL30C.glBindBuffer;
+import static org.lwjgl.opengl.GL30C.glBindVertexArray;
+import static org.lwjgl.opengl.GL30C.glDeleteVertexArrays;
+import static org.lwjgl.opengl.GL30C.glEnableVertexAttribArray;
+import static org.lwjgl.opengl.GL30C.glGenVertexArrays;
+import static org.lwjgl.opengl.GL30C.glVertexAttribIPointer;
+import static org.lwjgl.opengl.GL41C.glProgramUniform3i;
+
+@Slf4j
+@RequiredArgsConstructor
+class Zone
+{
+ private static final boolean USE_STATIC_UNSORTED = false;
+
+ // Zone vertex format
+ // index 0: short vec3(x, y, z)
+ // index 1: int abhsl
+ // index 2: short vec4(id, x, y, z)
+ static final int VERT_SIZE = 20;
+
+ int glVao;
+ int bufLen;
+
+ int glVaoA;
+ int bufLenA;
+
+ int sizeO, sizeA;
+ VBO vboO, vboA;
+
+ boolean initialized; // whether the zone vao and vbos are ready
+ boolean cull; // whether the zone is queued for deletion
+ boolean dirty; // whether the zone has temporary modifications
+ boolean invalidate; // whether the zone needs rebuilding
+
+ int[] levelOffsets = new int[4]; // buffer pos in ints for the end of the level
+
+ int[][] rids;
+ int[][] roofStart;
+ int[][] roofEnd;
+
+ final List alphaModels = new ArrayList<>(0);
+
+ void init(VBO o, VBO a)
+ {
+ assert glVao == 0;
+ assert glVaoA == 0;
+
+ if (o != null)
+ {
+ vboO = o;
+ glVao = glGenVertexArrays();
+ setupVao(glVao, o.bufId);
+ }
+
+ if (a != null)
+ {
+ vboA = a;
+ glVaoA = glGenVertexArrays();
+ setupVao(glVaoA, a.bufId);
+ }
+ }
+
+ void free()
+ {
+ if (vboO != null)
+ {
+ vboO.destroy();
+ vboO = null;
+ }
+
+ if (vboA != null)
+ {
+ vboA.destroy();
+ vboA = null;
+ }
+
+ if (glVao != 0)
+ {
+ glDeleteVertexArrays(glVao);
+ glVao = 0;
+ }
+
+ if (glVaoA != 0)
+ {
+ glDeleteVertexArrays(glVaoA);
+ glVaoA = 0;
+ }
+
+ // don't add permanent alphamodels to the cache as permanent alphamodels are always allocated
+ // to avoid having to synchronize the cache
+ alphaModels.clear();
+ }
+
+ void unmap()
+ {
+ if (vboO != null)
+ {
+ vboO.unmap();
+ }
+ if (vboA != null)
+ {
+ vboA.unmap();
+ }
+
+ if (vboO != null)
+ {
+ this.bufLen = vboO.len / (VERT_SIZE / 4);
+ }
+
+ if (vboA != null)
+ {
+ this.bufLenA = vboA.len / (VERT_SIZE / 4);
+ }
+ }
+
+ private void setupVao(int vao, int buffer)
+ {
+ glBindVertexArray(vao);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_SHORT, false, VERT_SIZE, 0);
+
+ glEnableVertexAttribArray(1);
+ glVertexAttribIPointer(1, 1, GL_INT, VERT_SIZE, 8);
+
+ glEnableVertexAttribArray(2);
+ glVertexAttribIPointer(2, 4, GL_SHORT, VERT_SIZE, 12);
+
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ void updateRoofs(Map updates)
+ {
+ for (int level = 0; level < 4; ++level)
+ {
+ for (int i = 0; i < rids[level].length; ++i)
+ {
+ rids[level][i] = updates.getOrDefault(rids[level][i], rids[level][i]);
+ }
+ }
+
+ for (AlphaModel m : alphaModels)
+ {
+ m.rid = (short) (int) updates.getOrDefault((int) m.rid, (int) m.rid);
+ }
+ }
+
+ private static final int NUM_DRAW_RANGES = 512;
+ private static final IntBuffer drawOff = BufferUtils.createIntBuffer(NUM_DRAW_RANGES);
+ private static final IntBuffer drawEnd = BufferUtils.createIntBuffer(NUM_DRAW_RANGES);
+
+ private void convertForDraw(int vertSize)
+ {
+ assert drawOff.position() == drawEnd.position();
+
+ drawOff.flip();
+ drawEnd.flip();
+
+ for (int i = 0; i < drawOff.limit(); ++i)
+ {
+ int off = drawOff.get(i);
+ int end = drawEnd.get(i);
+
+ assert end >= off;
+
+ // convert from bytes to verts
+ off /= vertSize >> 2;
+ end /= vertSize >> 2;
+
+ end -= off; // convert from end pos to length
+
+ drawOff.put(i, off);
+ drawEnd.put(i, end);
+ }
+ }
+
+ void renderOpaque(int zx, int zz, int minLevel, int currentLevel, int maxLevel, Set hiddenRoofIds)
+ {
+ drawOff.clear();
+ drawEnd.clear();
+
+ for (int level = minLevel; level <= maxLevel; ++level)
+ {
+ int[] rids = this.rids[level];
+ int[] roofStart = this.roofStart[level];
+ int[] roofEnd = this.roofEnd[level];
+
+ if (rids.length == 0 || hiddenRoofIds.isEmpty() || level <= currentLevel)
+ {
+ // draw the whole level
+ int start = level == 0 ? 0 : this.levelOffsets[level - 1];
+ int end = this.levelOffsets[level];
+ pushRange(start, end);
+ continue;
+ }
+
+ for (int roofIdx = 0; roofIdx < rids.length; ++roofIdx)
+ {
+ int rid = rids[roofIdx];
+ if (rid > 0 && !hiddenRoofIds.contains(rid))
+ {
+ // draw the roof
+ assert roofEnd[roofIdx] >= roofStart[roofIdx];
+ if (roofEnd[roofIdx] > roofStart[roofIdx])
+ {
+ pushRange(roofStart[roofIdx], roofEnd[roofIdx]);
+ }
+ }
+ }
+
+ // push from the end of the last roof to the end of the level
+ int endpos = level == 0 ? 0 : this.levelOffsets[level - 1];
+ for (int roofIdx = rids.length - 1; roofIdx >= 0; --roofIdx)
+ {
+ int rid = rids[roofIdx];
+ if (rid > 0)
+ {
+ endpos = roofEnd[roofIdx];
+ break;
+ }
+ }
+ // draw the non roofs
+ pushRange(endpos, this.levelOffsets[level]);
+ }
+
+ convertForDraw(VERT_SIZE);
+
+ glProgramUniform3i(glProgram, uniBase, zx << 10, 0, zz << 10);
+ glBindVertexArray(glVao);
+ glMultiDrawArrays(GL_TRIANGLES, drawOff, drawEnd);
+ }
+
+ private static void pushRange(int start, int end)
+ {
+ assert end >= start;
+
+ int idx = drawEnd.position();
+ if (idx > 0 && drawEnd.get(idx - 1) == start)
+ {
+ drawEnd.put(idx - 1, end);
+ }
+ else if (!drawEnd.hasRemaining())
+ {
+ log.debug("draw ranges exhausted");
+ }
+ else
+ {
+ drawOff.put(start);
+ drawEnd.put(end);
+ }
+ }
+
+ static class AlphaModel
+ {
+ int id;
+ int startpos, endpos;
+ short x, y, z; // local position
+ short rid;
+ int vao;
+ byte level;
+ byte lx, lz, ux, uz; // lower/upper zone coords
+ byte zofx, zofz; // for temp alpha models, offset of source zone from target zone
+ byte flags;
+
+ // only set for static geometry as they require sorting
+ int radius;
+ int[] packedFaces;
+ byte[] renderPriorities;
+
+ static final int SKIP = 1; // temporary model is in a closer zone
+ static final int TEMP = 2; // temporary model added to a closer zone
+
+ boolean isTemp()
+ {
+ return packedFaces == null;
+ }
+ }
+
+ static final Queue modelCache = new ArrayDeque<>();
+
+ void addAlphaModel(int vao, Model model, int startpos, int endpos, int x, int y, int z, int lx, int lz, int ux, int uz, int rid, int level, int id)
+ {
+ AlphaModel m = new AlphaModel();
+ m.id = id;
+ m.startpos = startpos;
+ m.endpos = endpos;
+ m.x = (short) x;
+ m.y = (short) y;
+ m.z = (short) z;
+ m.vao = vao;
+ m.rid = (short) rid;
+ m.level = (byte) level;
+ if (lx > -1)
+ {
+ m.lx = (byte) lx;
+ m.lz = (byte) lz;
+ m.ux = (byte) ux;
+ m.uz = (byte) uz;
+ }
+ else
+ {
+ m.lx = m.lz = m.ux = m.uz = -1;
+ }
+
+ int faceCount = model.getFaceCount();
+ int[] color3 = model.getFaceColors3();
+ byte[] transparencies = model.getFaceTransparencies();
+ float[] vertexX = model.getVerticesX();
+ float[] vertexY = model.getVerticesY();
+ float[] vertexZ = model.getVerticesZ();
+ int[] indices1 = model.getFaceIndices1();
+ int[] indices2 = model.getFaceIndices2();
+ int[] indices3 = model.getFaceIndices3();
+
+ int minX = Integer.MAX_VALUE, minY = minX, minZ = minY;
+ int maxX = Integer.MIN_VALUE, maxY = maxX, maxZ = maxY;
+
+ for (int f = 0; f < faceCount; ++f)
+ {
+ if (color3[f] == -2 || transparencies[f] == 0)
+ {
+ continue;
+ }
+
+ int fx = (int) (vertexX[indices1[f]] + vertexX[indices2[f]] + vertexX[indices3[f]]);
+ int fy = (int) (vertexY[indices1[f]] + vertexY[indices2[f]] + vertexY[indices3[f]]);
+ int fz = (int) (vertexZ[indices1[f]] + vertexZ[indices2[f]] + vertexZ[indices3[f]]);
+
+ minX = Math.min(minX, fx);
+ maxX = Math.max(maxX, fx);
+ minY = Math.min(minY, fy);
+ maxY = Math.max(maxY, fy);
+ minZ = Math.min(minZ, fz);
+ maxZ = Math.max(maxZ, fz);
+ }
+
+ int cx = (minX + maxX) / 6;
+ int cy = (minY + maxY) / 6;
+ int cz = (minZ + maxZ) / 6;
+
+ int size = Math.max(Math.max(
+ Math.max(maxX / 3 - cx, minX / -3 - cx),
+ Math.max(maxY / 3 - cy, minY / -3 - cy) * 2),
+ Math.max(maxZ / 3 - cz, minZ / -3 - cz));
+
+ int shift = 0;
+ // 10 bits because we need a sign bit
+ for (int v = size >> 10; v > 0; v >>= 1)
+ {
+ shift++;
+ }
+
+ int[] packedFaces = m.packedFaces = new int[(endpos - startpos) / ((3 * VERT_SIZE) >> 2)];
+ int radius = 0;
+ char bufferIdx = 0;
+ for (int f = 0; f < faceCount; ++f)
+ {
+ if (color3[f] == -2 || transparencies[f] == 0)
+ {
+ continue;
+ }
+
+ int fx = (((int) (vertexX[indices1[f]] + vertexX[indices2[f]] + vertexX[indices3[f]]) / 3) - cx) >> shift;
+ int fy = (((int) (vertexY[indices1[f]] + vertexY[indices2[f]] + vertexY[indices3[f]]) / 3) - cy) >> shift;
+ int fz = (((int) (vertexZ[indices1[f]] + vertexZ[indices2[f]] + vertexZ[indices3[f]]) / 3) - cz) >> shift;
+
+ radius = Math.max(radius, fx * fx + fy * fy + fz * fz);
+
+ packedFaces[bufferIdx] = ((fx & ((1 << 11) - 1)) << 21)
+ | ((fy & ((1 << 10) - 1)) << 11)
+ | (fz & ((1 << 11) - 1));
+ bufferIdx++;
+ }
+
+ assert radius >= 0;
+
+ m.renderPriorities = model.getFaceRenderPriorities();
+ m.radius = 2 + (int) Math.sqrt(radius);
+
+ assert packedFaces.length > 0;
+ assert bufferIdx == packedFaces.length;
+
+ alphaModels.add(m);
+ }
+
+ void addTempAlphaModel(int vao, int startpos, int endpos, int level, int x, int y, int z)
+ {
+ AlphaModel m = modelCache.poll();
+ if (m == null)
+ {
+ m = new AlphaModel();
+ }
+ m.id = -1;
+ m.startpos = startpos;
+ m.endpos = endpos;
+ m.x = (short) x;
+ m.y = (short) y;
+ m.z = (short) z;
+ m.vao = vao;
+ m.rid = -1;
+ m.level = (byte) level;
+ m.lx = m.lz = m.ux = m.uz = -1;
+ m.flags = 0;
+ m.zofx = m.zofz = 0;
+ alphaModels.add(m);
+ }
+
+ void removeTemp()
+ {
+ for (int i = alphaModels.size() - 1; i >= 0; --i)
+ {
+ AlphaModel m = alphaModels.get(i);
+ if (m.isTemp() || (m.flags & AlphaModel.TEMP) != 0)
+ {
+ alphaModels.remove(i);
+ m.packedFaces = null;
+ m.renderPriorities = null;
+ modelCache.add(m);
+ }
+ m.flags &= ~AlphaModel.SKIP;
+ }
+ }
+
+ // this needs to be larger than the max model alpha face count * 3
+ private static final IntBuffer alphaElements = BufferUtils.createIntBuffer(16384);
+
+ private static final int STATIC = 1;
+ private static final int TEMP = 2;
+ private static final int STATIC_UNSORTED = 3;
+
+ private static int lastDrawMode;
+ private static int lastVao;
+ private static int lastzx, lastzz;
+
+ private static int elementBufferId;
+
+ private static final int[] numOfPriority = FacePrioritySorter.numOfPriority;
+ private static final int[][] orderedFaces = FacePrioritySorter.orderedFaces;
+
+ static void initBuffer()
+ {
+ elementBufferId = glGenBuffers();
+ }
+
+ static void freeBuffer()
+ {
+ glDeleteBuffers(elementBufferId);
+ elementBufferId = 0;
+ }
+
+ void alphaSort(int zx, int zz, int cx, int cy, int cz)
+ {
+ alphaModels.sort(Comparator.comparingInt((AlphaModel m) ->
+ {
+ final int mx = (m.x + ((zx - m.zofx) << 10));
+ final int mz = (m.z + ((zz - m.zofz) << 10));
+ return (mx - cx) * (mx - cx) +
+ (m.y - cy) * (m.y - cy) +
+ (mz - cz) * (mz - cz);
+ }
+ )
+ .reversed()
+ );
+ }
+
+ void renderAlpha(int zx, int zz, int cyaw, int cpitch, int minLevel, int currentLevel, int maxLevel, int level, Set hiddenRoofIds)
+ {
+ drawOff.clear();
+ drawEnd.clear();
+ alphaElements.clear();
+ lastDrawMode = lastVao = 0;
+ lastzx = zx;
+ lastzz = zz;
+
+ int yawsin = Perspective.SINE[cyaw];
+ int yawcos = Perspective.COSINE[cyaw];
+ int pitchsin = Perspective.SINE[cpitch];
+ int pitchcos = Perspective.COSINE[cpitch];
+ for (AlphaModel m : alphaModels)
+ {
+ if ((m.flags & AlphaModel.SKIP) != 0) continue;
+ if (m.level != level) continue;
+
+ boolean ok = false;
+ if (level >= minLevel && level <= maxLevel)
+ {
+ if (level <= currentLevel || !hiddenRoofIds.contains((int) m.rid))
+ {
+ ok = true;
+ }
+ }
+ if (!ok)
+ {
+ continue;
+ }
+
+ if (lastVao != m.vao
+ || lastzx != (zx - m.zofx) || lastzz != (zz - m.zofz)
+ )
+ {
+ flush();
+ }
+
+ lastVao = m.vao;
+ lastzx = zx - m.zofx;
+ lastzz = zz - m.zofz;
+
+ if (m.isTemp())
+ {
+ // these are already sorted and so just requires a glMultiDrawArrays() from the active vao
+ lastDrawMode = TEMP;
+ pushRange(m.startpos, m.endpos);
+ continue;
+ }
+
+ if (USE_STATIC_UNSORTED)
+ {
+ lastDrawMode = STATIC_UNSORTED;
+ pushRange(m.startpos, m.endpos);
+ continue;
+ }
+
+ lastDrawMode = STATIC;
+
+ final int radius = m.radius;
+ int diameter = 1 + radius * 2;
+ final int[] packedFaces = m.packedFaces;
+ if (diameter >= 6000)
+ {
+ continue;
+ }
+
+ Arrays.fill(distanceFaceCount, 0, diameter, (char) 0);
+
+ for (int i = 0; i < packedFaces.length; ++i)
+ {
+ int pack = packedFaces[i];
+
+ int x = pack >> 21;
+ int y = (pack << 11) >> 22;
+ int z = (pack << 21) >> 21;
+
+ int t = z * yawcos - x * yawsin >> 16;
+ int fz = y * pitchsin + t * pitchcos >> 16;
+ fz += radius;
+
+ assert fz >= 0 && fz < diameter : fz;
+ distanceToFaces[fz][distanceFaceCount[fz]++] = (char) i;
+ }
+
+ if (packedFaces.length * 3 > alphaElements.remaining())
+ {
+ flush();
+ }
+
+ byte[] faceRenderPriorities = m.renderPriorities;
+ final int start = m.startpos / (VERT_SIZE >> 2); // ints to verts
+ if (faceRenderPriorities == null)
+ {
+ for (int i = diameter - 1; i >= 0; --i)
+ {
+ final int cnt = distanceFaceCount[i];
+ if (cnt > 0)
+ {
+ final char[] faces = distanceToFaces[i];
+
+ for (int faceIdx = 0; faceIdx < cnt; ++faceIdx)
+ {
+ int face = faces[faceIdx];
+ face *= 3;
+ face += start;
+ alphaElements.put(face++);
+ alphaElements.put(face++);
+ alphaElements.put(face++);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Vanilla uses priority draw order for alpha faces and not depth draw order
+ // And since we don't have the full model here, only the alpha faces, we can't compute the
+ // 10/11 insertion points either. Just ignore those since I think they are mostly for players,
+ // which are rendered differently anyway.
+ Arrays.fill(numOfPriority, 0);
+
+ for (int i = diameter - 1; i >= 0; --i)
+ {
+ final int cnt = distanceFaceCount[i];
+ if (cnt > 0)
+ {
+ final char[] faces = distanceToFaces[i];
+
+ for (int faceIdx = 0; faceIdx < cnt; ++faceIdx)
+ {
+ final int face = faces[faceIdx];
+ final byte pri = faceRenderPriorities[face];
+ final int distIdx = numOfPriority[pri]++;
+
+ orderedFaces[pri][distIdx] = face;
+ }
+ }
+ }
+
+ for (int pri = 0; pri < 12; ++pri)
+ {
+ final int priNum = numOfPriority[pri];
+ final int[] priFaces = orderedFaces[pri];
+
+ for (int faceIdx = 0; faceIdx < priNum; ++faceIdx)
+ {
+ final int face = priFaces[faceIdx];
+ int idx = face * 3 + start;
+ alphaElements.put(idx++);
+ alphaElements.put(idx++);
+ alphaElements.put(idx++);
+ }
+ }
+ }
+ }
+
+ flush();
+ }
+
+ private void flush()
+ {
+ if (lastDrawMode == TEMP)
+ {
+ convertForDraw(VAO.VERT_SIZE);
+ glProgramUniform3i(glProgram, uniBase, 0, 0, 0);
+ glBindVertexArray(lastVao);
+ glMultiDrawArrays(GL_TRIANGLES, drawOff, drawEnd);
+ drawOff.clear();
+ drawEnd.clear();
+ }
+ else if (lastDrawMode == STATIC)
+ {
+ alphaElements.flip();
+ glProgramUniform3i(glProgram, uniBase, lastzx << 10, 0, lastzz << 10);
+ glBindVertexArray(lastVao);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferId);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, alphaElements, GL_STREAM_DRAW);
+ glDrawElements(GL_TRIANGLES, alphaElements.limit(), GL_UNSIGNED_INT, 0L);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ alphaElements.clear();
+ }
+ else if (lastDrawMode == STATIC_UNSORTED)
+ {
+ convertForDraw(VERT_SIZE);
+ glProgramUniform3i(glProgram, uniBase, lastzx << 10, 0, lastzz << 10);
+ glBindVertexArray(lastVao);
+ glMultiDrawArrays(GL_TRIANGLES, drawOff, drawEnd);
+ drawOff.clear();
+ drawEnd.clear();
+ }
+ }
+
+ void multizoneLocs(Scene scene, int zx, int zz, int cx, int cz, Zone[][] zones)
+ {
+ int offset = scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET >> 3 : 0;
+ for (AlphaModel m : alphaModels)
+ {
+ if (m.lx == -1)
+ {
+ continue;
+ }
+
+ // calculate which zone this model should be drawn from
+ // TODO fix for boats
+ int max = Integer.MAX_VALUE;
+ int closestZoneX = -50, closestZoneZ = -50;
+ for (int x = m.lx >> 3; x <= m.ux >> 3; ++x)
+ {
+ for (int z = m.lz >> 3; z <= m.uz >> 3; ++z)
+ {
+ int centerX = (zx - m.zofx + x) * 8 + 4 << 7;
+ int centerZ = (zz - m.zofz + z) * 8 + 4 << 7;
+ int distance = (centerX - cx) * (centerX - cx) +
+ (centerZ - cz) * (centerZ - cz);
+ if (distance < max)
+ {
+ max = distance;
+ closestZoneX = centerX >> 10;
+ closestZoneZ = centerZ >> 10;
+ }
+ }
+ }
+ assert closestZoneX != -50;
+ if (closestZoneX != zx || closestZoneZ != zz)
+ {
+ assert (m.flags & AlphaModel.TEMP) == 0;
+
+ assert closestZoneX + offset >= 0 : closestZoneX;
+ assert closestZoneX + offset < zones.length : closestZoneX;
+ assert closestZoneZ + offset >= 0 : closestZoneZ;
+ assert closestZoneZ + offset < zones[0].length : closestZoneZ;
+
+ Zone z = zones[closestZoneX + offset][closestZoneZ + offset];
+ assert z != null;
+ assert z != this;
+
+ AlphaModel m2 = modelCache.poll();
+ if (m2 == null)
+ {
+ m2 = new AlphaModel();
+ }
+ m2.id = m.id;
+ m2.startpos = m.startpos;
+ m2.endpos = m.endpos;
+ m2.x = m.x;
+ m2.y = m.y;
+ m2.z = m.z;
+ m2.vao = m.vao;
+ m2.rid = m.rid;
+ m2.level = m.level;
+ m2.lx = m.lx;
+ m2.lz = m.lz;
+ m2.ux = m.ux;
+ m2.uz = m.uz;
+ m2.zofx = (byte) (closestZoneX - zx);
+ m2.zofz = (byte) (closestZoneZ - zz);
+
+ m2.packedFaces = m.packedFaces;
+ m2.renderPriorities = m.renderPriorities;
+ m2.radius = m.radius;
+
+ m2.flags = AlphaModel.TEMP;
+ m.flags |= AlphaModel.SKIP;
+
+ z.alphaModels.add(m2);
+ }
+ }
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java
index 04c01697c51..a78694d152f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java
@@ -31,14 +31,14 @@
@RequiredArgsConstructor
public enum UIScalingMode
{
- NEAREST("Nearest Neighbor", 0),
- LINEAR("Bilinear", 0),
- MITCHELL("Bicubic (Mitchell)", 1),
- CATMULL_ROM("Bicubic (Catmull-Rom)", 2),
- XBR("xBR", 3);
+ NEAREST("Nearest Neighbor"),
+ LINEAR("Bilinear"),
+ MITCHELL("Bicubic (Mitchell)"),
+ CATMULL_ROM("Bicubic (Catmull-Rom)"),
+ XBR("xBR"),
+ HYBRID("Hybrid");
private final String name;
- private final int mode;
@Override
public String toString()
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java
index 4d090771aa9..3ac5320cda0 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java
@@ -86,7 +86,7 @@ default boolean showPriceType()
name = "Remember loot",
description = "Saves loot between client sessions."
)
- default boolean syncPanel()
+ default boolean rememberLoot()
{
return true;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java
index 3491df9c6e8..fec21a553ed 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java
@@ -37,7 +37,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Predicate;
@@ -534,11 +533,8 @@ void rebuild()
}
else
{
- // Loop in reverse insertion order so the most recent kill is first on the UI
- Iterator it = sessionRecords.descendingIterator();
- while (it.hasNext())
+ for (LootTrackerRecord r : sessionRecords)
{
- LootTrackerRecord r = it.next();
if (!hideIgnoredItems || !plugin.isEventIgnored(r.getTitle()))
{
buildBox(r);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
index d5b81f66824..4eeb01b751f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
@@ -487,7 +487,7 @@ private void switchProfile(String profileKey)
log.debug("Switched to profile {}", profileKey);
- if (!config.syncPanel())
+ if (!config.rememberLoot())
{
return;
}
@@ -650,7 +650,7 @@ public void onGameStateChanged(final GameStateChanged event)
// if there is unloaded loot for a type+name, load it in
private void initLoot(LootRecordType type, String name)
{
- if (panel.hasRecord(type, name))
+ if (panel.hasRecord(type, name) || !config.rememberLoot())
{
return;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java
index d403563bcae..b41c8c893e2 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java
@@ -142,6 +142,11 @@ public void onScriptPostFired(ScriptPostFired scriptPostFired)
private void updateMinimapWidgetVisibility(boolean hide)
{
+ if (client.getGameState() != GameState.LOGGED_IN)
+ {
+ return;
+ }
+
boolean vanillaHideMinimap = client.getVarbitValue(VarbitID.MINIMAP_TOGGLE) == 1;
setHidden(InterfaceID.ToplevelOsrsStretch.MAP_MINIMAP, hide || vanillaHideMinimap);
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl
deleted file mode 100644
index ce9bb7111b7..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2021, 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.
- */
-
-struct uniform {
- float cameraYaw;
- float cameraPitch;
- int centerX;
- int centerY;
- int zoom;
- float cameraX;
- float cameraY;
- float cameraZ;
-};
-
-struct shared_data {
- int totalNum[12]; // number of faces with a given priority
- int totalDistance[12]; // sum of distances to faces of a given priority
- int totalMappedNum[18]; // number of faces with a given adjusted priority
- int min10; // minimum distance to a face of priority 10
- int renderPris[0]; // priority for face draw order
-};
-
-struct modelinfo {
- int offset; // offset into vertex buffer
- int toffset; // offset into texture buffer
- int size; // length in faces
- int idx; // write idx in target buffer
- int flags; // buffer, hillskew, plane, orientation
- int x; // scene position x
- int y; // scene position y
- int z; // scene position z
-};
-
-struct vert {
- float x;
- float y;
- float z;
- int ahsl;
-};
\ No newline at end of file
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl
index aa7068703e2..6421a3dc90b 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl
@@ -3,7 +3,6 @@
// https://web.archive.org/web/20090731011248/http://scien.stanford.edu/class/psych221/projects/05/ofidaner/project_report.pdf
//
-#define NONE 0
#define PROTAN 1
#define DEUTERAN 2
#define TRITAN 3
@@ -18,19 +17,19 @@ const mat3 lms2lmst = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(-0.395
const mat3 corrections = mat3(vec3(0.0, 0.0, 0.0), vec3(0.7, 1.0, 0.0), vec3(0.7, 0.0, 1.0));
-vec3 colorblind(int mode, vec3 color) {
+vec3 colorblind(vec3 color) {
vec3 LMS = color * rgb2lms;
vec3 lms;
- if (mode == PROTAN) {
- lms = LMS * lms2lmsp; // red deficiency
- } else if (mode == DEUTERAN) {
- lms = LMS * lms2lmsd; // green deficiency
- } else if (mode == TRITAN) {
- lms = LMS * lms2lmst; // blue deficiency
- } else {
- return color;
- }
+#if COLORBLIND_MODE == PROTAN
+ lms = LMS * lms2lmsp; // red deficiency
+#elif COLORBLIND_MODE == DEUTERAN
+ lms = LMS * lms2lmsd; // green deficiency
+#elif COLORBLIND_MODE == TRITAN
+ lms = LMS * lms2lmst; // blue deficiency
+#else
+#error 1
+#endif
// LMS to RGB matrix conversion
mat3 lms2rgb = inverse(rgb2lms);
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl
deleted file mode 100644
index 8b949b20ccc..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2021, 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.
- */
-
-#define PI 3.1415926535897932384626433832795f
-#define UNIT PI / 1024.0f
-
-float3 toScreen(float3 vertex, float cameraYaw, float cameraPitch, int centerX, int centerY, int zoom) {
- float yawSin = sin(cameraYaw);
- float yawCos = cos(cameraYaw);
-
- float pitchSin = sin(cameraPitch);
- float pitchCos = cos(cameraPitch);
-
- float rotatedX = (vertex.z * yawSin) + (vertex.x * yawCos);
- float rotatedZ = (vertex.z * yawCos) - (vertex.x * yawSin);
-
- float var13 = (vertex.y * pitchCos) - (rotatedZ * pitchSin);
- float var12 = (vertex.y * pitchSin) + (rotatedZ * pitchCos);
-
- float x = rotatedX * zoom / var12 + centerX;
- float y = var13 * zoom / var12 + centerY;
- float z = -var12; // in OpenGL depth is negative
-
- return (float3)(x, y, z);
-}
-
-/*
- * Rotate a vertex by a given orientation in JAU
- */
-float4 rotate_vertex(float4 vertex, int orientation) {
- float rad = orientation * UNIT;
- float s = sin(rad);
- float c = cos(rad);
- float x = vertex.z * s + vertex.x * c;
- float z = vertex.z * c - vertex.x * s;
- return (float4)(x, vertex.y, z, vertex.w);
-}
-
-/*
- * Calculate the distance to a vertex given the camera angle
- */
-float vertex_distance(float4 vertex, float cameraYaw, float cameraPitch) {
- float yawSin = sin(cameraYaw);
- float yawCos = cos(cameraYaw);
-
- float pitchSin = sin(cameraPitch);
- float pitchCos = cos(cameraPitch);
-
- float j = vertex.z * yawCos - vertex.x * yawSin;
- float l = vertex.y * pitchSin + j * pitchCos;
-
- return l;
-}
-
-/*
- * Calculate the distance to a face
- */
-float face_distance(float4 vA, float4 vB, float4 vC, float cameraYaw, float cameraPitch) {
- float dvA = vertex_distance(vA, cameraYaw, cameraPitch);
- float dvB = vertex_distance(vB, cameraYaw, cameraPitch);
- float dvC = vertex_distance(vC, cameraYaw, cameraPitch);
- float faceDistance = (dvA + dvB + dvC) / 3;
- return (int)faceDistance;
-}
-
-/*
- * Test if a face is visible (not backward facing)
- */
-bool face_visible(__constant struct uniform *uni, float3 vA, float3 vB, float3 vC, int4 position) {
- // Move model to scene location, and account for camera offset
- float3 cameraPos = (float3)(uni->cameraX, uni->cameraY, uni->cameraZ);
- float3 modelPos = convert_float3(position.xyz);
-
- float3 lA = vA + modelPos - cameraPos;
- float3 lB = vB + modelPos - cameraPos;
- float3 lC = vC + modelPos - cameraPos;
-
- float3 sA = toScreen(lA, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom);
- float3 sB = toScreen(lB, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom);
- float3 sC = toScreen(lC, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom);
-
- return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0;
-}
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
deleted file mode 100644
index 14ceab09d2b..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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
- */
-vec4 rotate_vertex(vec4 vertex, int orientation) {
- float rad = orientation * UNIT;
- float s = sin(rad);
- float c = cos(rad);
- // clang-format off
- mat4 m = mat4(
- c, 0, s, 0,
- 0, 1, 0, 0,
- -s, 0, c, 0,
- 0, 0, 0, 1
- );
- // clang-format on
- return vertex * m;
-}
-
-/*
- * Calculate the distance to a vertex given the camera angle
- */
-float distance(vec4 vertex, float cameraYaw, float cameraPitch) {
- float yawSin = sin(cameraYaw);
- float yawCos = cos(cameraYaw);
-
- float pitchSin = sin(cameraPitch);
- float pitchCos = cos(cameraPitch);
-
- float j = vertex.z * yawCos - vertex.x * yawSin;
- float l = vertex.y * pitchSin + j * pitchCos;
-
- return l;
-}
-
-/*
- * Calculate the distance to a face
- */
-int face_distance(vec4 vA, vec4 vB, vec4 vC, float cameraYaw, float cameraPitch) {
- float dvA = distance(vA, cameraYaw, cameraPitch);
- float dvB = distance(vB, cameraYaw, cameraPitch);
- float dvC = distance(vC, cameraYaw, cameraPitch);
- float faceDistance = (dvA + dvB + dvC) / 3;
- return int(faceDistance);
-}
-
-/*
- * Test if a face is visible (not backward facing)
- */
-bool face_visible(vec3 vA, vec3 vB, vec3 vC, ivec4 position) {
- // Move model to scene location, and account for camera offset
- vec3 cameraPos = vec3(cameraX, cameraY, cameraZ);
-
- vec3 lA = vA + position.xyz - cameraPos;
- vec3 lB = vB + position.xyz - cameraPos;
- vec3 lC = vC + position.xyz - cameraPos;
-
- vec3 sA = toScreen(lA, cameraYaw, cameraPitch, centerX, centerY, zoom);
- vec3 sB = toScreen(lB, cameraYaw, cameraPitch, centerX, centerY, zoom);
- vec3 sC = toScreen(lC, cameraYaw, cameraPitch, centerX, centerY, zoom);
-
- return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0;
-}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl
deleted file mode 100644
index 9049cd29a50..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2021, 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 FACE_COUNT
-
-#include "cl_types.cl"
-#include "common.cl"
-#include "priority_render.cl"
-
-__kernel __attribute__((work_group_size_hint(256, 1, 1))) void computeLarge(__local struct shared_data *shared, __global const struct modelinfo *ol,
- __global const struct vert *vb, __global const struct vert *tempvb,
- __global const float4 *texb, __global const float4 *temptexb,
- __global struct vert *vout, __global float4 *uvout, __constant struct uniform *uni,
- read_only image3d_t tileHeightImage) {
- size_t groupId = get_group_id(0);
- size_t localId = get_local_id(0) * FACE_COUNT;
- struct modelinfo minfo = ol[groupId];
- int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0);
-
- if (localId == 0) {
- shared->min10 = 6000;
- for (int i = 0; i < 12; ++i) {
- shared->totalNum[i] = 0;
- shared->totalDistance[i] = 0;
- }
- for (int i = 0; i < 18; ++i) {
- shared->totalMappedNum[i] = 0;
- }
- }
-
- int prio[FACE_COUNT];
- int dis[FACE_COUNT];
- struct vert v1[FACE_COUNT];
- struct vert v2[FACE_COUNT];
- struct vert v3[FACE_COUNT];
-
- for (int i = 0; i < FACE_COUNT; i++) {
- get_face(shared, vb, tempvb, localId + i, minfo, uni->cameraYaw, uni->cameraPitch, &prio[i], &dis[i], &v1[i], &v2[i], &v3[i]);
- }
-
- barrier(CLK_LOCAL_MEM_FENCE);
-
- for (int i = 0; i < FACE_COUNT; i++) {
- add_face_prio_distance(shared, uni, localId + i, minfo, v1[i], v2[i], v3[i], prio[i], dis[i], pos);
- }
-
- barrier(CLK_LOCAL_MEM_FENCE);
-
- int prioAdj[FACE_COUNT];
- int idx[FACE_COUNT];
- for (int i = 0; i < FACE_COUNT; i++) {
- idx[i] = map_face_priority(shared, localId + i, minfo, prio[i], dis[i], &prioAdj[i]);
- }
-
- barrier(CLK_LOCAL_MEM_FENCE);
-
- for (int i = 0; i < FACE_COUNT; i++) {
- insert_face(shared, localId + i, minfo, prioAdj[i], dis[i], idx[i]);
- }
-
- barrier(CLK_LOCAL_MEM_FENCE);
-
- for (int i = 0; i < FACE_COUNT; i++) {
- sort_and_insert(shared, uni, texb, temptexb, vout, uvout, localId + i, minfo, prioAdj[i], dis[i], v1[i], v2[i], v3[i], tileHeightImage);
- }
-}
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
deleted file mode 100644
index edf7d3ff2fe..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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 version_header
-
-#include thread_config
-
-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 renderPris[THREAD_COUNT * FACES_PER_THREAD]; // priority for face draw order
-
-#include "comp_common.glsl"
-
-layout(local_size_x = THREAD_COUNT) in;
-
-#include "common.glsl"
-#include "priority_render.glsl"
-
-void main() {
- uint groupId = gl_WorkGroupID.x;
- uint localId = gl_LocalInvocationID.x * FACES_PER_THREAD;
- modelinfo minfo = ol[groupId];
- ivec4 pos = ivec4(minfo.x, minfo.y, minfo.z, 0);
-
- if (localId == 0) {
- min10 = 6000;
- for (int i = 0; i < 12; ++i) {
- totalNum[i] = 0;
- totalDistance[i] = 0;
- }
- for (int i = 0; i < 18; ++i) {
- totalMappedNum[i] = 0;
- }
- }
-
- int prio[FACES_PER_THREAD];
- int dis[FACES_PER_THREAD];
- vert vA[FACES_PER_THREAD];
- vert vB[FACES_PER_THREAD];
- vert vC[FACES_PER_THREAD];
-
- for (int i = 0; i < FACES_PER_THREAD; i++) {
- get_face(localId + i, minfo, cameraYaw, cameraPitch, prio[i], dis[i], vA[i], vB[i], vC[i]);
- }
-
- memoryBarrierShared();
- barrier();
-
- for (int i = 0; i < FACES_PER_THREAD; i++) {
- add_face_prio_distance(localId + i, minfo, vA[i], vB[i], vC[i], prio[i], dis[i], pos);
- }
-
- memoryBarrierShared();
- barrier();
-
- int prioAdj[FACES_PER_THREAD];
- int idx[FACES_PER_THREAD];
- for (int i = 0; i < FACES_PER_THREAD; i++) {
- idx[i] = map_face_priority(localId + i, minfo, prio[i], dis[i], prioAdj[i]);
- }
-
- memoryBarrierShared();
- barrier();
-
- for (int i = 0; i < FACES_PER_THREAD; i++) {
- insert_face(localId + i, minfo, prioAdj[i], dis[i], idx[i]);
- }
-
- memoryBarrierShared();
- barrier();
-
- for (int i = 0; i < FACES_PER_THREAD; i++) {
- sort_and_insert(localId + i, minfo, prioAdj[i], dis[i], vA[i], vB[i], vC[i]);
- }
-}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl
deleted file mode 100644
index 738967216e9..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2021, 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 "cl_types.cl"
-
-__kernel __attribute__((reqd_work_group_size(6, 1, 1))) void computeUnordered(__global const struct modelinfo *ol, __global const struct vert *vb,
- __global const struct vert *tempvb, __global const float4 *texb,
- __global const float4 *temptexb, __global struct vert *vout,
- __global float4 *uvout) {
- size_t groupId = get_group_id(0);
- size_t localId = get_local_id(0);
- struct modelinfo minfo = ol[groupId];
-
- int offset = minfo.offset;
- int size = minfo.size;
- int outOffset = minfo.idx;
- int toffset = minfo.toffset;
- int flags = minfo.flags;
-
- if (localId >= size) {
- return;
- }
-
- uint ssboOffset = localId;
- struct vert thisA, thisB, 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];
- }
-
- uint myOffset = localId;
- float3 pos = convert_float3((int3)(minfo.x, minfo.y, minfo.z));
- float4 texPos = (float4)(0, minfo.x, minfo.y, minfo.z);
-
- float3 vertA = (float3)(thisA.x, thisA.y, thisA.z) + pos;
- float3 vertB = (float3)(thisB.x, thisB.y, thisB.z) + pos;
- float3 vertC = (float3)(thisC.x, thisC.y, thisC.z) + pos;
-
- // position vertices in scene and write to out buffer
- vout[outOffset + myOffset * 3] = (struct vert){vertA.x, vertA.y, vertA.z, thisA.ahsl};
- vout[outOffset + myOffset * 3 + 1] = (struct vert){vertB.x, vertB.y, vertB.z, thisB.ahsl};
- vout[outOffset + myOffset * 3 + 2] = (struct vert){vertC.x, vertC.y, vertC.z, thisC.ahsl};
-
- if (toffset < 0) {
- uvout[outOffset + myOffset * 3] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
- uvout[outOffset + myOffset * 3 + 1] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
- uvout[outOffset + myOffset * 3 + 2] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
- } else if (flags >= 0) {
- uvout[outOffset + myOffset * 3] = texPos + temptexb[toffset + localId * 3];
- uvout[outOffset + myOffset * 3 + 1] = texPos + temptexb[toffset + localId * 3 + 1];
- uvout[outOffset + myOffset * 3 + 2] = texPos + temptexb[toffset + localId * 3 + 2];
- } else {
- uvout[outOffset + myOffset * 3] = texPos + texb[toffset + localId * 3];
- uvout[outOffset + myOffset * 3 + 1] = texPos + texb[toffset + localId * 3 + 1];
- uvout[outOffset + myOffset * 3 + 2] = texPos + texb[toffset + localId * 3 + 2];
- }
-}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl
deleted file mode 100644
index a4eb42d23ad..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 version_header
-
-#include "comp_common.glsl"
-
-layout(local_size_x = 6) in;
-
-void main() {
- uint groupId = gl_WorkGroupID.x;
- uint localId = gl_LocalInvocationID.x;
- modelinfo minfo = ol[groupId];
-
- int offset = minfo.offset;
- int size = minfo.size;
- int outOffset = minfo.idx;
- int toffset = minfo.toffset;
- int flags = minfo.flags;
-
- if (localId >= size) {
- return;
- }
-
- uint ssboOffset = localId;
- vert thisA, thisB, 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];
- }
-
- uint myOffset = localId;
- vec3 pos = vec3(minfo.x, minfo.y, minfo.z);
- ivec4 texPos = ivec4(0, pos);
-
- vec3 vertA = thisA.pos + pos;
- vec3 vertB = thisB.pos + pos;
- vec3 vertC = thisC.pos + pos;
-
- // position vertices in scene and write to out buffer
- vout[outOffset + myOffset * 3] = vert(vertA, thisA.ahsl);
- vout[outOffset + myOffset * 3 + 1] = vert(vertB, thisB.ahsl);
- vout[outOffset + myOffset * 3 + 2] = vert(vertC, thisC.ahsl);
-
- if (toffset < 0) {
- uvout[outOffset + myOffset * 3] = vec4(0);
- uvout[outOffset + myOffset * 3 + 1] = vec4(0);
- uvout[outOffset + myOffset * 3 + 2] = vec4(0);
- } else if (flags >= 0) {
- uvout[outOffset + myOffset * 3] = texPos + temptexb[toffset + localId * 3];
- uvout[outOffset + myOffset * 3 + 1] = texPos + temptexb[toffset + localId * 3 + 1];
- uvout[outOffset + myOffset * 3 + 2] = texPos + temptexb[toffset + localId * 3 + 2];
- } else {
- uvout[outOffset + myOffset * 3] = texPos + texb[toffset + localId * 3];
- uvout[outOffset + myOffset * 3 + 1] = texPos + texb[toffset + localId * 3 + 1];
- uvout[outOffset + myOffset * 3 + 2] = texPos + texb[toffset + localId * 3 + 2];
- }
-}
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
index a8916f301fb..18c98a03e8e 100644
--- 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
@@ -25,13 +25,13 @@
#version 330
//#define FRAG_UVS
-//#define ZBUF
+//#define ZBUF_DEBUG
+
+#include colorblind_mode
uniform sampler2DArray textures;
uniform float brightness;
-uniform float smoothBanding;
uniform vec4 fogColor;
-uniform int colorBlindMode;
uniform float textureLightMode;
in vec4 fColor;
@@ -39,16 +39,19 @@ noperspective centroid in float fHsl;
flat in int fTextureId;
in vec2 fUv;
in float fFogAmount;
-#ifdef ZBUF
+#ifdef ZBUF_DEBUG
in float fDepth;
#endif
out vec4 FragColor;
#include "hsl_to_rgb.glsl"
+
+#if COLORBLIND_MODE > 0
#include "colorblind.glsl"
+#endif
-#ifdef ZBUF
+#ifdef ZBUF_DEBUG
float linear_depth(float depth) {
// depth is computed as 100/z, solve for z
float z = 100 / depth;
@@ -63,21 +66,26 @@ void main() {
int textureIdx = fTextureId - 1;
vec4 textureColor = texture(textures, vec3(fUv, float(textureIdx)));
- vec4 textureColorBrightness = pow(textureColor, vec4(brightness, brightness, brightness, 1.0f));
+ vec4 textureColor0 = textureLod(textures, vec3(fUv, float(textureIdx)), 0.f);
+
+ if (textureColor0.a < 1.f)
+ discard;
+
+ textureColor = vec4(textureColor.rgb, 1.f);
+
+ textureColor = pow(textureColor, vec4(brightness, brightness, brightness, 1.f));
// textured triangles hsl is a 7 bit lightness 2-126
float light = fHsl / 127.f;
vec3 mul = (1.f - textureLightMode) * vec3(light) + textureLightMode * fColor.rgb;
- c = textureColorBrightness * vec4(mul, fColor.a);
+ c = textureColor * vec4(mul, fColor.a);
} else {
- // pick interpolated hsl or rgb depending on smooth banding setting
- vec3 rgb = hslToRgb(int(fHsl)) * smoothBanding + fColor.rgb * (1.f - smoothBanding);
- c = vec4(rgb, fColor.a);
+ c = fColor;
}
- if (colorBlindMode > 0) {
- c.rgb = colorblind(colorBlindMode, c.rgb);
- }
+#if COLORBLIND_MODE > 0
+ c.rgb = colorblind(c.rgb);
+#endif
vec3 mixedColor = mix(c.rgb, fogColor.rgb, fFogAmount);
FragColor = vec4(mixedColor, c.a);
@@ -88,7 +96,7 @@ void main() {
}
#endif
-#ifdef ZBUF
+#ifdef ZBUF_DEBUG
float dc = linear_depth(fDepth);
if (dc > 1.0) {
FragColor = vec4(1, 0, 0, 1);
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
index 449dd16c2cb..b4611875e24 100644
--- 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
@@ -24,24 +24,36 @@
*/
#version 330
-#define SAMPLING_MITCHELL 1
-#define SAMPLING_CATROM 2
-#define SAMPLING_XBR 3
+#include sampling_mode
+#include colorblind_mode
+
+#define SAMPLING_NEAREST 0
+#define SAMPLING_LINEAR 1
+#define SAMPLING_MITCHELL 2
+#define SAMPLING_CATROM 3
+#define SAMPLING_XBR 4
+#define SAMPLING_HYBRID 5
uniform sampler2D tex;
-uniform int samplingMode;
uniform ivec2 sourceDimensions;
uniform ivec2 targetDimensions;
-uniform int colorBlindMode;
uniform vec4 alphaOverlay;
-#include "scale/bicubic.glsl"
-#include "scale/xbr_lv2_frag.glsl"
+#if COLORBLIND_MODE > 0
#include "colorblind.glsl"
+#endif
in vec2 TexCoord;
+#if SAMPLING_MODE == SAMPLING_MITCHELL || SAMPLING_MODE == SAMPLING_CATROM
+#include "scale/bicubic.glsl"
+#elif SAMPLING_MODE == SAMPLING_XBR
+#include "scale/xbr_lv2_frag.glsl"
+
in XBRTable xbrTable;
+#elif SAMPLING_MODE == SAMPLING_HYBRID
+#include "scale/hybrid.glsl"
+#endif
out vec4 FragColor;
@@ -52,16 +64,21 @@ vec4 alphaBlend(vec4 src, vec4 dst) {
void main() {
vec4 c;
- if (samplingMode == SAMPLING_CATROM || samplingMode == SAMPLING_MITCHELL) {
- c = textureCubic(tex, TexCoord, samplingMode);
- } else if (samplingMode == SAMPLING_XBR) {
- c = textureXBR(tex, TexCoord, xbrTable, ceil(1.0 * targetDimensions.x / sourceDimensions.x));
- } else { // NEAREST or LINEAR, which uses GL_TEXTURE_MIN_FILTER/GL_TEXTURE_MAG_FILTER to affect sampling
- c = texture(tex, TexCoord);
- }
+#if SAMPLING_MODE == SAMPLING_MITCHELL || SAMPLING_MODE == SAMPLING_CATROM
+ c = textureCubic(tex, TexCoord);
+#elif SAMPLING_MODE == SAMPLING_XBR
+ c = textureXBR(tex, TexCoord, xbrTable, ceil(1.0 * targetDimensions.x / sourceDimensions.x));
+#elif SAMPLING_MODE == SAMPLING_HYBRID
+ c = textureHybrid(tex, TexCoord);
+#else
+ // NEAREST or LINEAR, which uses GL_TEXTURE_MIN_FILTER/GL_TEXTURE_MAG_FILTER to affect sampling
+ c = texture(tex, TexCoord);
+#endif
c = alphaBlend(c, alphaOverlay);
- c.rgb = colorblind(colorBlindMode, c.rgb);
+#if COLORBLIND_MODE > 0
+ c.rgb = colorblind(c.rgb);
+#endif
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
index 8c72591a4c3..6846c6f097b 100644
--- 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
@@ -27,7 +27,8 @@
#include texture_config
-//#define ZBUF
+//#define ZBUF_DEBUG
+//#define BIAS_DEBUG
// smallest unit of the texture which can be moved per tick. textures are all
// 128x128px - so this is equivalent to +1px
@@ -39,9 +40,6 @@ layout(triangle_strip, max_vertices = 3) out;
layout(std140) uniform uniforms {
float cameraYaw;
float cameraPitch;
- int centerX;
- int centerY;
- int zoom;
float cameraX;
float cameraY;
float cameraZ;
@@ -51,21 +49,22 @@ layout(std140) uniform uniforms {
uniform vec2 textureAnimations[TEXTURE_COUNT];
uniform int tick;
-uniform mat4 projectionMatrix;
+uniform mat4 worldProj;
-in vec3 gVertex[3];
+in vec4 gVertex[3];
in vec4 gColor[3];
in float gHsl[3];
in int gTextureId[3];
-in vec3 gTexPos[3];
+in vec4 gTexPos[3];
in float gFogAmount[3];
+in int gBias[3];
out vec4 fColor;
noperspective centroid out float fHsl;
flat out int fTextureId;
out vec2 fUv;
out float fFogAmount;
-#ifdef ZBUF
+#ifdef ZBUF_DEBUG
out float fDepth;
#endif
@@ -75,7 +74,7 @@ void main() {
if (textureId > 0) {
vec3 cameraPos = vec3(cameraX, cameraY, cameraZ);
- compute_uv(cameraPos, gVertex[0], gVertex[1], gVertex[2], gTexPos[0], gTexPos[1], gTexPos[2], uv[0], uv[1], uv[2]);
+ compute_uv(cameraPos, gVertex[0].xyz, gVertex[1].xyz, gVertex[2].xyz, gTexPos[0].xyz, gTexPos[1].xyz, gTexPos[2].xyz, uv[0], uv[1], uv[2]);
vec2 textureAnim = textureAnimations[min(textureId - 1, TEXTURE_COUNT - 1)];
for (int i = 0; i < 3; ++i) {
@@ -88,16 +87,21 @@ void main() {
}
for (int i = 0; i < 3; ++i) {
+#ifdef BIAS_DEBUG
+ fColor = vec4(clamp(gBias[i], 0, 12) / 12.0, 0.0, 0.0, 1.0);
+#else
fColor = gColor[i];
+#endif
fHsl = gHsl[i];
fTextureId = gTextureId[i];
fUv = uv[i];
fFogAmount = gFogAmount[i];
- vec4 pos = projectionMatrix * vec4(gVertex[i], 1);
-#ifdef ZBUF
+ vec4 pos = worldProj * gVertex[i];
+#ifdef ZBUF_DEBUG
fDepth = pos.z / pos.w;
#endif
+ pos.z += gBias[i] / 128.0;
gl_Position = pos;
EmitVertex();
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl
index 218974247df..8d7d0bd2c15 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl
@@ -23,69 +23,63 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-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;
+vec3 hslToRgb(vec3 hsl) {
+ float hue = hsl.x / 64.0f + 0.0078125f;
+ float sat = hsl.y / 8.0f + 0.0625f;
+ float lum = hsl.z;
- int var10 = hsl % 128;
+ float var11 = lum / 128.0f;
+ float r = var11;
+ float g = var11;
+ float b = var11;
- 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 var19;
+ if (var11 < 0.5f) {
+ var19 = var11 * (1.0f + sat);
+ } else {
+ var19 = var11 + sat - var11 * sat;
+ }
- float var27 = var6 - 0.3333333333333333f;
- if (var27 < 0.0f) {
- var27 += 1.f;
- }
+ float var21 = 2.0f * var11 - var19;
+ float var23 = hue + 0.3333333333333333f;
+ if (var23 > 1.0f) {
+ var23 -= 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;
- }
+ float var27 = hue - 0.3333333333333333f;
+ if (var27 < 0.0f) {
+ var27 += 1.f;
+ }
- 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 * var23 < 1.0f) {
+ r = var21 + (var19 - var21) * 6.0f * var23;
+ } else if (2.0f * var23 < 1.0f) {
+ r = var19;
+ } else if (3.0f * var23 < 2.0f) {
+ r = var21 + (var19 - var21) * (0.6666666666666666f - var23) * 6.0f;
+ } else {
+ r = 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;
- }
+ if (6.0f * hue < 1.0f) {
+ g = var21 + (var19 - var21) * 6.0f * hue;
+ } else if (2.0f * hue < 1.0f) {
+ g = var19;
+ } else if (3.0f * hue < 2.0f) {
+ g = var21 + (var19 - var21) * (0.6666666666666666f - hue) * 6.0f;
+ } else {
+ g = var21;
}
- vec3 rgb = vec3(pow(var13, brightness), pow(var15, brightness), pow(var17, brightness));
+ if (6.0f * var27 < 1.0f) {
+ b = var21 + (var19 - var21) * 6.0f * var27;
+ } else if (2.0f * var27 < 1.0f) {
+ b = var19;
+ } else if (3.0f * var27 < 2.0f) {
+ b = var21 + (var19 - var21) * (0.6666666666666666f - var27) * 6.0f;
+ } else {
+ b = var21;
+ }
- return rgb;
+ return vec3(pow(r, brightness), pow(g, brightness), pow(b, brightness));
}
\ No newline at end of file
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl
deleted file mode 100644
index 7aa764af880..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (c) 2021, 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.
- */
-
-// 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:
- // this can't happen unless an invalid priority is sent. just assume 0.
- return 0;
- }
-}
-
-// calculate the number of faces with a lower adjusted priority than
-// the given adjusted priority
-int count_prio_offset(__local struct shared_data *shared, int priority) {
- // this shouldn't ever be outside of (0, 17) because it is the return value from priority_map
- priority = clamp(priority, 0, 17);
- int total = 0;
- for (int i = 0; i < priority; i++) {
- total += shared->totalMappedNum[i];
- }
- return total;
-}
-
-void get_face(__local struct shared_data *shared, __global const struct vert *vb, __global const struct vert *tempvb, uint localId, struct modelinfo minfo,
- float cameraYaw, float cameraPitch,
- /* out */ int *prio, int *dis, struct vert *o1, struct vert *o2, struct vert *o3) {
- int size = minfo.size;
- int offset = minfo.offset;
- int flags = minfo.flags;
- uint ssboOffset;
-
- if (localId < size) {
- ssboOffset = localId;
- } else {
- ssboOffset = 0;
- }
-
- struct vert thisA;
- struct vert thisB;
- struct vert 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];
- }
-
- if (localId < size) {
- int orientation = flags & 0x7ff;
-
- // rotate for model orientation
- float4 thisrvA = rotate_vertex((float4)(thisA.x, thisA.y, thisA.z, 0), orientation);
- float4 thisrvB = rotate_vertex((float4)(thisB.x, thisB.y, thisB.z, 0), orientation);
- float4 thisrvC = rotate_vertex((float4)(thisC.x, thisC.y, thisC.z, 0), orientation);
-
- // calculate distance to face
- int thisPriority = (thisA.ahsl >> 16) & 0xff; // all vertices on the face have the same priority
- int thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch);
-
- *o1 = (struct vert){thisrvA.x, thisrvA.y, thisrvA.z, thisA.ahsl};
- *o2 = (struct vert){thisrvB.x, thisrvB.y, thisrvB.z, thisB.ahsl};
- *o3 = (struct vert){thisrvC.x, thisrvC.y, thisrvC.z, thisC.ahsl};
-
- *prio = thisPriority;
- *dis = thisDistance;
- } else {
- *o1 = (struct vert){0, 0, 0, 0};
- *o2 = (struct vert){0, 0, 0, 0};
- *o3 = (struct vert){0, 0, 0, 0};
- *prio = 0;
- *dis = 0;
- }
-}
-
-void add_face_prio_distance(__local struct shared_data *shared, __constant struct uniform *uni, uint localId, struct modelinfo minfo, struct vert thisrvA,
- struct vert thisrvB, struct vert thisrvC, int thisPriority, int thisDistance, int4 pos) {
- if (localId < minfo.size) {
- // if the face is not culled, it is calculated into priority distance averages
- float3 posA = (float3)(thisrvA.x, thisrvA.y, thisrvA.z);
- float3 posB = (float3)(thisrvB.x, thisrvB.y, thisrvB.z);
- float3 posC = (float3)(thisrvC.x, thisrvC.y, thisrvC.z);
- if (face_visible(uni, posA, posB, posC, pos)) {
- atomic_add(&shared->totalNum[thisPriority], 1);
- atomic_add(&shared->totalDistance[thisPriority], thisDistance);
-
- // calculate minimum distance to any face of priority 10 for positioning the 11 faces later
- if (thisPriority == 10) {
- atomic_min(&shared->min10, thisDistance);
- }
- }
- }
-}
-
-int map_face_priority(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, int *prio) {
- int size = minfo.size;
-
- // Compute average distances for 0/2, 3/4, and 6/8
-
- if (localId < size) {
- int avg1 = -6000;
- int avg2 = -6000;
- int avg3 = -6000;
-
- if (shared->totalNum[1] > 0 || shared->totalNum[2] > 0) {
- avg1 = (shared->totalDistance[1] + shared->totalDistance[2]) / (shared->totalNum[1] + shared->totalNum[2]);
- }
-
- if (shared->totalNum[3] > 0 || shared->totalNum[4] > 0) {
- avg2 = (shared->totalDistance[3] + shared->totalDistance[4]) / (shared->totalNum[3] + shared->totalNum[4]);
- }
-
- if (shared->totalNum[6] > 0 || shared->totalNum[8] > 0) {
- avg3 = (shared->totalDistance[6] + shared->totalDistance[8]) / (shared->totalNum[6] + shared->totalNum[8]);
- }
-
- int adjPrio = priority_map(thisPriority, thisDistance, shared->min10, avg1, avg2, avg3);
- int prioIdx = atomic_add(&shared->totalMappedNum[adjPrio], 1);
-
- *prio = adjPrio;
- return prioIdx;
- }
-
- *prio = 0;
- return 0;
-}
-
-void insert_face(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int adjPrio, int distance, int prioIdx) {
- int size = minfo.size;
-
- if (localId < size) {
- // calculate base offset into renderPris based on number of faces with a lower priority
- int baseOff = count_prio_offset(shared, adjPrio);
- // the furthest faces draw first, and have the highest priority.
- // if two faces have the same distance, the one with the
- // lower id draws first.
- shared->renderPris[baseOff + prioIdx] = distance << 16 | (int)(~localId & 0xffffu);
- }
-}
-
-int tile_height(read_only image3d_t tileHeightImage, int z, int x, int y) {
-#define ESCENE_OFFSET 40 // (184-104)/2
- const sampler_t tileHeightSampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE;
- int4 coord = (int4)(x + ESCENE_OFFSET, y + ESCENE_OFFSET, z, 0);
- return read_imagei(tileHeightImage, tileHeightSampler, coord).x << 3;
-}
-
-float4 hillskew_vertexf(read_only image3d_t tileHeightImage, float4 v, int hillskew, int y, int plane) {
- if (hillskew == 1) {
- float fx = v.x / 128;
- float fz = v.z / 128;
- int sx = (int)(floor(fx));
- int sz = (int)(floor(fz));
- float it;
- float h1 = mix((float)tile_height(tileHeightImage, plane, sx, sz), (float)tile_height(tileHeightImage, plane, sx + 1, sz), fract(fx, &it));
- float h2 = mix((float)tile_height(tileHeightImage, plane, sx, sz + 1), (float)tile_height(tileHeightImage, plane, sx + 1, sz + 1), fract(fx, &it));
- float h3 = mix(h1, h2, fract(fz, &it));
- return (float4)(v.x, v.y + h3 - y, v.z, v.w);
- } else {
- return v;
- }
-}
-
-void sort_and_insert(__local struct shared_data *shared, __constant struct uniform *uni, __global const float4 *texb, __global const float4 *temptexb,
- __global struct vert *vout, __global float4 *uvout, uint localId, struct modelinfo minfo, int thisPriority, int thisDistance,
- struct vert thisrvA, struct vert thisrvB, struct vert thisrvC, read_only image3d_t tileHeightImage) {
- int size = minfo.size;
-
- if (localId < size) {
- int outOffset = minfo.idx;
- int toffset = minfo.toffset;
- int flags = minfo.flags;
-
- // we only have to order faces against others of the same priority
- const int priorityOffset = count_prio_offset(shared, thisPriority);
- const int numOfPriority = shared->totalMappedNum[thisPriority];
- const int start = priorityOffset; // index of first face with this priority
- const int end = priorityOffset + numOfPriority; // index of last face with this priority
- const int renderPriority = thisDistance << 16 | (int)(~localId & 0xffffu);
- int myOffset = priorityOffset;
-
- // calculate position this face will be in
- for (int i = start; i < end; ++i) {
- if (renderPriority < shared->renderPris[i]) {
- ++myOffset;
- }
- }
-
- // position into scene
- float4 pos = (float4)(minfo.x, minfo.y, minfo.z, 0);
- float4 vertA = (float4)(thisrvA.x, thisrvA.y, thisrvA.z, 0) + pos;
- float4 vertB = (float4)(thisrvB.x, thisrvB.y, thisrvB.z, 0) + pos;
- float4 vertC = (float4)(thisrvC.x, thisrvC.y, thisrvC.z, 0) + pos;
-
- // apply hillskew
- int plane = (flags >> 24) & 3;
- int hillskew = (flags >> 26) & 1;
- vertA = hillskew_vertexf(tileHeightImage, vertA, hillskew, minfo.y, plane);
- vertB = hillskew_vertexf(tileHeightImage, vertB, hillskew, minfo.y, plane);
- vertC = hillskew_vertexf(tileHeightImage, vertC, hillskew, minfo.y, plane);
-
- // write to out buffer
- vout[outOffset + myOffset * 3] = (struct vert){vertA.x, vertA.y, vertA.z, thisrvA.ahsl};
- vout[outOffset + myOffset * 3 + 1] = (struct vert){vertB.x, vertB.y, vertB.z, thisrvB.ahsl};
- vout[outOffset + myOffset * 3 + 2] = (struct vert){vertC.x, vertC.y, vertC.z, thisrvC.ahsl};
-
- if (toffset < 0) {
- uvout[outOffset + myOffset * 3] = (float4)(0, 0, 0, 0);
- uvout[outOffset + myOffset * 3 + 1] = (float4)(0, 0, 0, 0);
- uvout[outOffset + myOffset * 3 + 2] = (float4)(0, 0, 0, 0);
- } else {
- float4 texA, texB, texC;
-
- if (flags >= 0) {
- texA = temptexb[toffset + localId * 3];
- texB = temptexb[toffset + localId * 3 + 1];
- texC = temptexb[toffset + localId * 3 + 2];
- } else {
- texA = texb[toffset + localId * 3];
- texB = texb[toffset + localId * 3 + 1];
- texC = texb[toffset + localId * 3 + 2];
- }
-
- int orientation = flags & 0x7ff;
- // swizzle from (tex,x,y,z) to (x,y,z,tex) for rotate and hillskew
- texA = texA.yzwx;
- texB = texB.yzwx;
- texC = texC.yzwx;
- // rotate
- texA = rotate_vertex(texA, orientation);
- texB = rotate_vertex(texB, orientation);
- texC = rotate_vertex(texC, orientation);
- // position
- texA += pos;
- texB += pos;
- texC += pos;
- // hillskew
- texA = hillskew_vertexf(tileHeightImage, texA, hillskew, minfo.y, plane);
- texB = hillskew_vertexf(tileHeightImage, texB, hillskew, minfo.y, plane);
- texC = hillskew_vertexf(tileHeightImage, texC, hillskew, minfo.y, plane);
- uvout[outOffset + myOffset * 3] = texA.wxyz;
- uvout[outOffset + myOffset * 3 + 1] = texB.wxyz;
- uvout[outOffset + myOffset * 3 + 2] = texC.wxyz;
- }
- }
-}
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
deleted file mode 100644
index e8767766767..00000000000
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * 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.
- */
-
-layout(binding = 2) uniform isampler3D tileHeightSampler;
-
-// 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:
- // this can't happen unless an invalid priority is sent. just assume 0.
- return 0;
- }
-}
-
-// calculate the number of faces with a lower adjusted priority than
-// the given adjusted priority
-int count_prio_offset(int priority) {
- // this shouldn't ever be outside of (0, 17) because it is the return value from priority_map
- priority = clamp(priority, 0, 17);
- int total = 0;
- for (int i = 0; i < priority; i++) {
- total += totalMappedNum[i];
- }
- return total;
-}
-
-void get_face(uint localId, modelinfo minfo, float cameraYaw, float cameraPitch, out int prio, out int dis, out vert o1, out vert o2, out vert o3) {
- int size = minfo.size;
- int offset = minfo.offset;
- int flags = minfo.flags;
- uint ssboOffset;
-
- if (localId < size) {
- ssboOffset = localId;
- } else {
- ssboOffset = 0;
- }
-
- vert thisA;
- vert thisB;
- vert 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];
- }
-
- if (localId < size) {
- int orientation = flags & 0x7ff;
-
- // rotate for model orientation
- vec4 thisrvA = rotate_vertex(vec4(thisA.pos, 0), orientation);
- vec4 thisrvB = rotate_vertex(vec4(thisB.pos, 0), orientation);
- vec4 thisrvC = rotate_vertex(vec4(thisC.pos, 0), orientation);
-
- // calculate distance to face
- int thisPriority = (thisA.ahsl >> 16) & 0xff; // all vertices on the face have the same priority
- int thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch);
-
- o1.pos = thisrvA.xyz;
- o1.ahsl = thisA.ahsl;
-
- o2.pos = thisrvB.xyz;
- o2.ahsl = thisB.ahsl;
-
- o3.pos = thisrvC.xyz;
- o3.ahsl = thisC.ahsl;
-
- prio = thisPriority;
- dis = thisDistance;
- } else {
- o1.pos = vec3(0);
- o1.ahsl = 0;
-
- o2.pos = vec3(0);
- o2.ahsl = 0;
-
- o3.pos = vec3(0);
- o3.ahsl = 0;
-
- prio = 0;
- dis = 0;
- }
-}
-
-void add_face_prio_distance(uint localId, modelinfo minfo, vert thisrvA, vert thisrvB, vert thisrvC, int thisPriority, int thisDistance, ivec4 pos) {
- if (localId < minfo.size) {
- // if the face is not culled, it is calculated into priority distance averages
- if (face_visible(thisrvA.pos, thisrvB.pos, thisrvC.pos, pos)) {
- 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);
- }
- }
- }
-}
-
-int map_face_priority(uint localId, modelinfo minfo, int thisPriority, int thisDistance, out int prio) {
- int size = minfo.size;
-
- // Compute average distances for 0/2, 3/4, and 6/8
-
- if (localId < size) {
- int avg1 = -6000;
- int avg2 = -6000;
- int avg3 = -6000;
-
- 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 adjPrio = priority_map(thisPriority, thisDistance, min10, avg1, avg2, avg3);
- int prioIdx = atomicAdd(totalMappedNum[adjPrio], 1);
-
- prio = adjPrio;
- return prioIdx;
- }
-
- prio = 0;
- return 0;
-}
-
-void insert_face(uint localId, modelinfo minfo, int adjPrio, int distance, int prioIdx) {
- int size = minfo.size;
-
- if (localId < size) {
- // calculate base offset into renderPris based on number of faces with a lower priority
- int baseOff = count_prio_offset(adjPrio);
- // the furthest faces draw first, and have the highest priority.
- // if two faces have the same distance, the one with the
- // lower id draws first.
- renderPris[baseOff + prioIdx] = distance << 16 | int(~localId & 0xffffu);
- }
-}
-
-int tile_height(int z, int x, int y) {
-#define ESCENE_OFFSET 40 // (184-104)/2
- return texelFetch(tileHeightSampler, ivec3(x + ESCENE_OFFSET, y + ESCENE_OFFSET, z), 0).r << 3;
-}
-
-vec4 hillskew_vertexf(vec4 v, int hillskew, int y, int plane) {
- if (hillskew == 1) {
- float fx = v.x / 128;
- float fz = v.z / 128;
- int sx = int(floor(fx));
- int sz = int(floor(fz));
- float h1 = mix(tile_height(plane, sx, sz), tile_height(plane, sx + 1, sz), fract(fx));
- float h2 = mix(tile_height(plane, sx, sz + 1), tile_height(plane, sx + 1, sz + 1), fract(fx));
- float h3 = mix(h1, h2, fract(fz));
- return vec4(v.x, v.y + h3 - y, v.z, v.w);
- } else {
- return v;
- }
-}
-
-void sort_and_insert(uint localId, modelinfo minfo, int thisPriority, int thisDistance, vert thisrvA, vert thisrvB, vert thisrvC) {
- int size = minfo.size;
-
- if (localId < size) {
- int outOffset = minfo.idx;
- int toffset = minfo.toffset;
- int flags = minfo.flags;
-
- // we only have to order faces against others of the same priority
- const int priorityOffset = count_prio_offset(thisPriority);
- const int numOfPriority = totalMappedNum[thisPriority];
- const int start = priorityOffset; // index of first face with this priority
- const int end = priorityOffset + numOfPriority; // index of last face with this priority
- const int renderPriority = thisDistance << 16 | int(~localId & 0xffffu);
- int myOffset = priorityOffset;
-
- // calculate position this face will be in
- for (int i = start; i < end; ++i) {
- if (renderPriority < renderPris[i]) {
- ++myOffset;
- }
- }
-
- // position into scene
- vec4 pos = vec4(minfo.x, minfo.y, minfo.z, 0);
- vec4 vertA = vec4(thisrvA.pos, 0) + pos;
- vec4 vertB = vec4(thisrvB.pos, 0) + pos;
- vec4 vertC = vec4(thisrvC.pos, 0) + pos;
-
- // apply hillskew
- int plane = (flags >> 24) & 3;
- int hillskew = (flags >> 26) & 1;
- vertA = hillskew_vertexf(vertA, hillskew, minfo.y, plane);
- vertB = hillskew_vertexf(vertB, hillskew, minfo.y, plane);
- vertC = hillskew_vertexf(vertC, hillskew, minfo.y, plane);
-
- // write to out buffer
- vout[outOffset + myOffset * 3] = vert(vertA.xyz, thisrvA.ahsl);
- vout[outOffset + myOffset * 3 + 1] = vert(vertB.xyz, thisrvB.ahsl);
- vout[outOffset + myOffset * 3 + 2] = vert(vertC.xyz, thisrvC.ahsl);
-
- if (toffset < 0) {
- uvout[outOffset + myOffset * 3] = vec4(0);
- uvout[outOffset + myOffset * 3 + 1] = vec4(0);
- uvout[outOffset + myOffset * 3 + 2] = vec4(0);
- } else {
- vec4 texA, texB, texC;
-
- if (flags >= 0) {
- texA = temptexb[toffset + localId * 3];
- texB = temptexb[toffset + localId * 3 + 1];
- texC = temptexb[toffset + localId * 3 + 2];
- } else {
- texA = texb[toffset + localId * 3];
- texB = texb[toffset + localId * 3 + 1];
- texC = texb[toffset + localId * 3 + 2];
- }
-
- int orientation = flags & 0x7ff;
- // swizzle from (tex,x,y,z) to (x,y,z,tex) for rotate and hillskew
- texA = texA.yzwx;
- texB = texB.yzwx;
- texC = texC.yzwx;
- // rotate
- texA = rotate_vertex(texA, orientation);
- texB = rotate_vertex(texB, orientation);
- texC = rotate_vertex(texC, orientation);
- // position
- texA += pos;
- texB += pos;
- texC += pos;
- // hillskew
- texA = hillskew_vertexf(texA, hillskew, minfo.y, plane);
- texB = hillskew_vertexf(texB, hillskew, minfo.y, plane);
- texC = hillskew_vertexf(texC, hillskew, minfo.y, plane);
- uvout[outOffset + myOffset * 3] = texA.wxyz;
- uvout[outOffset + myOffset * 3 + 1] = texB.wxyz;
- uvout[outOffset + myOffset * 3 + 2] = texC.wxyz;
- }
- }
-}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl
index 9027c6e0f53..9fcfaf09afa 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl
@@ -89,7 +89,7 @@ float d(vec2 pt1, vec2 pt2) {
}
// Samples a texture using a 4x4 kernel.
-vec4 textureCubic(sampler2D sampler, vec2 texCoords, int mode) {
+vec4 textureCubic(sampler2D sampler, vec2 texCoords) {
vec2 texSize = textureSize(sampler, 0);
vec2 texelSize = 1.0 / texSize;
vec2 texelFCoords = texCoords * texSize;
@@ -103,53 +103,55 @@ vec4 textureCubic(sampler2D sampler, vec2 texCoords, int mode) {
vec4 c;
- if (mode == SAMPLING_CATROM) {
- // catrom benefits from anti-ringing, which requires knowledge of the minimum and maximum samples in the kernel
- vec4 min_sample = vec4(FLT_MAX);
- vec4 max_sample = vec4(FLT_MIN);
- for (int m = -1; m <= 2; m++) {
- for (int n = -1; n <= 2; n++) {
- // this would use texelFetch, but that would require manual implementation of texture wrapping
- vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize);
-
- // update min and max as we go
- min_sample = min(min_sample, vecData);
- max_sample = max(max_sample, vecData);
-
- // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location
- float w = catmull_rom(d(vec2(m, n), coordFract));
-
- // build the weighted average
- nSum += vecData * w;
- nDenom += w;
- }
+#if SAMPLING_MODE == SAMPLING_CATROM
+ // catrom benefits from anti-ringing, which requires knowledge of the minimum and maximum samples in the kernel
+ vec4 min_sample = vec4(FLT_MAX);
+ vec4 max_sample = vec4(FLT_MIN);
+ for (int m = -1; m <= 2; m++) {
+ for (int n = -1; n <= 2; n++) {
+ // this would use texelFetch, but that would require manual implementation of texture wrapping
+ vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize);
+
+ // update min and max as we go
+ min_sample = min(min_sample, vecData);
+ max_sample = max(max_sample, vecData);
+
+ // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location
+ float w = catmull_rom(d(vec2(m, n), coordFract));
+
+ // build the weighted average
+ nSum += vecData * w;
+ nDenom += w;
}
- // calculate weighted average
- c = nSum / nDenom;
-
- // store value before anti-ringing
- vec4 aux = c;
- // anti-ringing: clamp the color value so that it cannot exceed values already present in the kernel area
- c = clamp(c, min_sample, max_sample);
- // mix according to anti-ringing strength
- c = mix(aux, c, CR_AR_STRENGTH);
- } else if (mode == SAMPLING_MITCHELL) {
- for (int m = -1; m <= 2; m++) {
- for (int n = -1; n <= 2; n++) {
- // this would use texelFetch, but that would require manual implementation of texture wrapping
- vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize);
-
- // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location
- float w = mitchell(d(vec2(m, n), coordFract));
-
- // build the weighted average
- nSum += vecData * w;
- nDenom += w;
- }
+ }
+ // calculate weighted average
+ c = nSum / nDenom;
+
+ // store value before anti-ringing
+ vec4 aux = c;
+ // anti-ringing: clamp the color value so that it cannot exceed values already present in the kernel area
+ c = clamp(c, min_sample, max_sample);
+ // mix according to anti-ringing strength
+ c = mix(aux, c, CR_AR_STRENGTH);
+#elif SAMPLING_MODE == SAMPLING_MITCHELL
+ for (int m = -1; m <= 2; m++) {
+ for (int n = -1; n <= 2; n++) {
+ // this would use texelFetch, but that would require manual implementation of texture wrapping
+ vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize);
+
+ // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location
+ float w = mitchell(d(vec2(m, n), coordFract));
+
+ // build the weighted average
+ nSum += vecData * w;
+ nDenom += w;
}
- // calculate weighted average
- c = nSum / nDenom;
}
+ // calculate weighted average
+ c = nSum / nDenom;
+#else
+#error 1
+#endif
// return the weighted average
return c;
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/hybrid.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/hybrid.glsl
new file mode 100644
index 00000000000..4f45d609914
--- /dev/null
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/hybrid.glsl
@@ -0,0 +1,17 @@
+/*
+ * Written in 2025 by Casey Zwicker
+ * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights
+ * to this software to the public domain worldwide. This software is distributed without any warranty.
+ * You should have received a copy of the CC0 Public Domain Dedication along with this software.
+ * If not, see .
+ */
+
+// Anti-aliased UI scaling that respects pixel sharpness
+// Approach taken from https://colececil.dev/blog/2017/scaling-pixel-art-without-destroying-it/
+vec4 textureHybrid(sampler2D tex, vec2 uv) {
+ uv *= sourceDimensions;
+ vec2 texelUv = fract(uv);
+ vec2 pixelsPerTexel = vec2(targetDimensions) / vec2(sourceDimensions);
+ vec2 interpolationAmount = min(texelUv * pixelsPerTexel, .5) - min((1 - texelUv) * pixelsPerTexel, .5);
+ return texture(tex, (floor(uv) + .5 + interpolationAmount) / sourceDimensions);
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl
index 9e170e34e81..7818e59b2de 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl
@@ -24,14 +24,13 @@
*/
void compute_uv(vec3 cameraPos, vec3 f1, vec3 f2, vec3 f3, vec3 t1, vec3 t2, vec3 t3, out vec2 uv1, out vec2 uv2, out vec2 uv3) {
- vec3 v1 = t1;
- vec3 v2 = t2 - v1;
- vec3 v3 = t3 - v1;
+ vec3 tangent = t2 - t1;
+ vec3 bitangent = t3 - t1;
- vec3 texNormal = cross(v2, v3);
+ vec3 texNormal = cross(tangent, bitangent);
vec3 vertexToCamera;
- // Set the tri vertex position to the intersection point on the tex tri plane of the tri vertex
+ // Set the tri vertex position to the intersection point on the tex tri plane
// along the vector from the tri vertex to the camera.
// Point of intersection p, from https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form:
// d = ((p_0 - l_0) ⋅ n) / (l ⋅ n)
@@ -49,25 +48,25 @@ void compute_uv(vec3 cameraPos, vec3 f1, vec3 f2, vec3 f3, vec3 t1, vec3 t2, vec
vertexToCamera = cameraPos - f3;
f3 += vertexToCamera * dot(t3 - f3, texNormal) / dot(vertexToCamera, texNormal);
- vec3 v4 = f1 - v1;
- vec3 v5 = f2 - v1;
- vec3 v6 = f3 - v1;
+ vec3 v4 = f1 - t1;
+ vec3 v5 = f2 - t1;
+ vec3 v6 = f3 - t1;
- vec3 v8 = cross(v3, texNormal);
- float d = 1.0f / dot(v8, v2);
+ vec3 v8 = cross(bitangent, texNormal);
+ float d = 1.0f / dot(v8, tangent);
- float u0 = dot(v8, v4) * d;
- float u1 = dot(v8, v5) * d;
- float u2 = dot(v8, v6) * d;
+ float u1 = dot(v8, v4) * d;
+ float u2 = dot(v8, v5) * d;
+ float u3 = dot(v8, v6) * d;
- v8 = cross(v2, texNormal);
- d = 1.0f / dot(v8, v3);
+ v8 = cross(tangent, texNormal);
+ d = 1.0f / dot(v8, bitangent);
- float v0_ = dot(v8, v4) * d;
- float v1_ = dot(v8, v5) * d;
- float v2_ = dot(v8, v6) * d;
+ float v1 = dot(v8, v4) * d;
+ float v2 = dot(v8, v5) * d;
+ float v3 = dot(v8, v6) * d;
- uv1 = vec2(u0, v0_);
- uv2 = vec2(u1, v1_);
- uv3 = vec2(u2, v2_);
+ uv1 = vec2(u1, v1);
+ uv2 = vec2(u2, v2);
+ uv3 = vec2(u3, v3);
}
\ No newline at end of file
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
index 7c37d22d7d9..ad8f5e19c69 100644
--- 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
@@ -32,33 +32,34 @@
#define FOG_CORNER_ROUNDING 1.5
#define FOG_CORNER_ROUNDING_SQUARED (FOG_CORNER_ROUNDING * FOG_CORNER_ROUNDING)
-layout(location = 0) in vec3 vert;
-layout(location = 1) in int ahsl;
-layout(location = 2) in vec4 uv;
+layout(location = 0) in vec3 vertf;
+layout(location = 1) in int abhsl;
+layout(location = 2) in ivec4 tex;
layout(std140) uniform uniforms {
float cameraYaw;
float cameraPitch;
- int centerX;
- int centerY;
- int zoom;
float cameraX;
float cameraY;
float cameraZ;
};
+uniform mat4 entityProj;
+uniform ivec4 entityTint;
uniform float brightness;
uniform int useFog;
uniform int fogDepth;
uniform int drawDistance;
uniform int expandedMapLoadingChunks;
+uniform ivec3 base;
-out vec3 gVertex;
+out vec4 gVertex;
out vec4 gColor;
out float gHsl;
out int gTextureId;
-out vec3 gTexPos;
+out vec4 gTexPos;
out float gFogAmount;
+out int gBias;
#include "hsl_to_rgb.glsl"
@@ -67,28 +68,28 @@ float fogFactorLinear(const float dist, const float start, const float end) {
}
void main() {
- int hsl = ahsl & 0xffff;
- float a = float(ahsl >> 24 & 0xff) / 255.f;
+ vec4 vert = vec4(vertf + base, 1);
+ float a = float(abhsl >> 24 & 0xff) / 255.f;
+ vec3 hsl = vec3(abhsl >> 10 & 63, abhsl >> 7 & 7, abhsl & 127);
+ hsl += ((entityTint.xyz - hsl) * entityTint.w) / 128;
vec3 rgb = hslToRgb(hsl);
- gVertex = vert;
+ gVertex = entityProj * vert;
gColor = vec4(rgb, 1.f - a);
- gHsl = float(hsl);
+ gHsl = abhsl & 0xffff; // only used for texture lighting, which isn't affected by tint
- gTextureId = int(uv.x); // the texture id + 1;
- gTexPos = uv.yzw;
+ gTextureId = tex.x; // the texture id + 1
+ gTexPos = entityProj * (trunc(vert) + vec4(tex.yzw, 0));
- // the client draws one less tile to the north and east than it does to the south
- // and west, so subtract a tiles width from the north and east edges.
float fogWest = max(FOG_SCENE_EDGE_MIN, cameraX - drawDistance);
- float fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance - TILE_SIZE);
+ float fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance);
float fogSouth = max(FOG_SCENE_EDGE_MIN, cameraZ - drawDistance);
- float fogNorth = min(FOG_SCENE_EDGE_MAX, cameraZ + drawDistance - TILE_SIZE);
+ float fogNorth = min(FOG_SCENE_EDGE_MAX, cameraZ + drawDistance);
// Calculate distance from the scene edge
- float xDist = min(vert.x - fogWest, fogEast - vert.x);
- float zDist = min(vert.z - fogSouth, fogNorth - vert.z);
+ float xDist = min(gVertex.x - fogWest, fogEast - gVertex.x);
+ float zDist = min(gVertex.z - fogSouth, fogNorth - gVertex.z);
float nearestEdgeDistance = min(xDist, zDist);
float secondNearestEdgeDistance = max(xDist, zDist);
float fogDistance =
@@ -96,4 +97,6 @@ void main() {
max(0.f, (nearestEdgeDistance + FOG_CORNER_ROUNDING_SQUARED) / (secondNearestEdgeDistance + FOG_CORNER_ROUNDING_SQUARED));
gFogAmount = fogFactorLinear(fogDistance, 0, fogDepth * TILE_SIZE) * useFog;
+
+ gBias = (abhsl >> 16) & 0xff;
}
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
index 0bb514448e1..ae3294055e5 100644
--- 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
@@ -24,27 +24,33 @@
*/
#version 330
-#define SAMPLING_DEFAULT 0
-#define SAMPLING_MITCHELL 1
-#define SAMPLING_CATROM 2
-#define SAMPLING_XBR 3
+#include sampling_mode
+
+#define SAMPLING_NEAREST 0
+#define SAMPLING_LINEAR 1
+#define SAMPLING_MITCHELL 2
+#define SAMPLING_CATROM 3
+#define SAMPLING_XBR 4
+#define SAMPLING_HYBRID 5
-uniform int samplingMode;
uniform ivec2 sourceDimensions;
uniform ivec2 targetDimensions;
-#include "scale/xbr_lv2_vert.glsl"
-
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
+#if SAMPLING_MODE == SAMPLING_XBR
+#include "scale/xbr_lv2_vert.glsl"
+
out XBRTable xbrTable;
+#endif
void main() {
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
- if (samplingMode == SAMPLING_XBR)
- xbrTable = xbr_vert(TexCoord, sourceDimensions);
+#if SAMPLING_MODE == SAMPLING_XBR
+ xbrTable = xbr_vert(TexCoord, sourceDimensions);
+#endif
}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv
index 4e011021b58..aa5f7602a2a 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv
@@ -5188,4 +5188,6 @@
# Isle of Souls
2310 2919 0 2167 9308 0 Enter;Cave;40736
-2167 9308 0 2310 2919 0 Exit;Opening;40737
\ No newline at end of file
+2167 9308 0 2310 2919 0 Exit;Opening;40737
+2763 2951 0 2763 2951 1 Climb-up;Ladder;16683
+2763 2952 1 2763 2951 0 Climb-down;Ladder;16679
\ No newline at end of file
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
index 13fe5341f38..cb40d7d6718 100644
--- 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
@@ -30,6 +30,8 @@
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
+import net.runelite.client.plugins.gpu.config.ColorBlindMode;
+import net.runelite.client.plugins.gpu.config.UIScalingMode;
import net.runelite.client.plugins.gpu.template.Template;
import org.junit.Assert;
import org.junit.Assume;
@@ -54,20 +56,17 @@ public void testShaders() throws Exception
.addInclude(GpuPlugin.class)
.add(key ->
{
- if ("version_header".equals(key))
+ if ("texture_config".equals(key))
{
- return GpuPlugin.WINDOWS_VERSION_HEADER;
+ return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n";
}
- if ("thread_config".equals(key))
+ if ("sampling_mode".equals(key))
{
- int threadCount = 512;
- int facesPerThread = 1;
- return "#define THREAD_COUNT " + threadCount + "\n" +
- "#define FACES_PER_THREAD " + facesPerThread + "\n";
+ return "#define SAMPLING_MODE " + UIScalingMode.XBR.ordinal() + "\n";
}
- if ("texture_config".equals(key))
+ if ("colorblind_mode".equals(key))
{
- return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n";
+ return "#define COLORBLIND_MODE " + ColorBlindMode.DEUTERANOPE.ordinal() + "\n";
}
return null;
}),
@@ -75,8 +74,6 @@ public void testShaders() throws Exception
Shader[] shaders = {
GpuPlugin.PROGRAM,
- GpuPlugin.COMPUTE_PROGRAM,
- GpuPlugin.UNORDERED_COMPUTE_PROGRAM,
GpuPlugin.UI_PROGRAM,
};
diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml
index 7294e1f65a8..828a20170d4 100644
--- a/runelite-jshell/pom.xml
+++ b/runelite-jshell/pom.xml
@@ -30,7 +30,7 @@
net.runelite
runelite-parent
- 1.11.23-SNAPSHOT
+ 1.12.0-SNAPSHOT
jshell
diff --git a/runelite-maven-plugin/pom.xml b/runelite-maven-plugin/pom.xml
index 83c08704f03..26a1ad5525f 100644
--- a/runelite-maven-plugin/pom.xml
+++ b/runelite-maven-plugin/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.11.23-SNAPSHOT
+ 1.12.0-SNAPSHOT
runelite-maven-plugin