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<>());
});
}
}
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;