From 359a85a98ed01618ce9f4797573afdf4dda1d9ab Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 28 May 2016 11:36:18 -0400 Subject: [PATCH] Add model dumper and basic model viewer --- model-viewer/pom.xml | 96 ++++++++ .../java/net/runelite/modelviewer/Camera.java | 218 ++++++++++++++++++ .../net/runelite/modelviewer/ModelViewer.java | 149 ++++++++++++ .../net/runelite/modelviewer/Vector3f.java | 49 ++++ pom.xml | 1 + 5 files changed, 513 insertions(+) create mode 100644 model-viewer/pom.xml create mode 100644 model-viewer/src/main/java/net/runelite/modelviewer/Camera.java create mode 100644 model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java create mode 100644 model-viewer/src/main/java/net/runelite/modelviewer/Vector3f.java diff --git a/model-viewer/pom.xml b/model-viewer/pom.xml new file mode 100644 index 0000000000..a52ce360d7 --- /dev/null +++ b/model-viewer/pom.xml @@ -0,0 +1,96 @@ + + + + 4.0.0 + + + net.runelite + runelite-parent + 1.1.0-SNAPSHOT + + + net.runelite + modelviewer + 1.0.0-SNAPSHOT + Model Viewer + + + + net.runelite + deob + 1.1.0-SNAPSHOT + + + + org.lwjgl.lwjgl + lwjgl + 2.9.3 + + + + org.slf4j + slf4j-api + 1.7.12 + + + org.slf4j + slf4j-simple + 1.7.12 + + + + junit + junit + 4.12 + test + + + + + + + com.googlecode.mavennatives + maven-nativedependencies-plugin + 0.0.6 + + + unpacknatives + generate-resources + + + copy + + + + + + + diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/Camera.java b/model-viewer/src/main/java/net/runelite/modelviewer/Camera.java new file mode 100644 index 0000000000..1a25de1a31 --- /dev/null +++ b/model-viewer/src/main/java/net/runelite/modelviewer/Camera.java @@ -0,0 +1,218 @@ +package net.runelite.modelviewer; + +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import static org.lwjgl.opengl.GL11.glRotatef; +import static org.lwjgl.opengl.GL11.glTranslatef; + +public class Camera { + public static float moveSpeed = 0.05f; + + private static float maxLook = 85; + + private static float mouseSensitivity = 0.05f; + + private static Vector3f pos; + private static Vector3f rotation; + + public static void create() { + pos = new Vector3f(0, 0, 0); + rotation = new Vector3f(0, 0, 0); + } + + public static void apply() { + if(rotation.y / 360 > 1) { + rotation.y -= 360; + } else if(rotation.y / 360 < -1) { + rotation.y += 360; + } + // glLoadIdentity(); + glRotatef(rotation.x, 1, 0, 0); + glRotatef(rotation.y, 0, 1, 0); + glRotatef(rotation.z, 0, 0, 1); + glTranslatef(-pos.x, -pos.y, -pos.z); + } + + public static void acceptInput(float delta) { + acceptInputRotate(delta); + acceptInputGrab(); + acceptInputMove(delta); + } + + public static void acceptInputRotate(float delta) { + if(Mouse.isGrabbed()) { + float mouseDX = Mouse.getDX(); + float mouseDY = -Mouse.getDY(); + rotation.y += mouseDX * mouseSensitivity * delta; + rotation.x += mouseDY * mouseSensitivity * delta; + rotation.x = Math.max(-maxLook, Math.min(maxLook, rotation.x)); + } + } + + public static void acceptInputGrab() { + if(Mouse.isInsideWindow() && Mouse.isButtonDown(0)) { + Mouse.setGrabbed(true); + } + if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) { + Mouse.setGrabbed(false); + } + } + + public static void acceptInputMove(float delta) { + boolean keyUp = Keyboard.isKeyDown(Keyboard.KEY_W); + boolean keyDown = Keyboard.isKeyDown(Keyboard.KEY_S); + boolean keyRight = Keyboard.isKeyDown(Keyboard.KEY_D); + boolean keyLeft = Keyboard.isKeyDown(Keyboard.KEY_A); + boolean keyFast = Keyboard.isKeyDown(Keyboard.KEY_Q); + boolean keySlow = Keyboard.isKeyDown(Keyboard.KEY_E); + boolean keyFlyUp = Keyboard.isKeyDown(Keyboard.KEY_SPACE); + boolean keyFlyDown = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT); + + + float speed; + + if(keyFast) { + speed = moveSpeed * 5; + } + else if(keySlow) { + speed = moveSpeed / 2; + } + else { + speed = moveSpeed; + } + + speed *= delta; + + if(keyFlyUp) { + pos.y += speed; + } + if(keyFlyDown) { + pos.y -= speed; + } + + if(keyDown) { + pos.x -= Math.sin(Math.toRadians(rotation.y)) * speed; + pos.z += Math.cos(Math.toRadians(rotation.y)) * speed; + } + if(keyUp) { + pos.x += Math.sin(Math.toRadians(rotation.y)) * speed; + pos.z -= Math.cos(Math.toRadians(rotation.y)) * speed; + } + if(keyLeft) { + pos.x += Math.sin(Math.toRadians(rotation.y - 90)) * speed; + pos.z -= Math.cos(Math.toRadians(rotation.y - 90)) * speed; + } + if(keyRight) { + pos.x += Math.sin(Math.toRadians(rotation.y + 90)) * speed; + pos.z -= Math.cos(Math.toRadians(rotation.y + 90)) * speed; + } + } + + public static void setSpeed(float speed) { + moveSpeed = speed; + } + + public static void setPos(Vector3f pos) { + Camera.pos = pos; + } + + public static Vector3f getPos() { + return pos; + } + + public static void setX(float x) { + pos.x = x; + } + + public static float getX() { + return pos.x; + } + + public static void addToX(float x) { + pos.x += x; + } + + public static void setY(float y) { + pos.y = y; + } + + public static float getY() { + return pos.y; + } + + public static void addToY(float y) { + pos.y += y; + } + + public static void setZ(float z) { + pos.z = z; + } + + public static float getZ() { + return pos.z; + } + + public static void addToZ(float z) { + pos.z += z; + } + + public static void setRotation(Vector3f rotation) { + Camera.rotation = rotation; + } + + public static Vector3f getRotation() { + return rotation; + } + + public static void setRotationX(float x) { + rotation.x = x; + } + + public static float getRotationX() { + return rotation.x; + } + + public static void addToRotationX(float x) { + rotation.x += x; + } + + public static void setRotationY(float y) { + rotation.y = y; + } + + public static float getRotationY() { + return rotation.y; + } + + public static void addToRotationY(float y) { + rotation.y += y; + } + + public static void setRotationZ(float z) { + rotation.z = z; + } + + public static float getRotationZ() { + return rotation.z; + } + + public static void addToRotationZ(float z) { + rotation.z += z; + } + + public static void setMaxLook(float maxLook) { + Camera.maxLook = maxLook; + } + + public static float getMaxLook() { + return maxLook; + } + + public static void setMouseSensitivity(float mouseSensitivity) { + Camera.mouseSensitivity = mouseSensitivity; + } + + public static float getMouseSensitivity() { + return mouseSensitivity; + } +} \ No newline at end of file diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java new file mode 100644 index 0000000000..096e46d601 --- /dev/null +++ b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam + * 4. Neither the name of the Adam nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam ''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 Adam 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.awt.Color; +import java.io.File; +import java.nio.file.Files; +import net.runelite.cache.definitions.loaders.ModelLoader; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.GL11; + +public class ModelViewer +{ + public static void main(String[] args) throws Exception + { + if (args.length < 1) + { + System.err.println("Usage: modelfile"); + System.exit(1); + } + + ModelLoader md = new ModelLoader(); + byte[] b = Files.readAllBytes(new File(args[0]).toPath()); + md.load(b); + + Display.setDisplayMode(new DisplayMode(800, 600)); + Display.setTitle("Model Viewer"); + Display.setInitialBackground((float) Color.gray.getRed() / 255f, (float) Color.gray.getGreen() / 255f, (float) Color.gray.getBlue() / 255f); + Display.create(); + + GL11.glMatrixMode(GL11.GL_PROJECTION); + GL11.glLoadIdentity(); + double aspect = 1; + double near = 1; // near should be chosen as far into the scene as possible + double far = 1000; + double fov = 1; // 1 gives you a 90° field of view. It's tan(fov_angle)/2. + GL11.glFrustum(-aspect * near * fov, aspect * near * fov, -fov, fov, near, far); + + GL11.glCullFace(GL11.GL_BACK); + + long last = 0; + + while (!Display.isCloseRequested()) + { + // Clear the screen and depth buffer + GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + + GL11.glBegin(GL11.GL_TRIANGLES); + + for (int i = 0; i < md.triangleFaceCount; ++i) + { + int vertexA = md.trianglePointsX[i]; + int vertexB = md.trianglePointsY[i]; + int vertexC = md.trianglePointsZ[i]; + + int vertexAx = md.vertexX[vertexA]; + int vertexAy = md.vertexY[vertexA]; + int vertexAz = md.vertexZ[vertexA]; + + int vertexBx = md.vertexX[vertexB]; + int vertexBy = md.vertexY[vertexB]; + int vertexBz = md.vertexZ[vertexB]; + + int vertexCx = md.vertexX[vertexC]; + int vertexCy = md.vertexY[vertexC]; + int vertexCz = md.vertexZ[vertexC]; + + short hsb = md.faceColor[i]; + + int rgb = RS2HSB_to_RGB(hsb); + Color c = new Color(rgb); + + // convert to range of 0-1 + float rf = (float) c.getRed() / 255f; + float gf = (float) c.getGreen() / 255f; + float bf = (float) c.getBlue() / 255f; + + GL11.glColor3f(rf, gf, bf); + + GL11.glVertex3i(vertexAx, vertexAy, vertexAz - 50); + GL11.glVertex3i(vertexBx, vertexBy, vertexBz - 50); + GL11.glVertex3i(vertexCx, vertexCy, vertexCz - 50); + } + + GL11.glEnd(); + + Display.update(); + Display.sync(50); // fps + + long delta = System.currentTimeMillis() - last; + last = System.currentTimeMillis(); + + Camera.create(); + Camera.acceptInput(delta); + + Camera.apply(); + } + + Display.destroy(); + } + + // 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) { + float[] HSB = Color.RGBtoHSB(red, green, blue, null); + float hue = (HSB[0]); + float saturation = (HSB[1]); + float brightness = (HSB[2]); + int encode_hue = (int) (hue * 63); //to 6-bits + int encode_saturation = (int) (saturation * 7); //to 3-bits + int encode_brightness = (int) (brightness * 127); //to 7-bits + return (encode_hue << 10) + (encode_saturation << 7) + (encode_brightness); + } + + public static int RS2HSB_to_RGB(int RS2HSB) { + int decode_hue = (RS2HSB >> 10) & 0x3f; + int decode_saturation = (RS2HSB >> 7) & 0x07; + int decode_brightness = (RS2HSB & 0x7f); + return Color.HSBtoRGB((float)decode_hue/63, (float)decode_saturation/7, (float)decode_brightness/127); + } +} diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/Vector3f.java b/model-viewer/src/main/java/net/runelite/modelviewer/Vector3f.java new file mode 100644 index 0000000000..f8c67fac49 --- /dev/null +++ b/model-viewer/src/main/java/net/runelite/modelviewer/Vector3f.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam + * 4. Neither the name of the Adam nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam ''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 Adam 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 Vector3f +{ + public float x, y, z; + + public Vector3f(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public String toString() + { + return "Vector3f{" + "x=" + x + ", y=" + y + ", z=" + z + '}'; + } +} diff --git a/pom.xml b/pom.xml index 324c559dfe..3551ac75d6 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,7 @@ runescape-client runelite-client runelite-api + model-viewer