Thread: MQO Exporter & Importer

Results 1 to 10 of 10
  1. #1 MQO Exporter & Importer 
    nice


    Join Date
    Jul 2014
    Posts
    641
    Thanks given
    271
    Thanks received
    300
    Rep Power
    2739
    Wrote this for my model editor and decided to release it cuz the current (datmakers) mqo importer & exporter is quite bad

    Differences from datmaker:
    • Preserves textures
    • No color loss if possible(if it's possible to convert a certain rgb color to runescapes 16 bit hsl it will do so properly) example: exporting fire cape, doing no changes and importing it will preserve all the original colors
    • No vskin1, vskin2 bullshit to add weights that are over 100, instead 0-0.255 is mapped to 0-255
    • Includes a color palette without duplicates



    Spoiler for Exporter:

    Code:
    package editor.exporter;
    
    import editor.model.format.mqo.StateType;
    
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public interface Exporter {
    
        List<String> writeHeader();
    
        List<String> writeVertices();
    
        List<String> writeFaces(StateType type);
    
        List<String> writeMaterials();
    
        List<String> writeWeights();
    
        List<String> writeModel();
    
        List<String> writePriorities();
    
        List<String> writeTSkins();
    
        List<String> writeFooter();
    
        default List<String> write() {
            return Stream.of(writeHeader(), writeMaterials(), writeModel(), writePriorities(), writeTSkins(), writeFooter())
                    .filter(Objects::nonNull)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }
    }
    Code:
    package editor.exporter.mqo;
    
    import editor.exporter.Exporter;
    import editor.model.RSMaterial;
    import editor.model.format.mqo.StateType;
    import editor.rs.Model;
    import editor.util.ColorUtils;
    import editor.util.HSLColor;
    import editor.util.MathUtils;
    import javafx.scene.paint.Color;
    
    import java.util.*;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    import java.util.stream.Stream;
    
    public class MqoExporter implements Exporter {
    
        private final Model model;
    
        public MqoExporter(Model model) {
            this.model = model;
        }
    
        public Model getModel() {
            return model;
        }
    
        private final Map<RSMaterial, List<Integer>> materialMap = new LinkedHashMap<>();
    
        @Override
        public List<String> writeHeader() {
            List<String> result = new ArrayList<>();
            result.add("""
                    Metasequoia Document
                    Format Text Ver 1.1
                    """);
            return result;
        }
    
        public List<String> writeScene() {
            List<String> result = new ArrayList<>();
            result.add("""
                    Scene {
                    	pos 6.85959339 -30.8681774 1500
                    	lookat 0 0 0
                    	head -3.1400
                    	pich 0.1900
                    	bank 0.0000
                    	ortho 0
                    	zoom2 5.0000
                    	amb 0.250 0.250 0.250
                    	frontclip 225.000015
                    	backclip 45000
                    	dirlights 1 {
                    		light {
                    			dir 0.408 0.408 0.816
                    			color 1.000 1.000 1.000
                    		}
                    	}
                    }
                    """);
            return result;
        }
    
        @Override
        public List<String> writeVertices() {
            List<String> result = new ArrayList<>();
            result.add("\tvertex " + model.vertices + " {");
            for (int vertex = 0; vertex < model.vertices; vertex++) {
                result.add("\t".repeat(2) + model.verticesXCoordinate[vertex] + " " + model.verticesYCoordinate[vertex] * -1 + " " + model.verticesZCoordinate[vertex] * -1);
            }
            result.add("\t}");
            return result;
        }
    
        @Override
        public List<String> writeFaces(StateType type) {
            model.computeUVCoordinates();
            List<String> result = new ArrayList<>();
            result.add("\tface " + model.triangleCount + " {");
            for (int i = 0; i < model.triangleCount; i++) {
                int faceA = model.faceIndicesC[i];
                int faceB = model.faceIndicesB[i];
                int faceC = model.faceIndicesA[i];
                int materialIndex = switch (type) {
                    case MODEL -> getIndex(model.triangleColors[i]);
                    case PRIORITY -> model.trianglePriorities[i];
                    case TRIANGLE_SKIN -> model.triangleSkin[i];
                };
    
                if (model.triangleInfo[i] >= 2 && type == StateType.MODEL) {
                    result.add("\t".repeat(2) + "3 V(" + faceA + " " + faceB + " " + faceC + ") M(" + materialIndex + ") " + "UV(" +
                            model.faceTextureUCoordinates[i][2] + " " + model.faceTextureVCoordinates[i][2] + " " +
                            model.faceTextureUCoordinates[i][1] + " " + model.faceTextureVCoordinates[i][1] + " " +
                            model.faceTextureUCoordinates[i][0] + " " + model.faceTextureVCoordinates[i][0] + ")");
                } else {
                    result.add("\t".repeat(2) + "3 V(" + faceA + " " + faceB + " " + faceC + ") M(" + materialIndex + ")");
                }
            }
            result.add("\t}");
            return result;
        }
    
        @Override
        public List<String> writeMaterials() {
            List<String> result = new ArrayList<>();
    
            generateRandomPalette();
            for (int i = 0; i < model.triangleCount; i++) {
                int color = model.triangleColors[i];
                int alpha = model.faceAlpha != null ? model.faceAlpha[i] : 255;
                boolean textured = model.triangleInfo != null && model.triangleInfo[i] >= 2;
                RSMaterial material = new RSMaterial(color, alpha, textured);
                materialMap.computeIfAbsent(material, k -> new ArrayList<>())
                        .add(i);
            }
            result.add("Material " + materialMap.size() + " {");
            int index = 0;
            for (RSMaterial material : materialMap.keySet()) {
                int id = material.id();
                String materialName = index >= 255 ? "mat" + id : "material" + index;
                if (material.textured()) {
                    result.add("\t\"" + materialName + "\" shader(3) col(1.000 1.000 1.000 1.000) " + "dif(0.5) amb(0.250) emi(0.45) spc(0.000) power(5.00) tex(\"" + id + ".png\")");
                } else {
                    Color color = ColorUtils.rs2HSLToColor(id, material.alpha());
                    result.add("\t\"" + materialName + "\" shader(3) col(" + color
                            .getRed() + " " +
                            color.getGreen() + " " + (color.getBlue() + " " +
                            color.getOpacity()
                            + ") " + "dif(0.5) amb(0.250) emi(0.45) spc(0.000) power(5.00)"));
                    index++;
                }
            }
    
            result.add("}");
            return result;
        }
    
        @Override
        public List<String> writeWeights() {
            if (model.vertexWeights == null) {
                return null;
            }
            List<String> result = new ArrayList<>();
            result.add("\tvertexattr {");
            result.add("\t".repeat(2) + "weit {");
            System.out.println(Arrays.toString(model.vertexWeights));
            for (int vertex = 0; vertex < model.vertices; vertex++) {
                float mappedWeight = MathUtils.map(model.vertexWeights[vertex], 0f, 255f, 0f, 0.255f);
                result.add("\t".repeat(3) + vertex + " " + mappedWeight);
            }
            result.add("\t}");
            result.add("}");
            return result;
        }
    
        public List<String> writeModelHeader() {
            return List.of("Object \"MODEL " + model.getModelId() + "\" {");
        }
    
        public List<String> writePriorityHeader() {
            return List.of("Object \"PRIORITY" + "\" {", "\tvisible 0");
        }
    
        public List<String> writeTSkinHeader() {
            return List.of("Object \"TSKIN" + "\" {", "\tvisible 0");
        }
    
        public List<String> writeObjectFooter() {
            return List.of("}");
        }
    
        @Override
        public List<String> writeModel() {
            return Stream.of(writeModelHeader(), writeVertices(), writeFaces(StateType.MODEL), writeWeights(), writeObjectFooter())
                    .filter(Objects::nonNull)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }
    
        @Override
        public List<String> writePriorities() {
            if (model.trianglePriorities == null) {
                return null;
            }
            return Stream.of(writePriorityHeader(), writeVertices(), writeFaces(StateType.PRIORITY), writeObjectFooter())
                    .filter(Objects::nonNull)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }
    
        @Override
        public List<String> writeTSkins() {
            if (model.triangleSkin == null) {
                return null;
            }
            return Stream.of(writeTSkinHeader(), writeVertices(), writeFaces(StateType.TRIANGLE_SKIN), writeObjectFooter())
                    .filter(Objects::nonNull)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }
    
        @Override
        public List<String> writeFooter() {
            return List.of("Eof");
        }
    
        @Override
        public List<String> write() {
            return Stream.of(writeHeader(), writeScene(), writeMaterials(), writeModel(), writePriorities(), writeTSkins(), writeFooter())
                    .filter(Objects::nonNull)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }
    
        private int getIndex(int materialId) {
            int index = 0;
            for (RSMaterial material : materialMap.keySet()) {
                if (material.id() == materialId) {
                    return index;
                }
                index++;
            }
            return 0;
        }
    
        private void generateRandomPalette() {
            IntStream.rangeClosed(0, 255).forEach(x -> {
                int hue = (int) MathUtils.map(x, 0, 255 % 32, 0, 360);
    
                java.awt.Color hslColor = HSLColor.toRGB(hue, 10 + (x % 40) + (x % 35), 10 + (x % 35) + (x % 40)); // some random colors
                int hsl = ColorUtils.colorToHSL(Color.rgb(hslColor.getRed(), hslColor.getGreen(), hslColor
                        .getBlue()));
                RSMaterial material = new RSMaterial(hsl, 0, false);
                materialMap.put(material, new ArrayList<>());
            });
        }
    
    }
    Triangle -> UV
    Code:
                            int faceA = faceIndicesA[i];
                            int faceB = faceIndicesB[i];
                            int faceC = faceIndicesC[i];
    
                            Point3D a = new Point3D(verticesXCoordinate[faceA], verticesYCoordinate[faceA], verticesZCoordinate[faceA]);
                            Point3D b = new Point3D(verticesXCoordinate[faceB], verticesYCoordinate[faceB], verticesZCoordinate[faceB]);
                            Point3D c = new Point3D(verticesXCoordinate[faceC], verticesYCoordinate[faceC], verticesZCoordinate[faceC]);
    
                            Point3D p = new Point3D(verticesXCoordinate[textureVertexA[coordinate]], verticesYCoordinate[textureVertexA[coordinate]], verticesZCoordinate[textureVertexA[coordinate]]);
                            Point3D m = new Point3D(verticesXCoordinate[textureVertexB[coordinate]], verticesYCoordinate[textureVertexB[coordinate]], verticesZCoordinate[textureVertexB[coordinate]]);
                            Point3D n = new Point3D(verticesXCoordinate[textureVertexC[coordinate]], verticesYCoordinate[textureVertexC[coordinate]], verticesZCoordinate[textureVertexC[coordinate]]);
    
                            Point3D pM = m.subtract(p);
                            Point3D pN = n.subtract(p);
                            Point3D pA = a.subtract(p);
                            Point3D pB = b.subtract(p);
                            Point3D pC = c.subtract(p);
    
                            Point3D pMxPn = pM.crossProduct(pN);
    
                            Point3D uCoordinate = pN.crossProduct(pMxPn);
                            double mU = 1.0F / uCoordinate.dotProduct(pM);
    
                            double uA = uCoordinate.dotProduct(pA) * mU;
                            double uB = uCoordinate.dotProduct(pB) * mU;
                            double uC = uCoordinate.dotProduct(pC) * mU;
    
                            Point3D vCoordinate = pM.crossProduct(pMxPn);
                            double mV = 1.0 / vCoordinate.dotProduct(pN);
                            double vA = vCoordinate.dotProduct(pA) * mV;
                            double vB = vCoordinate.dotProduct(pB) * mV;
                            double vC = vCoordinate.dotProduct(pC) * mV;
    
                            u[0] = (float) uA;
                            u[1] = (float) uB;
                            u[2] = (float) uC;
    
                            v[0] = (float) vA;
                            v[1] = (float) vB;
                            v[2] = (float) vC;


    Spoiler for Importer:

    Code:
    package editor.importer;
    
    import editor.model.RSMaterial;
    import editor.model.format.mqo.MqoFace;
    import editor.model.format.mqo.MqoVertex;
    import editor.rs.Model;
    
    import java.io.IOException;
    
    public interface Importer {
    
        MqoVertex readVertex(String line);
    
        MqoFace readFace(String line);
    
        RSMaterial readMaterial(String line);
    
        int readWeight(String line);
    
        byte readPriority(String line);
    
        int readTSkin(String line);
    
        Model read() throws IOException;
    }
    Code:
    package editor.importer.mqo;
    
    import editor.importer.Importer;
    import editor.math.Vector3i;
    import editor.model.RSMaterial;
    import editor.model.UVCoordinate;
    import editor.model.format.mqo.MqoFace;
    import editor.model.format.mqo.MqoVertex;
    import editor.model.format.mqo.StateType;
    import editor.rs.Model;
    import editor.util.ColorUtils;
    import editor.util.MathUtils;
    import javafx.scene.paint.Color;
    
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import static java.lang.Byte.parseByte;
    import static java.lang.Integer.parseInt;
    import static java.lang.Float.parseFloat;
    import static java.lang.Double.parseDouble;
    
    public class MqoImporter implements Importer {
    
        private final Path path;
    
        public MqoImporter(Path path) {
            this.path = path;
        }
    
        private final String FACE_REGEX = "\\d (?:V\\()(\\d+ \\d+ \\d+)(?:\\)) (?:M\\()(\\d+)(?:\\))(?: UV\\()?((?:-)?[0-9]\\d*(?:\\.\\d+)? (?:-)?[0-9]\\d*(?:\\.\\d+)? (?:-)?[0-9]\\d*(?:\\.\\d+)? (?:-)?[0-9]\\d*(?:\\.\\d+)? (?:-)?[0-9]\\d*(?:\\.\\d+)? (?:-)?[0-9]\\d*(?:\\.\\d+)?)?(?:\\)?)";
        private final String MATERIAL_REGEX = "\"(?:.*?)\" shader\\(\\d\\) col\\(([0-9]\\d*(?:\\.\\d+)? [0-9]\\d*(?:\\.\\d+)? [0-9]\\d*(?:\\.\\d+)? [0-9]\\d*(?:\\.\\d+)?)\\)? dif\\([0-9]\\d*(?:\\.\\d+)\\) amb\\([0-9]\\d*(?:\\.\\d+)\\) emi\\([0-9]\\d*(?:\\.\\d+)\\) spc\\([0-9]\\d*(?:\\.\\d+)\\) power\\([0-9]\\d*(?:\\.\\d+)\\)( tex.+)?";
        private final String TEXTURE_REGEX = "(\\d+).png|jpg";
        private final Pattern FACE_PATTERN = Pattern.compile(FACE_REGEX);
        private final Pattern MATERIAL_PATTERN = Pattern.compile(MATERIAL_REGEX);
        private final Pattern TEXTURE_PATTERN = Pattern.compile(TEXTURE_REGEX);
    
        @Override
        public MqoVertex readVertex(String line) {
            String[] data = line.split(" ");
            return new MqoVertex((int) parseDouble(data[0]), (int) parseDouble(data[1]) * -1, (int) parseDouble(data[2]) * -1);
        }
    
        @Override
        public MqoFace readFace(String line) {
            Matcher matcher = FACE_PATTERN.matcher(line);
            if (!matcher.matches()) {
                System.err.println("pattern didn't match " + line);
                return null;
            }
            String[] indicesData = matcher.group(1).split(" ");
            Vector3i indices = new Vector3i(parseInt(indicesData[2]), parseInt(indicesData[1]), parseInt(indicesData[0]));
            int materialIndex = parseInt(matcher.group(2));
            String uvGroup = matcher.group(3);
            UVCoordinate uv = null;
            if (uvGroup != null) {
                String[] uvData = uvGroup.split(" ");
                float[] u = {parseFloat(uvData[0]), parseFloat(uvData[2]), parseFloat(uvData[4])};
                float[] v = {parseFloat(uvData[1]), parseFloat(uvData[3]), parseFloat(uvData[5])};
                uv = new UVCoordinate(u[2], u[1], u[0], v[2], v[1], v[0]);
            }
    
            return new MqoFace(indices, materialIndex, uv);
        }
    
        @Override
        public RSMaterial readMaterial(String line) {
            Matcher matcher = MATERIAL_PATTERN.matcher(line);
            if (!matcher.matches()) {
                System.out.println("Didn't match for " + line);
                return null;
            }
            String[] colorData = matcher.group(1).split(" ");
            Color color = new Color(parseDouble(colorData[0]), parseDouble(colorData[1]), parseDouble(colorData[2]), parseDouble(colorData[3]));
            Map<Color, Integer> table = ColorUtils.getRGBToHSLTable();
            int hsl = table.get(color) == null ? ColorUtils.colorToHSL(color) : table.get(color);
            int alpha = (int) (color.getOpacity() * 255.0D);
    
            String texGroup = matcher.group(2);
            int textureId = -1;
            if (texGroup != null) {
                Matcher textureMatcher = TEXTURE_PATTERN.matcher(texGroup);
                textureId = textureMatcher.find() ? parseInt(texGroup.substring(textureMatcher.start(), textureMatcher
                        .end() - 4)) : -1;
            }
            boolean textured = textureId != -1;
            return new RSMaterial(textured ? textureId : hsl, alpha, textured);
        }
    
        @Override
        public int readWeight(String line) {
            return (int) MathUtils.map(parseFloat(line.split(" ")[1]), 0f, 0.255f, 0, 255);
        }
    
        @Override
        public byte readPriority(String line) {
            Matcher matcher = FACE_PATTERN.matcher(line);
            if (!matcher.matches()) {
                return -1;
            }
            return parseByte(matcher.group(2));
        }
    
        @Override
        public int readTSkin(String line) {
            Matcher matcher = FACE_PATTERN.matcher(line);
            if (!matcher.matches()) {
                return -1;
            }
            return parseInt(matcher.group(2));
        }
    
        @Override
        public Model read() throws IOException {
            Model model = new Model();
    
            List<String> lines = Files.readAllLines(path);
            List<MqoVertex> vertices = new ArrayList<>();
            List<MqoFace> faces = new ArrayList<>();
            List<RSMaterial> materials = new ArrayList<>();
            List<Integer> weights = new ArrayList<>();
            List<Byte> priorities = new ArrayList<>();
            List<Integer> triangleSkins = new ArrayList<>();
    
            State state = State.SKIP;
            StateType type = StateType.MODEL;
    
            for (String line : lines) {
                line = line.trim();
                State determinedState = determineState(line);
                StateType determinedType = determineType(line);
    
                if (determinedState.name().matches("\\w+\\b(?<!\\bSKIP|END_READING)")) {
                    state = determinedState;
                    continue;
                }
    
                if (determinedState == State.END_READING) {
                    state = State.SKIP;
                    continue;
                }
    
                if (determinedType != null) {
                    type = determinedType;
                }
    
                switch (state) {
                    case READING_VERTICES -> vertices.add(readVertex(line));
                    case READING_FACES -> {
                        switch (type) {
                            case MODEL -> faces.add(readFace(line));
                            case PRIORITY -> priorities.add(readPriority(line));
                            case TRIANGLE_SKIN -> triangleSkins.add(readTSkin(line));
                        }
                    }
                    case READING_MATERIALS -> materials.add(readMaterial(line));
                    case READING_WEIGHTS -> weights.add(readWeight(line));
                }
            }
    
            int vertexCount = vertices.size();
            model.vertices = vertexCount;
            model.verticesXCoordinate = new int[vertexCount];
            model.verticesYCoordinate = new int[vertexCount];
            model.verticesZCoordinate = new int[vertexCount];
            boolean hasWeights = !weights.isEmpty();
            if (hasWeights) {
                model.vertexWeights = new int[vertexCount];
            }
    
            for (int i = 0; i < vertexCount; i++) {
                MqoVertex vertex = vertices.get(i);
                model.verticesXCoordinate[i] = vertex.x();
                model.verticesYCoordinate[i] = vertex.y();
                model.verticesZCoordinate[i] = vertex.z();
                if (hasWeights && weights.size() > i) {
                    model.vertexWeights[i] = weights.get(i);
                }
            }
    
            int faceCount = faces.size();
            model.triangleCount = faceCount;
            model.faceIndicesA = new int[faceCount];
            model.faceIndicesB = new int[faceCount];
            model.faceIndicesC = new int[faceCount];
            model.triangleColors = new short[faceCount];
            model.triangleInfo = new int[faceCount];
    
            boolean hasAlpha = materials.stream()
                    .filter(Objects::nonNull)
                    .anyMatch(material -> material.alpha() != 255);
    
            boolean hasPriorities = !priorities.isEmpty();
            boolean hasTriangleSkins = !triangleSkins.isEmpty();
    
            if (hasAlpha) {
                model.faceAlpha = new int[faceCount];
            }
    
            if (hasPriorities) {
                model.trianglePriorities = new byte[faceCount];
            }
    
            if (hasTriangleSkins) {
                model.triangleSkin = new int[faceCount];
            }
    
            for (int i = 0; i < faceCount; i++) {
                MqoFace face = faces.get(i);
                Vector3i indices = face.indices();
                RSMaterial material = materials.get(face.materialIndex());
                model.faceIndicesA[i] = indices.getX();
                model.faceIndicesB[i] = indices.getY();
                model.faceIndicesC[i] = indices.getZ();
    
                if (material != null) {
                    model.triangleColors[i] = (short) material.id();
                    if (hasAlpha) {
                        model.faceAlpha[i] = material.alpha();
                    }
                }
    
                if (hasTriangleSkins) {
                    model.triangleSkin[i] = triangleSkins.get(i);
                }
    
                if (hasPriorities) {
                    model.trianglePriorities[i] = priorities.get(i);
                }
            }
    
            // Convert texture coordinates
            Map<Integer, List<Vector3i>> coordinateMap = new HashMap<>();
    
            for (int i = 0; i < faceCount; i++) {
                MqoFace face = faces.get(i);
                UVCoordinate uv = face.uv();
                if (uv != null) {
                    List<Vector3i> texIndices = model.computePMNFromUV(i, uv);
                    coordinateMap.put(i, texIndices);
                }
            }
    
            int len = coordinateMap.size() * 3;
    
            List<Integer> newVerticesX = new ArrayList<>(vertexCount + len);
            List<Integer> newVerticesY = new ArrayList<>(vertexCount + len);
            List<Integer> newVerticesZ = new ArrayList<>(vertexCount + len);
    
            model.texturedFaces = len / 3;
            model.textureVertexA = new short[len / 3];
            model.textureVertexB = new short[len / 3];
            model.textureVertexC = new short[len / 3];
    
    
            for (int i = 0; i < vertexCount; i++) {
                newVerticesX.add(model.verticesXCoordinate[i]);
                newVerticesY.add(model.verticesYCoordinate[i]);
                newVerticesZ.add(model.verticesZCoordinate[i]);
            }
    
            int mask = 2;
            for (Map.Entry<Integer, List<Vector3i>> entry : coordinateMap.entrySet()) {
                int index = entry.getKey();
                List<Vector3i> coordinates = entry.getValue();
                for (int i = 0; i < 3; i++) {
                    Vector3i coordinate = coordinates.get(i);
                    newVerticesX.add(coordinate.getX());
                    newVerticesY.add(coordinate.getY());
                    newVerticesZ.add(coordinate.getZ());
                }
    
                int size = newVerticesX.size();
                model.triangleInfo[index] = mask;
                model.textureVertexA[mask >> 2] = (short) (size - 3);
                model.textureVertexB[mask >> 2] = (short) (size - 2);
                model.textureVertexC[mask >> 2] = (short) (size - 1);
                mask += 4;
            }
    
            int newLen = newVerticesX.size();
    
            model.verticesXCoordinate = new int[newLen];
            model.verticesYCoordinate = new int[newLen];
            model.verticesZCoordinate = new int[newLen];
            model.vertices = newLen;
    
            if (model.vertexWeights != null) {
                int[] newWeights = new int[newLen];
                System.arraycopy(model.vertexWeights, 0, newWeights, 0, model.vertexWeights.length);
                model.vertexWeights = newWeights;
            }
    
            for (int i = 0; i < newLen; i++) {
                model.verticesXCoordinate[i] = newVerticesX.get(i);
                model.verticesYCoordinate[i] = newVerticesY.get(i);
                model.verticesZCoordinate[i] = newVerticesZ.get(i);
            }
    
            return model;
        }
    
        private State determineState(String line) {
            if (line.startsWith("}")) {
                return State.END_READING;
            }
    
            State state = State.SKIP;
            if (line.matches("vertex \\d+ \\{")) {
                state = State.READING_VERTICES;
            } else if (line.matches("face \\d+ \\{")) {
                state = State.READING_FACES;
            } else if (line.matches("Material \\d+ \\{")) {
                state = State.READING_MATERIALS;
            } else if (line.startsWith("weit")) {
                state = State.READING_WEIGHTS;
            }
            return state;
        }
    
        private StateType determineType(String line) {
            if (line.contains("MODEL")) {
                return StateType.MODEL;
            } else if (line.contains("PRIORITY")) {
                return StateType.PRIORITY;
            } else if (line.contains("TSKIN")) {
                return StateType.TRIANGLE_SKIN;
            }
    
            return null;
        }
    
        private enum State {
            SKIP, END_READING, READING_VERTICES, READING_FACES, READING_MATERIALS, READING_WEIGHTS
        }
    }
    Spoiler for pics:

    Exported fire cape model:


    Imported fire cape model:


    (the light settings are different in mqo which is why it looks a little different colorwise) but the texture mapping is about 99% accurate(the 1% loss comes from the fact that vertices are stored as integers in the rs client and the mapping is defined by a triangle)
    if you want to export an already existing textured model and are not planning to edit any UV's in mqo then you can just store the vertices/indices used for texture mapping or for simpler models(like fire cape) you can just bruteforce it, simpler because that scales as O(n ^ 3) which will result in the original coordinates
    NOTE: for any questions on how the texturing technique rs uses works feel free to ask me, i actually spent quite a bit of time researching it & understanding how it works
    Last edited by Suic; 02-24-2021 at 05:49 PM. Reason: forgot a null check for tskins
    Reply With Quote  
     


  2. #2  
    number 1 sponsor

    Join Date
    Aug 2017
    Posts
    877
    Thanks given
    658
    Thanks received
    652
    Rep Power
    4691
    good stufff
    Reply With Quote  
     

  3. Thankful user:


  4. #3  
    ᗪ乇尺乇乙乙乇ᗪ

    lumplum's Avatar
    Join Date
    Nov 2015
    Posts
    1,121
    Thanks given
    484
    Thanks received
    1,371
    Rep Power
    5000


    at long last we finally get a much better tool. tyvm Thesuic12 for this great addition!
    [Only registered and activated users can see links. ]
    Reply With Quote  
     

  5. Thankful user:


  6. #4  
    nice


    Join Date
    Jul 2014
    Posts
    641
    Thanks given
    271
    Thanks received
    300
    Rep Power
    2739
    Quote Originally Posted by lumplum View Post


    at long last we finally get a much better tool. tyvm Thesuic12 for this great addition!
    np loomploom
    Reply With Quote  
     

  7. #5  
    Contributor
    Kris's Avatar
    Join Date
    Jun 2016
    Age
    23
    Posts
    3,523
    Thanks given
    689
    Thanks received
    2,290
    Rep Power
    4916
    Solid release, wrote one of these myself a couple years back, never sorted the few bugs I had out with it though.
    Reply With Quote  
     

  8. Thankful users:


  9. #6  
    Registered Member

    Join Date
    Dec 2009
    Posts
    737
    Thanks given
    308
    Thanks received
    386
    Rep Power
    740
    Nice. Good job on the weights.
    link removed
    Reply With Quote  
     

  10. Thankful user:


  11. #7  
    Registered Member
    Tamatea's Avatar
    Join Date
    Aug 2010
    Posts
    1,286
    Thanks given
    377
    Thanks received
    332
    Rep Power
    2318
    Didn't realize one of these wasn't public, would've released ages ago!! Nice contribution sir.

    For anyone wanting to research further I mirrored the original Datmaker code [Only registered and activated users can see links. ] (0 credits to myself for this)

    [Only registered and activated users can see links. ]



    Reply With Quote  
     

  12. Thankful user:


  13. #8  
    WVWVWVWVWVWVWVW

    _jordan's Avatar
    Join Date
    Nov 2012
    Age
    23
    Posts
    2,837
    Thanks given
    57
    Thanks received
    1,485
    Rep Power
    5000
    Very nice

    Spoiler for signature:
    When your vision is crystal clear, they say it's razor-sharp
    A focused mind is said to be like a knife, but the scalpel is wisdom
    All one can do is accept that life is a double-edged sword
    Stay on the edge

    -



    Reply With Quote  
     

  14. Thankful user:


  15. #9  
    Registered Member
    Join Date
    Nov 2020
    Posts
    31
    Thanks given
    3
    Thanks received
    11
    Rep Power
    12
    thanks man nice work
    Reply With Quote  
     

  16. Thankful user:


  17. #10  
    🎶 As you're falling down 🎶

    uint32_t's Avatar
    Join Date
    Feb 2015
    Posts
    1,351
    Thanks given
    5,735
    Thanks received
    696
    Rep Power
    3709
    Nice contribution
    [Only registered and activated users can see links. ]

    Quote Originally Posted by Idiot Bird View Post
    Quote Originally Posted by Velocity View Post
    lol np mate looks like the community brought ur rep down to ur IQ
    Not too sure about that, it's at 0 . It would have to go minus to even be remotely close to his IQ.
    Reply With Quote  
     

  18. Thankful user:



Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Replies: 4
    Last Post: 08-24-2019, 06:32 PM
  2. rs3 model viewer with .mqo export
    By GelS in forum Buying
    Replies: 1
    Last Post: 08-24-2019, 03:34 PM
  3. Replies: 143
    Last Post: 04-12-2017, 03:40 AM
  4. Replies: 7
    Last Post: 07-13-2016, 10:48 PM
  5. Replies: 67
    Last Post: 12-02-2007, 05:50 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •