diff --git a/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java index 12b0d4c3cb..96df44381e 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java @@ -5,6 +5,7 @@ import net.runelite.cache.models.VertexNormal; public class ModelDefinition { + public int id; public short[] texTriangleX; public int[] vertexX; public byte[] faceRenderPriorities; @@ -44,8 +45,8 @@ public class ModelDefinition public int anInt2595; public int vertexCount = 0; public short[] texturePrimaryColor; - public VertexNormal[] normals; - public FaceNormal[] faceNormals; + public transient VertexNormal[] normals; + public transient FaceNormal[] faceNormals; public void computeNormals() { diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java index 373b868d08..bf412ca26c 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java @@ -5,9 +5,10 @@ import net.runelite.cache.io.InputStream; public class ModelLoader { - public ModelDefinition load(byte[] var1) + public ModelDefinition load(int modelId, byte[] var1) { ModelDefinition def = new ModelDefinition(); + def.id = modelId; if (var1[var1.length - 1] == -1 && var1[var1.length - 2] == -1) { diff --git a/cache/src/main/java/net/runelite/cache/models/ObjExporter.java b/cache/src/main/java/net/runelite/cache/models/ObjExporter.java new file mode 100644 index 0000000000..24053c1008 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/models/ObjExporter.java @@ -0,0 +1,92 @@ +/* + * 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.cache.models; + +import java.awt.Color; +import java.io.IOException; +import java.io.PrintWriter; +import net.runelite.cache.definitions.ModelDefinition; + +public class ObjExporter +{ + private final ModelDefinition model; + + public ObjExporter(ModelDefinition model) + { + this.model = model; + } + + public void export(PrintWriter objWriter, PrintWriter mtlWriter) throws IOException + { + model.computeNormals(); + + objWriter.println("mtllib " + model.id + ".mtl"); + + objWriter.println("o runescapemodel"); + + for (int i = 0; i < model.vertexCount; ++i) + { + objWriter.println(" v " + model.vertexX[i] + " " + model.vertexY[i] + " " + model.vertexZ[i]); + } + + for (VertexNormal normal : model.normals) + { + objWriter.println(" vn " + normal.x + " " + normal.y + " " + normal.z); + } + + for (int i = 0; i < model.triangleFaceCount; ++i) + { + objWriter.println(" usemtl color" + model.faceColor[i]); + objWriter.println(" f " + (model.trianglePointsX[i] + 1) + " " + (model.trianglePointsY[i] + 1) + " " + (model.trianglePointsZ[i] + 1)); + objWriter.println(""); + } + + for (int i = 0; i < model.faceColor.length; ++i) + { + mtlWriter.println("newmtl color" + model.faceColor[i]); + + Color color = rs2hsbToColor(model.faceColor[i]); + + double r = color.getRed() / 255.0; + double g = color.getGreen() / 255.0; + double b = color.getBlue() / 255.0; + + mtlWriter.println(" Kd " + r + " " + g + " " + b); + + if (model.faceAlphas != null) + { + mtlWriter.println(" d " + (model.faceAlphas[i] & 0xFF) / 255.0); + } + } + } + + private static Color rs2hsbToColor(int hsb) + { + int decode_hue = (hsb >> 10) & 0x3f; + int decode_saturation = (hsb >> 7) & 0x07; + int decode_brightness = (hsb & 0x7f); + return Color.getHSBColor((float) decode_hue / 63, (float) decode_saturation / 7, (float) decode_brightness / 127); + } +} diff --git a/cache/src/test/java/net/runelite/cache/ModelDumperTest.java b/cache/src/test/java/net/runelite/cache/ModelDumperTest.java index 365ee4bcce..6484bbeb1b 100644 --- a/cache/src/test/java/net/runelite/cache/ModelDumperTest.java +++ b/cache/src/test/java/net/runelite/cache/ModelDumperTest.java @@ -69,7 +69,7 @@ public class ModelDumperTest byte[] contents = file.getContents(); ModelLoader loader = new ModelLoader(); - loader.load(contents); + loader.load(archive.getArchiveId(), contents); Files.write(contents, new java.io.File(modelDir, archive.getArchiveId() + ".model")); //Files.write(gson.toJson(loader), new java.io.File(modelDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()); diff --git a/cache/src/test/java/net/runelite/cache/models/ObjExporterTest.java b/cache/src/test/java/net/runelite/cache/models/ObjExporterTest.java new file mode 100644 index 0000000000..0f9841225d --- /dev/null +++ b/cache/src/test/java/net/runelite/cache/models/ObjExporterTest.java @@ -0,0 +1,52 @@ +/* + * 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.cache.models; + +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.nio.file.Files; +import net.runelite.cache.definitions.ModelDefinition; +import net.runelite.cache.definitions.loaders.ModelLoader; +import org.junit.Ignore; +import org.junit.Test; + +public class ObjExporterTest +{ + @Test + @Ignore + public void testExport() throws Exception + { + ModelLoader loader = new ModelLoader(); + ModelDefinition model = loader.load(11048, Files.readAllBytes(new File("D:\\rs\\07\\cache\\models\\11048.model").toPath())); + + ObjExporter exporter = new ObjExporter(model); + try (PrintWriter objWriter = new PrintWriter(new FileWriter(new File("C:\\rs\\temp\\11048.obj"))); + PrintWriter mtlWriter = new PrintWriter(new FileWriter(new File("C:\\rs\\temp\\11048.mtl")))) + { + exporter.export(objWriter, mtlWriter); + } + } +} 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 e718ff0e60..3d635184ad 100644 --- a/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java +++ b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java @@ -558,7 +558,7 @@ public class ModelViewer { ModelLoader loader = new ModelLoader(); byte[] b = Files.readAllBytes(new File("models/" + id + ".model").toPath()); - md = loader.load(b); + md = loader.load(id, b); models[id] = md; return md; }