model viewer: add support for texture rendering on terrain
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
||||
@@ -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<Integer, Texture> 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)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user