Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import javafx.application.Application;
- import javafx.scene.*;
- import javafx.scene.image.WritableImage;
- import javafx.scene.layout.AnchorPane;
- import javafx.scene.paint.Color;
- import javafx.scene.paint.PhongMaterial;
- import javafx.scene.shape.*;
- import javafx.scene.transform.Rotate;
- import javafx.scene.transform.Scale;
- import javafx.scene.transform.Translate;
- import javafx.stage.Stage;
- import java.util.*;
- import java.util.stream.IntStream;
- /**
- * To run this, create a new class (e.g. RunTriangleMeshTransparency), create a main method and add this line:
- * Application.launch(TriangleMeshTransparency.class);
- * then run that class
- */
- public class TriangleMeshTransparency extends Application {
- private static final int WIDTH = 800;
- private static final int HEIGHT = 600;
- private AnchorPane modelPane;
- private Group scene;
- private SubScene subScene;
- private final float MODEL_SCALE = 0.03f;
- private RSTriangleMeshView meshView;
- @Override
- public void start(Stage primaryStage) throws Exception {
- modelPane = new AnchorPane();
- modelPane.setPrefWidth(WIDTH);
- modelPane.setPrefHeight(HEIGHT);
- initScene();
- // data for the same mesh i used as an example in my question
- // write palette
- /*try {
- ImageIO.write(SwingFXUtils.fromFXImage(atlas, null), "png", new File("./palette.png"));
- } catch (IOException e) {
- e.printStackTrace();
- }*/
- int idx = 0;
- for (int i = 0; i < model.triangleCount; i++) {
- int faceA = model.faceIndicesA[i];
- int faceB = model.faceIndicesB[i];
- int faceC = model.faceIndicesC[i];
- int vertexIndex1 = mesh.addVertex(faceA, model.verticesXCoordinate[faceA], model.verticesYCoordinate[faceA], model.verticesZCoordinate[faceA]);
- int vertexIndex2 = mesh.addVertex(faceB, model.verticesXCoordinate[faceB], model.verticesYCoordinate[faceB], model.verticesZCoordinate[faceB]);
- int vertexIndex3 = mesh.addVertex(faceC, model.verticesXCoordinate[faceC], model.verticesYCoordinate[faceC], model.verticesZCoordinate[faceC]);
- float u = (idx % width + 0.5f) / width;
- float v = (idx / width + 0.5f) / (float) atlas.getHeight();
- int texIndex1 = mesh.addUV(u, v);
- int texIndex2 = mesh.addUV(u, v);
- int texIndex3 = mesh.addUV(u, v);
- mesh.getFaces().addAll(
- vertexIndex1, texIndex1,
- vertexIndex2, texIndex2,
- vertexIndex3, texIndex3
- );
- idx++;
- }
- RSTriangleMeshView view = new RSTriangleMeshView(mesh);
- PhongMaterial mat = new PhongMaterial();
- mat.setDiffuseMap(atlas);
- view.setMaterial(mat);
- view.getTransforms().add(new Scale(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE));
- view.setDepthTest(DepthTest.ENABLE);
- view.setCullFace(CullFace.BACK);
- meshView = view;
- }
- private void initScene() {
- scene = new Group();
- //Group grid = new Grid3D().create(48f, 1.25f);
- //scene.getChildren().add(grid);
- subScene = createScene3D();
- scene.getChildren().add(new AmbientLight(Color.WHITE));
- modelPane.getChildren().addAll(subScene);
- }
- private SubScene createScene3D() {
- SubScene scene3d = new SubScene(scene, modelPane.getPrefWidth(), modelPane.getPrefHeight(), true, SceneAntialiasing.BALANCED);
- scene3d.setFill(Color.rgb(25, 25, 25));
- new OrbitCamera(scene3d, scene);
- return scene3d;
- }
- /*
- * Orbit camera
- */
- private static class OrbitCamera {
- private final SubScene subScene;
- private final Group root3D;
- private final double MAX_ZOOM = 300.0;
- public OrbitCamera(SubScene subScene, Group root) {
- this.subScene = subScene;
- this.root3D = root;
- init();
- }
- private void init() {
- camera.setNearClip(0.1D);
- camera.setFarClip(MAX_ZOOM * 1.15D);
- camera.getTransforms().addAll(
- yUpRotate,
- cameraPosition,
- cameraLookXRotate,
- cameraLookZRotate
- );
- Group rotateGroup = new Group();
- try {
- rotateGroup.getChildren().addAll(cameraXform);
- } catch (Exception e) {
- e.printStackTrace();
- }
- cameraXform.ry.setAngle(0);
- cameraXform.rx.setAngle(-18);
- cameraXform.getChildren().add(cameraXform2);
- cameraXform2.getChildren().add(cameraXform3);
- cameraXform3.getChildren().add(camera);
- cameraPosition.setZ(-cameraDistance);
- root3D.getChildren().addAll(rotateGroup);
- subScene.setCamera(camera);
- subScene.setOnScroll(event -> {
- double zoomFactor = 1.05;
- double deltaY = event.getDeltaY();
- if (deltaY < 0) {
- zoomFactor = 2.0 - zoomFactor;
- }
- double z = cameraPosition.getZ() / zoomFactor;
- z = Math.max(z, -MAX_ZOOM);
- z = Math.min(z, 10.0);
- cameraPosition.setZ(z);
- });
- subScene.setOnMousePressed(event -> {
- if (!event.isAltDown()) {
- dragStartX = event.getSceneX();
- dragStartY = event.getSceneY();
- dragStartRotateX = cameraXRotate.getAngle();
- dragStartRotateY = cameraYRotate.getAngle();
- mousePosX = event.getSceneX();
- mousePosY = event.getSceneY();
- mouseOldX = event.getSceneX();
- mouseOldY = event.getSceneY();
- }
- });
- subScene.setOnMouseDragged(event -> {
- if (!event.isAltDown()) {
- double modifier = 1.0;
- double modifierFactor = 0.3;
- if (event.isControlDown()) modifier = 0.1;
- if (event.isSecondaryButtonDown()) modifier = 0.035;
- mouseOldX = mousePosX;
- mouseOldY = mousePosY;
- mousePosX = event.getSceneX();
- mousePosY = event.getSceneY();
- mouseDeltaX = mousePosX - mouseOldX;
- mouseDeltaY = mousePosY - mouseOldY;
- double flip = -1.0;
- if (event.isSecondaryButtonDown()) {
- double newX = cameraXform2.t.getX() + flip * mouseDeltaX * modifierFactor * modifier * 2.0;
- double newY = cameraXform2.t.getY() + 1.0 * -mouseDeltaY * modifierFactor * modifier * 2.0;
- cameraXform2.t.setX(newX);
- cameraXform2.t.setY(newY);
- } else if (event.isPrimaryButtonDown()) {
- double yAngle = cameraXform.ry.getAngle() - 1.0 * -mouseDeltaX * modifierFactor * modifier * 2.0;
- double xAngle = cameraXform.rx.getAngle() + flip * mouseDeltaY * modifierFactor * modifier * 2.0;
- cameraXform.ry.setAngle(yAngle);
- cameraXform.rx.setAngle(xAngle);
- }
- }
- });
- }
- private final PerspectiveCamera camera = new PerspectiveCamera(true);
- private final Rotate cameraXRotate = new Rotate(-20.0, 0.0, 0.0, 0.0, Rotate.X_AXIS);
- private final Rotate cameraYRotate = new Rotate(-20.0, 0.0, 0.0, 0.0, Rotate.Y_AXIS);
- private final Rotate cameraLookXRotate = new Rotate(0.0, 0.0, 0.0, 0.0, Rotate.X_AXIS);
- private final Rotate cameraLookZRotate = new Rotate(0.0, 0.0, 0.0, 0.0, Rotate.Z_AXIS);
- private final Translate cameraPosition = new Translate(0.0, 0.0, 0.0);
- private Xform cameraXform = new Xform();
- private Xform cameraXform2 = new Xform();
- private Xform cameraXform3 = new Xform();
- private double cameraDistance = 25.0;
- private double dragStartX = 0;
- private double dragStartY = 0;
- private double dragStartRotateX = 0;
- private double dragStartRotateY = 0;
- private double mousePosX = 0;
- private double mousePosY = 0;
- private double mouseOldX = 0;
- private double mouseOldY = 0;
- private double mouseDeltaX = 0;
- private double mouseDeltaY = 0;
- private Rotate yUpRotate = new Rotate(0.0, 0.0, 0.0, 0.0, Rotate.X_AXIS);
- public Camera getCamera() {
- return camera;
- }
- public Xform getCameraXform() {
- return cameraXform;
- }
- }
- private static class Xform extends Group {
- Translate t = new Translate();
- Translate p = new Translate();
- public Rotate rx = new Rotate();
- public Rotate ry = new Rotate();
- Rotate rz = new Rotate();
- Scale s = new Scale();
- public Xform() {
- rx.setAxis(Rotate.X_AXIS);
- ry.setAxis(Rotate.Y_AXIS);
- rz.setAxis(Rotate.Z_AXIS);
- getTransforms().addAll(t, rz, ry, rx, s);
- }
- }
- // contains data + decoder for the mesh format i use
- private static class Model {
- public static Model decode(byte[] data) {
- Model model = new Model();
- model.decodeOld(data);
- return model;
- }
- public void decodeOld(byte[] data) {
- TriangleMeshTransparency.Buffer first = new TriangleMeshTransparency.Buffer(data);
- TriangleMeshTransparency.Buffer second = new TriangleMeshTransparency.Buffer(data);
- TriangleMeshTransparency.Buffer third = new TriangleMeshTransparency.Buffer(data);
- TriangleMeshTransparency.Buffer fourth = new TriangleMeshTransparency.Buffer(data);
- TriangleMeshTransparency.Buffer fifth = new TriangleMeshTransparency.Buffer(data);
- first.pos = data.length - 18;
- vertexCount = first.getUnsignedShort();
- triangleCount = first.getUnsignedShort();
- texturedTriangleCount = first.getUnsignedByte();
- int renderTypeOpcode = first.getUnsignedByte();
- int renderPriorityOpcode = first.getUnsignedByte();
- int triangleAlphaOpcode = first.getUnsignedByte();
- int triangleSkinOpcode = first.getUnsignedByte();
- int vertexLabelOpcode = first.getUnsignedByte();
- int verticesXCoordinateOffset = first.getUnsignedShort();
- int verticesYCoordinateOffset = first.getUnsignedShort();
- int verticesZCoordinateOffset = first.getUnsignedShort();
- int triangleIndicesOffset = first.getUnsignedShort();
- int pos = 0;
- int vertexFlagOffset = pos;
- pos += vertexCount;
- int triangleCompressTypeOffset = pos;
- pos += triangleCount;
- int facePriorityOffset = pos;
- if (renderPriorityOpcode == 255) {
- pos += triangleCount;
- }
- int triangleSkinOffset = pos;
- if (triangleSkinOpcode == 1) {
- pos += triangleCount;
- }
- int renderTypeOffset = pos;
- if (renderTypeOpcode == 1) {
- pos += triangleCount;
- }
- int vertexLabelsOffset = pos;
- if (vertexLabelOpcode == 1) {
- pos += vertexCount;
- }
- int triangleAlphaOffset = pos;
- if (triangleAlphaOpcode == 1) {
- pos += triangleCount;
- }
- int indicesOffset = pos;
- pos += triangleIndicesOffset;
- int triangleColorOffset = pos;
- pos += triangleCount * 2;
- int textureOffset = pos;
- pos += texturedTriangleCount * 6;
- int xOffset = pos;
- pos += verticesXCoordinateOffset;
- int yOffset = pos;
- pos += verticesYCoordinateOffset;
- int zOffset = pos;
- verticesXCoordinate = new int[vertexCount];
- verticesYCoordinate = new int[vertexCount];
- verticesZCoordinate = new int[vertexCount];
- faceIndicesA = new int[triangleCount];
- faceIndicesB = new int[triangleCount];
- faceIndicesC = new int[triangleCount];
- if (texturedTriangleCount > 0) {
- textureMap = new short[texturedTriangleCount];
- textureVertexA = new short[texturedTriangleCount];
- textureVertexB = new short[texturedTriangleCount];
- textureVertexC = new short[texturedTriangleCount];
- }
- if (vertexLabelOpcode == 1)
- vertexLabels = new int[vertexCount];
- if (renderTypeOpcode == 1) {
- triangleInfo = new int[triangleCount];
- faceTexture = new short[triangleCount];
- triangleMaterial = new int[triangleCount];
- faceTextureMasks = new byte[triangleCount];
- }
- if (renderPriorityOpcode == 255)
- trianglePriorities = new byte[triangleCount];
- else
- modelPriority = (byte) renderPriorityOpcode;
- if (triangleAlphaOpcode == 1)
- triangleAlpha = new int[triangleCount];
- if (triangleSkinOpcode == 1)
- triangleLabels = new int[triangleCount];
- triangleColors = new int[triangleCount];
- first.pos = vertexFlagOffset;
- second.pos = xOffset;
- third.pos = yOffset;
- fourth.pos = zOffset;
- fifth.pos = vertexLabelsOffset; // 18 +
- int baseX = 0;
- int baseY = 0;
- int baseZ = 0;
- for (int point = 0; point < vertexCount; point++) {
- int flag = first.getUnsignedByte();
- int x = 0;
- if ((flag & 0x1) != 0) {
- x = second.getSignedSmart();
- }
- int y = 0;
- if ((flag & 0x2) != 0) {
- y = third.getSignedSmart();
- }
- int z = 0;
- if ((flag & 0x4) != 0) {
- z = fourth.getSignedSmart();
- }
- verticesXCoordinate[point] = baseX + x;
- verticesYCoordinate[point] = baseY + y;
- verticesZCoordinate[point] = baseZ + z;
- baseX = verticesXCoordinate[point];
- baseY = verticesYCoordinate[point];
- baseZ = verticesZCoordinate[point];
- if (vertexLabelOpcode == 1) {
- vertexLabels[point] = fifth.getUnsignedByte();
- }
- }
- first.pos = triangleColorOffset;
- second.pos = renderTypeOffset;
- third.pos = facePriorityOffset;
- fourth.pos = triangleAlphaOffset;
- fifth.pos = triangleSkinOffset;
- for (int face = 0; face < triangleCount; face++) {
- int color = first.getUnsignedShort();
- triangleColors[face] = color;
- if (renderTypeOpcode == 1) {
- triangleInfo[face] = second.getUnsignedByte();
- }
- if (renderPriorityOpcode == 255) {
- trianglePriorities[face] = third.getSignedByte();
- }
- if (triangleAlphaOpcode == 1) {
- triangleAlpha[face] = fourth.getUnsignedByte();
- }
- if (triangleSkinOpcode == 1) {
- triangleLabels[face] = fifth.getUnsignedByte();
- }
- }
- first.pos = indicesOffset;
- second.pos = triangleCompressTypeOffset;
- int a = 0;
- int b = 0;
- int c = 0;
- int offset = 0;
- int coordinate;
- for (int face = 0; face < triangleCount; face++) {
- int opcode = second.getUnsignedByte();
- if (opcode == 1) {
- a = (first.getSignedSmart() + offset);
- offset = a;
- b = (first.getSignedSmart() + offset);
- offset = b;
- c = (first.getSignedSmart() + offset);
- offset = c;
- faceIndicesA[face] = a;
- faceIndicesB[face] = b;
- faceIndicesC[face] = c;
- }
- if (opcode == 2) {
- b = c;
- c = (first.getSignedSmart() + offset);
- offset = c;
- faceIndicesA[face] = a;
- faceIndicesB[face] = b;
- faceIndicesC[face] = c;
- }
- if (opcode == 3) {
- a = c;
- c = (first.getSignedSmart() + offset);
- offset = c;
- faceIndicesA[face] = a;
- faceIndicesB[face] = b;
- faceIndicesC[face] = c;
- }
- if (opcode == 4) {
- coordinate = a;
- a = b;
- b = coordinate;
- c = (first.getSignedSmart() + offset);
- offset = c;
- faceIndicesA[face] = a;
- faceIndicesB[face] = b;
- faceIndicesC[face] = c;
- }
- }
- first.pos = textureOffset;
- for (int face = 0; face < texturedTriangleCount; face++) {
- textureMap[face] = 0;
- textureVertexA[face] = (short) first.getUnsignedShort();
- textureVertexB[face] = (short) first.getUnsignedShort();
- textureVertexC[face] = (short) first.getUnsignedShort();
- }
- if (triangleInfo == null) {
- triangleInfo = new int[triangleCount];
- }
- }
- public int vertexCount;
- public int triangleCount;
- public int[] verticesXCoordinate;
- public int[] verticesYCoordinate;
- public int[] verticesZCoordinate;
- public int[] faceIndicesA;
- public int[] faceIndicesB;
- public int[] faceIndicesC;
- public int[] triangleInfo;
- public byte[] trianglePriorities;
- public int[] triangleAlpha;
- public int[] triangleColors;
- public byte modelPriority = 0;
- public int texturedTriangleCount;
- public short[] textureVertexA;
- public short[] textureVertexB;
- public short[] textureVertexC;
- public int[] vertexLabels;
- public int[] triangleLabels;
- public int[] triangleMaterial;
- public short[] faceTexture;
- public short[] textureMap;
- public byte[] faceTextureMasks;
- }
- private static class Buffer {
- public Buffer(byte[] payload) {
- this.payload = payload;
- this.pos = 0;
- }
- public int getUnsignedByte() {
- return this.payload[this.pos++] & 0xff;
- }
- public byte getSignedByte() {
- return this.payload[this.pos++];
- }
- public int getUnsignedShort() {
- this.pos += 2;
- return ((this.payload[this.pos - 2] & 0xff) << 8) + (this.payload[this.pos - 1] & 0xff);
- }
- public int getSignedSmart() {
- int value = this.payload[this.pos] & 0xff;
- if (value < 128) {
- return this.getUnsignedByte() - 64;
- } else {
- return this.getUnsignedShort() - 49152;
- }
- }
- public byte[] payload;
- public int pos;
- }
- private static class ColorUtils {
- public static int getBlue(int rgb) {
- return rgb & 0xFF;
- }
- public static int getGreen(int rgb) {
- return (rgb >> 8) & 0xFF;
- }
- public static int getRed(int rgb) {
- return (rgb >> 16) & 0xFF;
- }
- public static Color getJFXColor(int rgb, double opacity) {
- return Color.color(getRed(rgb) / 255.0, getGreen(rgb) / 255.0, getBlue(rgb) / 255.0, opacity);
- }
- }
- // just a wrapper for TriangleMesh, includes helper methods like addVertex, addUV
- private static class RSTriangleMesh extends TriangleMesh {
- private final Map<Integer, Integer> vertexMap = new HashMap<>();
- public int addVertex(int vertex, int x, int y, int z) {
- int cur = getPoints().size() / 3;
- getPoints().addAll(x, y, z);
- vertexMap.putIfAbsent(vertex, cur);
- return vertexMap.getOrDefault(vertex, 0);
- }
- public int addUV(float u, float v) {
- int cur = getTexCoords().size() / 2;
- getTexCoords().addAll(u, v);
- return cur;
- }
- }
- private static class RSTriangleMeshView extends MeshView {
- public RSTriangleMeshView(TriangleMeshTransparency.RSTriangleMesh mesh) {
- super(mesh);
- }
- public TriangleMeshTransparency.RSTriangleMesh toRSTriangleMesh() {
- return (TriangleMeshTransparency.RSTriangleMesh) getMesh();
- }
- }
- private static class HSLPalette {
- public static int[] table = new int[128 * 512];
- static {
- generatePalette(0.80000000000000004D); // default is 0.8 in the engine
- }
- public static void generatePalette(double brightness) {
- int index = 0;
- for (int y = 0; y < 512; y++) {
- double hue = ((double) (y / 8) / 64.0) + 0.0078125;
- double saturation = ((double) (y & 0x7) / 8.0) + 0.0625;
- for (int x = 0; x < 128; x++) {
- double lightness = (double) x / 128.0;
- double red = lightness;
- double green = lightness;
- double blue = lightness;
- if (saturation != 0.0) {
- double a;
- if (lightness < 0.5) {
- a = lightness * (1.0 + saturation);
- } else {
- a = (lightness + saturation) - (lightness * saturation);
- }
- double b = (2.0 * lightness) - a;
- double fRed = hue + (1.0 / 3.0);
- double fBlue = hue - (1.0 / 3.0);
- if (fRed > 1.0) fRed--;
- if (fBlue < 0.0) fBlue++;
- red = hueToRgb(fRed, a, b);
- green = hueToRgb(hue, a, b);
- blue = hueToRgb(fBlue, a, b);
- }
- table[index++] = applyBrightness(((int) (red * 256.0) << 16) | ((int) (green * 256.0) << 8) | (int) (blue * 256.0), brightness);
- }
- }
- }
- private static double hueToRgb(double value, double a, double b) {
- if ((6.0 * value) < 1.0)
- return b + ((a - b) * 6.0 * value);
- if (2.0 * value < 1.0)
- return a;
- if (3.0 * value < 2.0)
- return b + ((a - b) * ((2.0 / 3.0) - value) * 6.0);
- return b;
- }
- public static int applyBrightness(int rgb, double brightness) {
- double r = (double) (rgb >> 16) / 256.0;
- double g = (double) (rgb >> 8 & 0xff) / 256.0;
- double b = (double) (rgb & 0xff) / 256.0;
- r = Math.pow(r, brightness);
- g = Math.pow(g, brightness);
- b = Math.pow(b, brightness);
- return ((int) (r * 256.0) << 16) + ((int) (g * 256.0) << 8) + (int) (b * 256.0);
- }
- public static int getRgbForHsl(int hsl) {
- if (hsl >= table.length) {
- return 0;
- }
- return table[hsl & 0xFFFF];
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement