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());
}
}
Triangle -> UVCode: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;
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:
[Only registered and activated users can see links. Click Here To Register...]
Imported fire cape model:
[Only registered and activated users can see links. Click Here To Register...]
(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);
}
