diff --git a/cache/src/test/java/net/runelite/cache/TextureDumper.java b/cache/src/test/java/net/runelite/cache/TextureDumper.java
index 827d667780..4c12df4694 100644
--- a/cache/src/test/java/net/runelite/cache/TextureDumper.java
+++ b/cache/src/test/java/net/runelite/cache/TextureDumper.java
@@ -70,7 +70,7 @@ public class TextureDumper
TextureLoader loader = new TextureLoader();
TextureDefinition texture = loader.load(file.getFileId(), file.getContents());
- Files.write(gson.toJson(texture), new java.io.File(outDir, file.getFileId() + ".json"), Charset.defaultCharset());
+ Files.write(gson.toJson(texture), new java.io.File(outDir, texture.getId() + ".json"), Charset.defaultCharset());
++count;
}
}
diff --git a/model-viewer/pom.xml b/model-viewer/pom.xml
index 2c1d79b1d9..f1b544c5a9 100644
--- a/model-viewer/pom.xml
+++ b/model-viewer/pom.xml
@@ -66,6 +66,11 @@
commons-cli
1.3.1
+
+ commons-io
+ commons-io
+ 2.5
+
junit
diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java
index 7ad6d0029c..5819c19f1d 100644
--- a/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java
+++ b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java
@@ -26,17 +26,23 @@ package net.runelite.modelviewer;
import com.google.gson.Gson;
import java.awt.Color;
+import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import javax.imageio.ImageIO;
import net.runelite.cache.definitions.ModelDefinition;
import net.runelite.cache.definitions.NpcDefinition;
import net.runelite.cache.definitions.OverlayDefinition;
+import net.runelite.cache.definitions.TextureDefinition;
import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.definitions.loaders.ModelLoader;
import net.runelite.cache.models.Vector3f;
@@ -50,14 +56,28 @@ import org.apache.commons.compress.utils.IOUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
+import static org.lwjgl.opengl.GL11.GL_NEAREST;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_S;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_T;
+import static org.lwjgl.opengl.GL11.glTexParameteri;
+import static org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class ModelViewer
{
- private static final int NUM_UNDERLAYS = 150;
- private static final int NUM_OVERLAYS = 174;
+ private static final Logger logger = LoggerFactory.getLogger(ModelViewer.class);
+
+ private static int NUM_UNDERLAYS = 150;
+ private static int NUM_OVERLAYS = 174;
+ private static int NUM_TEXTURES = 61;
private static UnderlayDefinition[] underlays = new UnderlayDefinition[NUM_UNDERLAYS];
private static OverlayDefinition[] overlays = new OverlayDefinition[NUM_OVERLAYS];
+ private static Map textures = new HashMap<>();
public static void main(String[] args) throws Exception
{
@@ -256,8 +276,6 @@ public class ModelViewer
return;
}
- GL11.glBegin(GL11.GL_TRIANGLES);
-
for (int regionX = 0; regionX < Region.X; ++regionX)
{
for (int regionY = 0; regionY < Region.Y; ++regionY)
@@ -303,6 +321,8 @@ public class ModelViewer
int overlayId = region.getOverlayId(0, regionX, regionY);
Color color = null;
+ int glTexture = -1;
+
if (underlayId > 0)
{
UnderlayDefinition ud = underlays[underlayId - 1];
@@ -320,26 +340,67 @@ public class ModelViewer
if (od.getTexture() > -1)
{
- // textures?
+ color = Color.WHITE;
+
+ Texture texture = getTexture(od.getTexture());
+ glTexture = texture.getOpenglId();
+ assert glTexture > -1;
+
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
}
}
+ GL11.glBegin(GL11.GL_TRIANGLES);
+
if (color != null)
{
GL11.glColor3f((float) color.getRed() / 255f, (float) color.getGreen() / 255f, (float) color.getBlue() / 255f);
}
+ // triangle 1
+ if (glTexture > -1)
+ {
+ GL11.glTexCoord2f(0, 0);
+ }
GL11.glVertex3i(x, z1, -y);
+ if (glTexture > -1)
+ {
+ GL11.glTexCoord2f(1, 0);
+ }
GL11.glVertex3i(x + TILE_SCALE, z2, -y);
+ if (glTexture > -1)
+ {
+ GL11.glTexCoord2f(0, 1);
+ }
GL11.glVertex3i(x, z3, -(y + TILE_SCALE));
+ // triangle 2
+ if (glTexture > -1)
+ {
+ GL11.glTexCoord2f(0, 1);
+ }
GL11.glVertex3i(x, z3, -(y + TILE_SCALE));
+ if (glTexture > -1)
+ {
+ GL11.glTexCoord2f(1, 0);
+ }
GL11.glVertex3i(x + TILE_SCALE, z2, -y);
+ if (glTexture > -1)
+ {
+ GL11.glTexCoord2f(1, 1);
+ }
GL11.glVertex3i(x + TILE_SCALE, z4, -(y + TILE_SCALE));
+
+ GL11.glEnd();
+
+ if (glTexture > -1)
+ {
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ }
+
}
}
-
- GL11.glEnd();
}
private static void loadUnderlays() throws IOException
@@ -359,7 +420,7 @@ public class ModelViewer
private static void loadOverlays() throws IOException
{
- for (int i = 0; i < NUM_UNDERLAYS; ++i)
+ for (int i = 0; i < NUM_OVERLAYS; ++i)
{
try (FileInputStream fin = new FileInputStream("overlays/" + i + ".json"))
{
@@ -372,6 +433,83 @@ public class ModelViewer
}
}
+ private static Texture getTexture(int id)
+ {
+ Texture texture = textures.get(id);
+ if (texture != null)
+ {
+ return texture;
+ }
+
+ TextureDefinition td;
+ try (FileInputStream fin = new FileInputStream("textures/" + id + ".json"))
+ {
+ td = new Gson().fromJson(new InputStreamReader(fin), TextureDefinition.class);
+ }
+ catch (IOException ex)
+ {
+ logger.warn(null, ex);
+ return null;
+ }
+
+ try (FileInputStream fin = new FileInputStream("sprite/" + td.getFileIds()[0] + "-0.png"))
+ {
+ BufferedImage image = ImageIO.read(fin);
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+ int[] rgb = new int[width * height];
+
+ int[] out = image.getRGB(0, 0, width, height, rgb, 0, width);
+ assert rgb == out;
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(rgb.length * 4);
+ for (int i = 0; i < rgb.length; ++i)
+ {
+ int pixel = rgb[i];
+
+ // argb -> rgba
+ int a = pixel >>> 24;
+ int r = (pixel >> 16) & 0xff;
+ int g = (pixel >> 8) & 0xff;
+ int b = pixel & 0xff;
+
+ buffer.put((byte) r);
+ buffer.put((byte) g);
+ buffer.put((byte) b);
+ buffer.put((byte) a);
+ }
+ buffer.position(0);
+
+ int glTexture = GL11.glGenTextures();
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
+
+ //Setup filtering, i.e. how OpenGL will interpolate the pixels when scaling up or down
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ //Setup wrap mode, i.e. how OpenGL will handle pixels outside of the expected range
+ //Note: GL_CLAMP_TO_EDGE is part of GL12
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
+
+ GL11.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); // Linear Filtering
+ GL11.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); // Linear Filtering
+
+ texture = new Texture(rgb, width, height, glTexture);
+ textures.put(id, texture);
+
+ return texture;
+ }
+ catch (IOException ex)
+ {
+ logger.warn(null, ex);
+ return null;
+ }
+ }
+
// found these two functions here https://www.rune-server.org/runescape-development/rs2-client/tools/589900-rs2-hsb-color-picker.html
public static int RGB_to_RS2HSB(int red, int green, int blue)
{
diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/ShaderManager.java b/model-viewer/src/main/java/net/runelite/modelviewer/ShaderManager.java
new file mode 100644
index 0000000000..971ed18d2e
--- /dev/null
+++ b/model-viewer/src/main/java/net/runelite/modelviewer/ShaderManager.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2017, 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.modelviewer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import org.apache.commons.io.IOUtils;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL20;
+import static org.lwjgl.opengl.GL20.GL_INFO_LOG_LENGTH;
+import static org.lwjgl.opengl.GL20.glGetProgrami;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ShaderManager
+{
+ private static final Logger logger = LoggerFactory.getLogger(ShaderManager.class);
+
+ private static final int ERR_LEN = 1024;
+
+ private int program;
+ private int vertexShader;
+ private int fragmentShader;
+
+ public void load(InputStream vertexShaderStream, InputStream fragShaderStream) throws IOException
+ {
+ program = GL20.glCreateProgram();
+ vertexShader = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
+ fragmentShader = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
+
+ String vertexShaderStr = IOUtils.toString(new InputStreamReader(vertexShaderStream));
+ String fragShaderStr = IOUtils.toString(new InputStreamReader(fragShaderStream));
+
+ GL20.glShaderSource(vertexShader, vertexShaderStr);
+ GL20.glCompileShader(vertexShader);
+
+ if (GL20.glGetShader(vertexShader, GL20.GL_COMPILE_STATUS) == GL11.GL_TRUE)
+ {
+ GL20.glAttachShader(program, vertexShader);
+ }
+ else
+ {
+ String err = GL20.glGetShaderInfoLog(vertexShader, ERR_LEN);
+ logger.warn("Error compiling vertex shader: {}", err);
+ }
+
+ GL20.glShaderSource(fragmentShader, fragShaderStr);
+ GL20.glCompileShader(fragmentShader);
+
+ if (GL20.glGetShader(fragmentShader, GL20.GL_COMPILE_STATUS) == GL11.GL_TRUE)
+ {
+ GL20.glAttachShader(program, fragmentShader);
+ }
+ else
+ {
+ String err = GL20.glGetShaderInfoLog(fragmentShader, ERR_LEN);
+ logger.warn("Error compiling fragment shader: {}", err);
+ }
+
+ GL20.glLinkProgram(program);
+
+ if (GL20.glGetProgram(program, GL20.GL_LINK_STATUS) == GL11.GL_FALSE)
+ {
+ String err = GL20.glGetProgramInfoLog(program, glGetProgrami(program, GL_INFO_LOG_LENGTH));
+ logger.warn("Error linking program: {}", err);
+ }
+
+ GL20.glValidateProgram(program);
+ }
+
+ public void destroy()
+ {
+ GL20.glDeleteShader(vertexShader);
+ GL20.glDeleteShader(fragmentShader);
+ GL20.glDeleteProgram(program);
+ }
+
+ public void use()
+ {
+ GL20.glUseProgram(program);
+ }
+
+ void unuse()
+ {
+ GL20.glUseProgram(0);
+ }
+
+ public int getProgram()
+ {
+ return program;
+ }
+
+ public int getVertexShader()
+ {
+ return vertexShader;
+ }
+
+ public int getFragmentShader()
+ {
+ return fragmentShader;
+ }
+}
diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/Texture.java b/model-viewer/src/main/java/net/runelite/modelviewer/Texture.java
new file mode 100644
index 0000000000..16e1a45c1f
--- /dev/null
+++ b/model-viewer/src/main/java/net/runelite/modelviewer/Texture.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016-2017, 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.modelviewer;
+
+public class Texture
+{
+ private final int[] rgb;
+ private final int width;
+ private final int height;
+ private final int openglId;
+
+ public Texture(int[] rgb, int width, int height, int openglId)
+ {
+ this.rgb = rgb;
+ this.width = width;
+ this.height = height;
+ this.openglId = openglId;
+ }
+
+ public int[] getRgb()
+ {
+ return rgb;
+ }
+
+ public int getWidth()
+ {
+ return width;
+ }
+
+ public int getHeight()
+ {
+ return height;
+ }
+
+ public int getOpenglId()
+ {
+ return openglId;
+ }
+}