Thread: MQO Exporter & Importer

Page 1 of 2 12 LastLast
Results 1 to 10 of 15
  1. #1 MQO Exporter & Importer 
    nice


    Join Date
    Jul 2014
    Posts
    740
    Thanks given
    382
    Thanks received
    562
    Rep Power
    4239
    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:
    Attached image

    Imported fire cape model:
    Attached image

    (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


    Edit: since quite a few people have asked me about the few missing methods(like converting colors, or the map method) i might aswell post them:

    Spoiler for some methods:

    Code:
        public static float map(float value, float iStart, float iStop, float oStart, float oStop) {
            return oStart + (oStop - oStart) * ((value - iStart) / (iStop - iStart));
        }
    
        public static Color rs2HSLToColor(int hsl, int alpha) {
            //not sure if this is 100% correct, but i use it to build the table, so it doesn't matter
            int transparency = alpha;
            if (transparency <= 0) {
                transparency = 255;
            }
    
            int hue = hsl >> 10 & 0x3f;
            int sat = hsl >> 7 & 0x07;
            int bri = hsl & 0x7f;
            java.awt.Color awtCol = java.awt.Color.getHSBColor((float) hue / 63, (float) sat / 7, (float) bri / 127);
            double r = awtCol.getRed() / 255.0;
            double g = awtCol.getGreen() / 255.0;
            double b = awtCol.getBlue() / 255.0;
            return Color.color(r, g, b, transparency / 255.0);
        }
    
        public static int colorToHSL(Color color) {
            return convertToHSL((int) (color.getRed() * 255D), (int) (color.getGreen() * 255D), (int) (color
                    .getBlue() * 255D));
        }
    
        public static int convertToHSL(int red, int green, int blue) {
            float[] HSB = java.awt.Color.RGBtoHSB(red, green, blue, null);
            float hue = (HSB[0]);
            float saturation = (HSB[1]);
            float brightness = (HSB[2]);
            int encode_hue = (int) (hue * 63f);            //to 6-bits
            int encode_saturation = (int) (saturation * 7f);        //to 3-bits
            int encode_brightness = (int) (brightness * 127f);    //to 7-bits
            return (encode_hue << 10) + (encode_saturation << 7) + (encode_brightness);
        }
    Last edited by Suic; 04-17-2021 at 01:06 AM. Reason: :)
    Attached image
    Reply With Quote  
     


  2. #2  
    Registered Member
    rebecca's Avatar
    Join Date
    Aug 2017
    Posts
    1,071
    Thanks given
    862
    Thanks received
    915
    Rep Power
    5000
    good stufff
    Reply With Quote  
     

  3. Thankful user:


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

    lumplum's Avatar
    Join Date
    Nov 2015
    Posts
    1,145
    Thanks given
    529
    Thanks received
    1,463
    Rep Power
    5000
    Attached image

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

  5. Thankful user:


  6. #4  
    nice


    Join Date
    Jul 2014
    Posts
    740
    Thanks given
    382
    Thanks received
    562
    Rep Power
    4239
    Quote Originally Posted by lumplum View Post
    Attached image

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

  7. #5  
    Respected Member


    Kris's Avatar
    Join Date
    Jun 2016
    Age
    26
    Posts
    3,638
    Thanks given
    820
    Thanks received
    2,642
    Rep Power
    5000
    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
    774
    Thanks given
    367
    Thanks received
    455
    Rep Power
    927
    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,317
    Thanks given
    401
    Thanks received
    357
    Rep Power
    2457
    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 Here (0 credits to myself for this)
    Spoiler for sig too large:


    Attached image
    Attached image
    Reply With Quote  
     

  12. Thankful user:


  13. #8  
    WVWVWVWVWVWVWVW

    _jordan's Avatar
    Join Date
    Nov 2012
    Posts
    3,046
    Thanks given
    111
    Thanks received
    1,848
    Rep Power
    5000
    Very nice
    Attached image
    Attached image
    Reply With Quote  
     

  14. Thankful user:


  15. #9  
    Registered Member
    Join Date
    Nov 2020
    Posts
    32
    Thanks given
    8
    Thanks received
    12
    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,396
    Thanks given
    6,177
    Thanks received
    776
    Rep Power
    5000
    Nice contribution
    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:


Page 1 of 2 12 LastLast

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
  •