summaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2024-06-16 01:29:43 -0500
committerLibravatar bigfoot547 <[email protected]>2024-06-16 01:29:43 -0500
commit625ddde31025f53b246ba3aaafebbba96aa21c9f (patch)
treefe2d87ad58aed1f64d151568d9fa522715815611 /src/main/java
parentreadme (diff)
add separate control window
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/dev/figboot/cuberender/math/Vector4f.java7
-rw-r--r--src/main/java/dev/figboot/cuberender/state/BlendMode.java7
-rw-r--r--src/main/java/dev/figboot/cuberender/test/GraphicsPanel.java531
-rw-r--r--src/main/java/dev/figboot/cuberender/test/TestWindow.java46
-rw-r--r--src/main/java/dev/figboot/cuberender/test/TestWindowControl.java204
5 files changed, 512 insertions, 283 deletions
diff --git a/src/main/java/dev/figboot/cuberender/math/Vector4f.java b/src/main/java/dev/figboot/cuberender/math/Vector4f.java
index 402b5ed..1995622 100644
--- a/src/main/java/dev/figboot/cuberender/math/Vector4f.java
+++ b/src/main/java/dev/figboot/cuberender/math/Vector4f.java
@@ -74,4 +74,11 @@ public class Vector4f {
target.w = this.w / len;
return target;
}
+
+ public void copyFrom(Vector4f src) {
+ x = src.x;
+ y = src.y;
+ z = src.z;
+ w = src.w;
+ }
}
diff --git a/src/main/java/dev/figboot/cuberender/state/BlendMode.java b/src/main/java/dev/figboot/cuberender/state/BlendMode.java
index 1c1acf4..9ea8d29 100644
--- a/src/main/java/dev/figboot/cuberender/state/BlendMode.java
+++ b/src/main/java/dev/figboot/cuberender/state/BlendMode.java
@@ -8,6 +8,13 @@ import lombok.RequiredArgsConstructor;
@Getter
public enum BlendMode {
DISABLE((inOutColor, prev) -> inOutColor.w = 1),
+ BINARY((inOutColor, prev) -> {
+ if (inOutColor.w < 0.5) {
+ inOutColor.copyFrom(prev);
+ } else {
+ inOutColor.w = 1;
+ }
+ }),
BLEND_OVER((inOutColor, prev) -> {
float pAlphaFactor = prev.w * (1 - inOutColor.w);
float aOut = inOutColor.w + pAlphaFactor;
diff --git a/src/main/java/dev/figboot/cuberender/test/GraphicsPanel.java b/src/main/java/dev/figboot/cuberender/test/GraphicsPanel.java
index 2f7f1fc..10417e8 100644
--- a/src/main/java/dev/figboot/cuberender/test/GraphicsPanel.java
+++ b/src/main/java/dev/figboot/cuberender/test/GraphicsPanel.java
@@ -5,6 +5,8 @@ import dev.figboot.cuberender.state.BlendMode;
import dev.figboot.cuberender.state.Framebuffer;
import dev.figboot.cuberender.state.Mesh;
import dev.figboot.cuberender.state.Texture;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
import javax.imageio.ImageIO;
import javax.swing.*;
@@ -21,98 +23,36 @@ public class GraphicsPanel extends JPanel {
private Framebuffer framebuffer;
private final EnumMap<BodyPart, Mesh<?>> meshes = new EnumMap<>(BodyPart.class);
- private float xRot = 0, yRot = 0, capeRot = 0;
+
+ @Setter private boolean translucentModel = true, normalModel = true;
+ @Setter private float walkAngle, capeAngle, worldRotY, worldRotX, headPitch;
+
+ public static final int OVERLAY_HAT = 0x0001;
+ public static final int OVERLAY_TORSO = 0x0002;
+ public static final int OVERLAY_LEFT_ARM = 0x0004;
+ public static final int OVERLAY_RIGHT_ARM = 0x0008;
+ public static final int OVERLAY_LEFT_LEG = 0x0010;
+ public static final int OVERLAY_RIGHT_LEG = 0x0020;
+
+ public static final int OVERLAY_CAPE = 0x0100;
+
+ private int renderOverlayFlags;
private final long[] clrTime = new long[32];
private final long[] meshTime = new long[32];
private int tidx = 0;
boolean rollOver = false;
- private Matrix4f defaultTransform, capeTransform;
-
- private static void addCuboid(Mesh.Builder mb, float x1, float y1, float z1, float x2, float y2, float z2, float tx, float ty, float tspanX, float tspanY, float tspanZ, float aspect, int ibase) {
- mb.vertex(new Vector4f(x1, y1, z2), /* front */
- new Vector4f(x1, y2, z2),
- new Vector4f(x2, y2, z2),
- new Vector4f(x2, y1, z2),
-
- new Vector4f(x2, y1, z2), /* +X side */
- new Vector4f(x2, y2, z2),
- new Vector4f(x2, y2, z1),
- new Vector4f(x2, y1, z1),
-
- new Vector4f(x2, y1, z1), /* back */
- new Vector4f(x2, y2, z1),
- new Vector4f(x1, y2, z1),
- new Vector4f(x1, y1, z1),
-
- new Vector4f(x1, y1, z1), /* -X side */
- new Vector4f(x1, y2, z1),
- new Vector4f(x1, y2, z2),
- new Vector4f(x1, y1, z2),
-
- new Vector4f(x1, y1, z1), /* top */
- new Vector4f(x1, y1, z2),
- new Vector4f(x2, y1, z2),
- new Vector4f(x2, y1, z1),
-
- new Vector4f(x1, y2, z1), /* bottom */
- new Vector4f(x1, y2, z2),
- new Vector4f(x2, y2, z2),
- new Vector4f(x2, y2, z1))
- .normals(new Vector4f(0, 0, 1, 0),
- new Vector4f(0, 0, 1, 0),
-
- new Vector4f(1, 0, 0, 0),
- new Vector4f(1, 0, 0, 0),
-
- new Vector4f(0, 0, -1, 0),
- new Vector4f(0, 0, -1, 0),
-
- new Vector4f(-1, 0, 0, 0),
- new Vector4f(-1, 0, 0, 0),
-
- new Vector4f(0, -1, 0, 0),
- new Vector4f(0, -1, 0, 0),
-
- new Vector4f(0, 1, 0, 0),
- new Vector4f(0, 1, 0, 0))
- .texCoords(new Vector2f(tx, ty),
- new Vector2f(tx, ty - tspanY),
- new Vector2f(tx + tspanX, ty - tspanY),
- new Vector2f(tx + tspanX, ty),
-
- new Vector2f(tx + tspanX, ty),
- new Vector2f(tx + tspanX, ty - tspanY),
- new Vector2f(tx + tspanX + tspanZ, ty - tspanY),
- new Vector2f(tx + tspanX + tspanZ, ty),
-
- new Vector2f(tx + tspanX + tspanZ, ty),
- new Vector2f(tx + tspanX + tspanZ, ty - tspanY),
- new Vector2f(tx + 2 * tspanX + tspanZ, ty - tspanY),
- new Vector2f(tx + 2 * tspanX + tspanZ, ty),
-
- new Vector2f(tx - tspanZ, ty),
- new Vector2f(tx - tspanZ, ty - tspanY),
- new Vector2f(tx, ty - tspanY),
- new Vector2f(tx, ty),
-
- new Vector2f(tx, ty + tspanZ),
- new Vector2f(tx, ty),
- new Vector2f(tx + tspanX, ty),
- new Vector2f(tx + tspanX, ty + (tspanZ / aspect)),
-
- new Vector2f(tx + tspanX, ty + (tspanZ / aspect)),
- new Vector2f(tx + tspanX, ty),
- new Vector2f(tx + 2 * tspanX, ty),
- new Vector2f(tx + 2 * tspanX, ty + tspanZ))
- .indices(ibase, ibase + 1, ibase + 2, ibase, ibase + 2, ibase + 3,
- ibase + 4, ibase + 5, ibase + 6, ibase + 4, ibase + 6, ibase + 7,
- ibase + 8, ibase + 9, ibase + 10, ibase + 8, ibase + 10, ibase + 11,
- ibase + 12, ibase + 13, ibase + 14, ibase + 12, ibase + 14, ibase + 15,
- ibase + 16, ibase + 17, ibase + 18, ibase + 16, ibase + 18, ibase + 19,
- ibase + 20, ibase + 21, ibase + 22, ibase + 20, ibase + 22, ibase + 23);
- }
+ private final EnumMap<BodyPart, Matrix4f> transforms = new EnumMap<>(BodyPart.class);
+
+ private static final BodyPart[] MAIN_PARTS = new BodyPart[]{BodyPart.HEAD, BodyPart.TORSO, BodyPart.LEFT_ARM, BodyPart.RIGHT_ARM, BodyPart.LEFT_LEG, BodyPart.RIGHT_LEG};
+ private static final BodyPart[] MAIN_PARTS_SLIM = new BodyPart[]{BodyPart.HEAD, BodyPart.TORSO, BodyPart.LEFT_ARM_SLIM, BodyPart.RIGHT_ARM_SLIM, BodyPart.LEFT_LEG, BodyPart.RIGHT_LEG};
+
+ private final BodyPart[] OVERLAY_PARTS = new BodyPart[]{BodyPart.HAT, BodyPart.TORSO_OVERLAY, BodyPart.LEFT_ARM_OVERLAY, BodyPart.RIGHT_ARM_OVERLAY, BodyPart.LEFT_LEG_OVERLAY, BodyPart.RIGHT_LEG_OVERLAY};
+ private final BodyPart[] OVERLAY_PARTS_SLIM = new BodyPart[]{BodyPart.HAT, BodyPart.TORSO_OVERLAY, BodyPart.LEFT_ARM_OVERLAY_SLIM, BodyPart.RIGHT_ARM_OVERLAY_SLIM, BodyPart.LEFT_LEG_OVERLAY, BodyPart.RIGHT_LEG_OVERLAY};
+
+ private BodyPart[] overlayParts;
+ private BodyPart[] overlayPartsSlim;
private void copyLimbFlipped(BufferedImage src, BufferedImage target) {
for (int y = 4, maxY = src.getHeight(); y < maxY; ++y) {
@@ -138,7 +78,6 @@ public class GraphicsPanel extends JPanel {
g2d.drawImage(bi, 0, 0, 64, 32, null);
- // TODO: this is incomplete. the actual game mirrors the arm (so there is always an "inside" and an "outside")
copyLimbFlipped(bi.getSubimage(0, 16, 16, 16), realBI.getSubimage(16, 48, 16, 16));
copyLimbFlipped(bi.getSubimage(40, 16, 16, 16), realBI.getSubimage(32, 48, 16, 16));
g2d.dispose();
@@ -152,11 +91,6 @@ public class GraphicsPanel extends JPanel {
return realBI;
}
- private static Mesh.Builder defaultBuilder() {
- return new Mesh.Builder().attach(Mesh.AttachmentType.LIGHT_FACTOR, 1f)
- .attach(Mesh.AttachmentType.LIGHT_VECTOR, new Vector4f(0, 0, 1, 0));
- }
-
public GraphicsPanel() {
addComponentListener(new ComponentAdapter() {
@Override
@@ -166,7 +100,7 @@ public class GraphicsPanel extends JPanel {
});
BufferedImage bi;
- try (InputStream is = getClass().getResourceAsStream("/translucent.png")) {
+ try (InputStream is = getClass().getResourceAsStream("/skin2.png")) {
bi = ImageIO.read(is);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -184,119 +118,41 @@ public class GraphicsPanel extends JPanel {
}
Texture tex = new Texture(bi);
+ Texture capeTex = new Texture(capeBI);
- Mesh.Builder bodyBuilder = defaultBuilder().texture(tex);
-
- // TODO: slim model
- boolean slim = false;
- float overlayOffset = 1/64f;
- float aspect = tex.calcAspect();
-
- int idxbase = -24;
- // head
- addCuboid(bodyBuilder,
- -4 / 16f, -16 / 16f, -4 / 16f,
- 4 / 16f, -8 / 16f, 4 / 16f,
- 1 / 8f, 7 / 8f,
- 1 / 8f, 1 / 8f, 1 / 8f, aspect, idxbase += 24);
-
- // torso
- addCuboid(bodyBuilder,
- -4 / 16f, -8 / 16f, -2 / 16f,
- 4 / 16f, 4 / 16f, 2 / 16f,
- 5 / 16f, 11 / 16f,
- 1 / 8f, 3 / 16f, 1 / 16f, aspect, idxbase += 24);
-
- // left+right arm
- addCuboid(bodyBuilder,
- (slim ? -7 : -8) / 16f, -8 / 16f, -2 / 16f,
- -4 / 16f, 4 / 16f, 2 / 16f,
- 11 / 16f, 11 / 16f,
- (slim ? 3 / 64f : 1 / 16f), 3 / 16f, 1 / 16f, aspect, idxbase += 24);
- addCuboid(bodyBuilder,
- 4 / 16f, -8 / 16f, -2 / 16f,
- (slim ? 7 : 8) / 16f, 4 / 16f, 2 / 16f,
- 9 / 16f, 3 / 16f,
- (slim ? 3 / 64f : 1 / 16f), 3 / 16f, 1 / 16f, aspect, idxbase += 24);
-
- // left+right leg
- addCuboid(bodyBuilder,
- -4 / 16f, 4 / 16f, -2 / 16f,
- 0 / 16f, 16 / 16f, 2 / 16f,
- 1 / 16f, 11 / 16f,
- 1 / 16f, 3 / 16f, 1 / 16f, aspect, idxbase += 24);
- addCuboid(bodyBuilder,
- 0 / 16f, 4 / 16f, -2 / 16f,
- 4 / 16f, 16 / 16f, 2 / 16f,
- 5 / 16f, 3 / 16f,
- 1 / 16f, 3 / 16f, 1 / 16f, aspect, idxbase += 24);
- meshes.put(BodyPart.MAIN, bodyBuilder.build());
-
- Mesh.Builder hatBuilder = defaultBuilder().texture(tex);
- addCuboid(hatBuilder,
- -4 / 16f - overlayOffset, -16 / 16f - overlayOffset, -4 / 16f - overlayOffset,
- 4 / 16f + overlayOffset, -8 / 16f + overlayOffset, 4 / 16f + overlayOffset,
- 5 / 8f, 7 / 8f,
- 1 / 8f, 1 / 8f, 1 / 8f, aspect, 0);
-
- meshes.put(BodyPart.HAT, hatBuilder.build());
-
- overlayOffset *= 1.01f;
- Mesh.Builder torsoBuilder = defaultBuilder().texture(tex);
- addCuboid(torsoBuilder,
- -4 / 16f - overlayOffset, -8 / 16f - overlayOffset, -2 / 16f - overlayOffset,
- 4 / 16f + overlayOffset, 4 / 16f + overlayOffset, 2 / 16f + overlayOffset,
- 5 / 16f, 7 / 16f,
- 1 / 8f, 3 / 16f, 1 / 16f, aspect, 0);
-
- meshes.put(BodyPart.TORSO_OVERLAY, torsoBuilder.build());
-
- overlayOffset /= 1.01f;
- Mesh.Builder leftArmBuilder = defaultBuilder().texture(tex);
- addCuboid(leftArmBuilder,
- (slim ? -7 : -8) / 16f - overlayOffset, -8 / 16f - overlayOffset, -2 / 16f - overlayOffset,
- -4 / 16f + overlayOffset, 4 / 16f + overlayOffset, 2 / 16f + overlayOffset,
- 13 / 16f, 3 / 16f,
- (slim ? 3 / 64f : 1 / 16f), 3 / 16f, 1 / 16f, aspect, 0);
-
- meshes.put(BodyPart.LEFT_ARM_OVERLAY, leftArmBuilder.build());
-
- Mesh.Builder rightArmBuilder = defaultBuilder().texture(tex);
- addCuboid(rightArmBuilder,
- 4 / 16f - overlayOffset, -8 / 16f - overlayOffset, -2 / 16f - overlayOffset,
- (slim ? 7 : 8) / 16f + overlayOffset, 4 / 16f + overlayOffset, 2 / 16f + overlayOffset,
- 11 / 16f, 7 / 16f,
- (slim ? 3 / 64f : 1 / 16f), 3 / 16f, 1 / 16f, aspect, 0);
-
- meshes.put(BodyPart.RIGHT_ARM_OVERLAY, rightArmBuilder.build());
-
- overlayOffset /= 1.01f;
- Mesh.Builder leftLegBuilder = defaultBuilder().texture(tex);
- addCuboid(leftLegBuilder,
- -4 / 16f - overlayOffset, 4 / 16f - overlayOffset, -2 / 16f - overlayOffset,
- 0 / 16f + overlayOffset, 16 / 16f + overlayOffset, 2 / 16f + overlayOffset,
- 1 / 16f, 7 / 16f,
- 1 / 16f, 3 / 16f, 1 / 16f, aspect, 0);
- meshes.put(BodyPart.LEFT_LEG_OVERLAY, leftLegBuilder.build());
-
- overlayOffset /= 1.01f;
- Mesh.Builder rightLegBuilder = defaultBuilder().texture(tex);
- addCuboid(rightLegBuilder,
- 0 / 16f - overlayOffset, 4 / 16f - overlayOffset, -2 / 16f - overlayOffset,
- 4 / 16f + overlayOffset, 16 / 16f + overlayOffset, 2 / 16f + overlayOffset,
- 1 / 16f, 3 / 16f,
- 1 / 16f, 3 / 16f, 1 / 16f, aspect, 0);
- meshes.put(BodyPart.RIGHT_LEG_OVERLAY, rightLegBuilder.build());
+ for (BodyPart part : BodyPart.values()) {
+ meshes.put(part, part.toBuilder(part != BodyPart.CAPE ? tex : capeTex, 0)
+ .attach(Mesh.AttachmentType.LIGHT_FACTOR, 1f)
+ .attach(Mesh.AttachmentType.LIGHT_VECTOR, new Vector4f(0, 0, 1, 0)).build());
+ }
+ }
- Texture capeTex = new Texture(capeBI);
+ private void updateOverlayParts() {
+ int realOverlayParts = this.renderOverlayFlags & ~OVERLAY_CAPE;
+ int nParts = Integer.bitCount(realOverlayParts);
+ int idx = 0;
- Mesh.Builder capeBuilder = defaultBuilder().texture(capeTex);
- addCuboid(capeBuilder,
- -4/16f, 0, 0,
- 4/16f, 16/16f, 1/16f,
- 1/64f, 31/32f,
- 10/64f, 16/32f, 1/64f, capeTex.calcAspect(), 0);
- meshes.put(BodyPart.CAPE, capeBuilder.build());
+ if (nParts == 0) {
+ overlayParts = overlayPartsSlim = new BodyPart[0];
+ return;
+ }
+
+ overlayParts = new BodyPart[nParts];
+ overlayPartsSlim = new BodyPart[nParts];
+
+ for (int i = 0; idx < nParts && i < OVERLAY_PARTS.length; ++i) {
+ if ((realOverlayParts & (1 << i)) != 0) {
+ overlayParts[idx] = OVERLAY_PARTS[i];
+ overlayPartsSlim[idx] = OVERLAY_PARTS_SLIM[i];
+
+ ++idx;
+ }
+ }
+ }
+
+ public void setRenderOverlayFlags(int flags) {
+ this.renderOverlayFlags = flags;
+ updateOverlayParts();
}
private void handleResize(int width, int height) {
@@ -304,9 +160,64 @@ public class GraphicsPanel extends JPanel {
updateTransform();
}
- private void updateTransform() {
- defaultTransform = Matrix4f.rotateY(yRot).times(Matrix4f.rotateX(xRot)).times(Matrix4f.scale(0.75f));
- capeTransform = new Matrix4f(defaultTransform).times(Matrix4f.scale(-1, 1, -1)).times(Matrix4f.translate(0, -8/16f, 2/16f)).times(Matrix4f.rotateX(capeRot));
+ public void updateTransform() {
+ transforms.put(BodyPart.HEAD, calculateTransform(BodyPart.HEAD, headPitch, 0));
+ transforms.put(BodyPart.TORSO, calculateTransform(BodyPart.TORSO, 0, 0));
+ transforms.put(BodyPart.LEFT_ARM, calculateTransform(BodyPart.LEFT_ARM, walkAngle, 0));
+ transforms.put(BodyPart.LEFT_ARM_SLIM, calculateTransform(BodyPart.LEFT_ARM_SLIM, walkAngle, 0));
+ transforms.put(BodyPart.RIGHT_ARM, calculateTransform(BodyPart.RIGHT_ARM, -walkAngle, 0));
+ transforms.put(BodyPart.RIGHT_ARM_SLIM, calculateTransform(BodyPart.RIGHT_ARM_SLIM, -walkAngle, 0));
+ transforms.put(BodyPart.LEFT_LEG, calculateTransform(BodyPart.LEFT_LEG, walkAngle, 0));
+ transforms.put(BodyPart.RIGHT_LEG, calculateTransform(BodyPart.RIGHT_LEG, -walkAngle, 0));
+
+ transforms.put(BodyPart.HAT, calculateTransform(BodyPart.HEAD, headPitch, 0));
+ transforms.put(BodyPart.TORSO_OVERLAY, calculateTransform(BodyPart.TORSO_OVERLAY, 0, 0));
+ transforms.put(BodyPart.LEFT_ARM_OVERLAY, calculateTransform(BodyPart.LEFT_ARM_OVERLAY, walkAngle, 0));
+ transforms.put(BodyPart.LEFT_ARM_OVERLAY_SLIM, calculateTransform(BodyPart.LEFT_ARM_OVERLAY_SLIM, walkAngle, 0));
+ transforms.put(BodyPart.RIGHT_ARM_OVERLAY, calculateTransform(BodyPart.RIGHT_ARM_OVERLAY, -walkAngle, 0));
+ transforms.put(BodyPart.RIGHT_ARM_OVERLAY_SLIM, calculateTransform(BodyPart.RIGHT_ARM_OVERLAY_SLIM, -walkAngle, 0));
+ transforms.put(BodyPart.LEFT_LEG_OVERLAY, calculateTransform(BodyPart.LEFT_LEG_OVERLAY, walkAngle, 0));
+ transforms.put(BodyPart.RIGHT_LEG_OVERLAY, calculateTransform(BodyPart.RIGHT_LEG_OVERLAY, -walkAngle, 0));
+
+ transforms.put(BodyPart.CAPE, calculateTransform(BodyPart.CAPE, capeAngle, 0));
+
+ for (BodyPart part : BodyPart.values()) {
+ transforms.put(part, Matrix4f.scale(0.75f).times(Matrix4f.rotateX(worldRotX)).times(Matrix4f.rotateY(worldRotY)).times(transforms.get(part)));
+ }
+ }
+
+ /* f1 and f2 control part-specific stuff (f1 is usually main rotation) */
+ private Matrix4f calculateTransform(BodyPart part, float f1, float f2) {
+ switch (part) {
+ case HEAD:
+ case HAT:
+ return Matrix4f.translate(0, -8/16f, 0).times(Matrix4f.rotateX(f1)).times(Matrix4f.translate(0, -4/16f, 0));
+ case TORSO:
+ case TORSO_OVERLAY:
+ return Matrix4f.translate(0, -2/16f, 0);
+ case LEFT_ARM:
+ case LEFT_ARM_OVERLAY:
+ return Matrix4f.translate(-6/16f, -6/16f, 0).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 4/16f, 0)));
+ case LEFT_ARM_SLIM:
+ case LEFT_ARM_OVERLAY_SLIM:
+ return Matrix4f.translate(-5.5f/16f, -6/16f, 0).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 4/16f, 0)));
+ case RIGHT_ARM:
+ case RIGHT_ARM_OVERLAY:
+ return Matrix4f.translate(6/16f, -6/16f, 0).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 4/16f, 0)));
+ case RIGHT_ARM_SLIM:
+ case RIGHT_ARM_OVERLAY_SLIM:
+ return Matrix4f.translate(5.5f/16f, -6/16f, 0).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 4/16f, 0)));
+ case LEFT_LEG:
+ case LEFT_LEG_OVERLAY:
+ return Matrix4f.translate(-2f/16f, 4/16f, 0).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 6/16f, 0)));
+ case RIGHT_LEG:
+ case RIGHT_LEG_OVERLAY:
+ return Matrix4f.translate(2f/16f, 4/16f, 0).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 6/16f, 0)));
+ case CAPE:
+ return Matrix4f.translate(0, -8/16f, -2/16f).times(Matrix4f.rotateX(f1).times(Matrix4f.translate(0, 8/16f, 0).times(Matrix4f.scale(-1, 1, -1))));
+ default:
+ throw new IllegalArgumentException("invalid body part");
+ }
}
@Override
@@ -314,27 +225,49 @@ public class GraphicsPanel extends JPanel {
if (framebuffer == null) handleResize(getWidth(), getHeight());
long start = System.nanoTime();
+
+ BodyPart[] main, overlay;
+ if (normalModel) {
+ main = MAIN_PARTS;
+ overlay = overlayParts;
+ } else {
+ main = MAIN_PARTS_SLIM;
+ overlay = overlayPartsSlim;
+ }
+
framebuffer.setBlendMode(BlendMode.DISABLE);
framebuffer.setCullBackFace(true);
framebuffer.clear(Framebuffer.FB_CLEAR_COLOR | Framebuffer.FB_CLEAR_DEPTH, 0xFF000000);
+
long t1 = System.nanoTime();
- framebuffer.setTransform(capeTransform);
- framebuffer.drawMesh(meshes.get(BodyPart.CAPE));
+ if ((renderOverlayFlags & OVERLAY_CAPE) != 0) {
+ framebuffer.setTransform(transforms.get(BodyPart.CAPE));
+ framebuffer.drawMesh(meshes.get(BodyPart.CAPE));
+ }
- framebuffer.setTransform(defaultTransform);
framebuffer.setDepthMode(Framebuffer.FB_DEPTH_COMMIT | Framebuffer.FB_DEPTH_USE);
- framebuffer.drawMesh(meshes.get(BodyPart.MAIN));
-
- framebuffer.setDepthMode(Framebuffer.FB_DEPTH_USE | Framebuffer.FB_DEPTH_COMMIT_TRANSPARENT);
- framebuffer.setBlendMode(BlendMode.BLEND_OVER);
- framebuffer.setCullBackFace(false);
- framebuffer.drawMesh(meshes.get(BodyPart.HAT));
- framebuffer.drawMesh(meshes.get(BodyPart.TORSO_OVERLAY));
- framebuffer.drawMesh(meshes.get(BodyPart.LEFT_ARM_OVERLAY));
- framebuffer.drawMesh(meshes.get(BodyPart.RIGHT_ARM_OVERLAY));
- framebuffer.drawMesh(meshes.get(BodyPart.LEFT_LEG_OVERLAY));
- framebuffer.drawMesh(meshes.get(BodyPart.RIGHT_LEG_OVERLAY));
+
+ for (BodyPart part : main) {
+ framebuffer.setTransform(transforms.get(part));
+ framebuffer.drawMesh(meshes.get(part));
+ }
+
+ if (translucentModel) {
+ framebuffer.setDepthMode(Framebuffer.FB_DEPTH_USE | Framebuffer.FB_DEPTH_COMMIT_TRANSPARENT);
+ framebuffer.setBlendMode(BlendMode.BLEND_OVER);
+ framebuffer.setCullBackFace(false);
+ } else {
+ framebuffer.setBlendMode(BlendMode.BINARY);
+ }
+
+ if (overlay != null) {
+ for (BodyPart part : overlay) {
+ framebuffer.setTransform(transforms.get(part));
+ framebuffer.drawMesh(meshes.get(part));
+ }
+ }
+
long t2 = System.nanoTime();
g.clearRect(0, 0, getWidth(), getHeight());
@@ -376,29 +309,145 @@ public class GraphicsPanel extends JPanel {
return String.format("avg %.02fms clr %.02fms msh", sumClr / (double)n / 1000000, sumMsh / (double)n / 1000000);
}
- public void setYRot(float rad) {
- this.yRot = rad;
- updateTransform();
- }
+ @RequiredArgsConstructor
+ public enum BodyPart {
+ HEAD(8/16f, 8/16f, 8/16f, 8/64f, 56/64f),
+ TORSO(8/16f, 12/16f, 4/16f, 20/64f, 44/64f),
+ LEFT_ARM(4/16f, 12/16f, 4/16f, 44/64f, 44/64f),
+ RIGHT_ARM(4/16f, 12/16f, 4/16f, 36/64f, 12/64f),
+ LEFT_ARM_SLIM(3/16f, 12/16f, 4/16f, 44/64f, 44/64f),
+ RIGHT_ARM_SLIM(3/16f, 12/16f, 4/16f, 36/64f, 12/64f),
+ LEFT_LEG(4/16f, 12/16f, 4/16f, 4/64f, 44/64f),
+ RIGHT_LEG(4/16f, 12/16f, 4/16f, 20/64f, 12/64f),
+ HAT(8/16f, 8/16f, 8/16f, 40/64f, 56/64f, 0),
+ TORSO_OVERLAY(8/16f, 12/16f, 4/16f, 20/64f, 28/64f, 2),
+ LEFT_ARM_OVERLAY(4/16f, 12/16f, 4/16f, 52/64f, 12/64f, 0),
+ LEFT_ARM_OVERLAY_SLIM(3/16f, 12/16f, 4/16f, 52/64f, 12/64f, 0),
+ RIGHT_ARM_OVERLAY(4/16f, 12/16f, 4/16f, 44/64f, 28/64f, 0),
+ RIGHT_ARM_OVERLAY_SLIM(3/16f, 12/16f, 4/16f, 44/64f, 28/64f, 0),
+ LEFT_LEG_OVERLAY(4/16f, 12/16f, 4/16f, 4/64f, 28/64f, 0),
+ RIGHT_LEG_OVERLAY(4/16f, 12/16f, 4/16f, 4/64f, 12/64f, 1),
+ CAPE(8/16f, 16/16f, 1/16f, 1/64f, 31/32f, 10/64f, 16/32f, 1/64f, true);
+
+ private final float xMin, yMin, zMin;
+ private final float xMax, yMax, zMax;
+ private final float texBaseX, texBaseY;
+ private final float texSpanX, texSpanY, texSpanZ;
+
+ private static final float OVERLAY_OFFSET = 2f/64;
+ private static final float CAPE_OFFSET = -0.001f;
+
+ // for overlay parts
+ BodyPart(float spanX, float spanY, float spanZ, float texBaseX, float texBaseY, int planeFightOffset) {
+ this(spanX + (planeFightOffset * 0.001f) + OVERLAY_OFFSET,
+ spanY + (planeFightOffset * 0.001f) + OVERLAY_OFFSET,
+ spanZ + (planeFightOffset * 0.001f) + OVERLAY_OFFSET,
+ texBaseX, texBaseY, spanX / 4, spanY / 4, spanZ / 4);
+ }
- public void setXRot(float rad) {
- this.xRot = rad;
- updateTransform();
- }
+ // for main parts
+ BodyPart(float spanX, float spanY, float spanZ, float texBaseX, float texBaseY) {
+ this(spanX, spanY, spanZ, texBaseX, texBaseY, spanX / 4, spanY / 4, spanZ / 4);
+ }
- public void setCapeRot(float rad) {
- this.capeRot = rad;
- updateTransform();
- }
+ @SuppressWarnings("unused")
+ BodyPart(float spanX, float spanY, float spanZ, float texBaseX, float texBaseY, float texSpanX, float texSpanY, float texSpanZ, boolean cape) {
+ this(spanX + CAPE_OFFSET, spanY + CAPE_OFFSET, spanZ + CAPE_OFFSET, texBaseX, texBaseY, texSpanX, texSpanY, texSpanZ);
+ }
- public enum BodyPart {
- MAIN,
- HAT,
- TORSO_OVERLAY,
- LEFT_ARM_OVERLAY,
- RIGHT_ARM_OVERLAY,
- LEFT_LEG_OVERLAY,
- RIGHT_LEG_OVERLAY,
- CAPE
+ // for cape (it does not follow the convention of 1px per 1/32 logical units)
+ BodyPart(float spanX, float spanY, float spanZ, float texBaseX, float texBaseY, float texSpanX, float texSpanY, float texSpanZ) {
+ this(-spanX / 2, -spanY / 2, -spanZ / 2, spanX / 2, spanY / 2, spanZ / 2, texBaseX, texBaseY, texSpanX, texSpanY, texSpanZ);
+ }
+
+ private static void addCuboid(Mesh.Builder mb, float x1, float y1, float z1, float x2, float y2, float z2, float tx, float ty, float tspanX, float tspanY, float tspanZ, float aspect, int ibase) {
+ mb.vertex(new Vector4f(x1, y1, z2), /* front */
+ new Vector4f(x1, y2, z2),
+ new Vector4f(x2, y2, z2),
+ new Vector4f(x2, y1, z2),
+
+ new Vector4f(x2, y1, z2), /* +X side */
+ new Vector4f(x2, y2, z2),
+ new Vector4f(x2, y2, z1),
+ new Vector4f(x2, y1, z1),
+
+ new Vector4f(x2, y1, z1), /* back */
+ new Vector4f(x2, y2, z1),
+ new Vector4f(x1, y2, z1),
+ new Vector4f(x1, y1, z1),
+
+ new Vector4f(x1, y1, z1), /* -X side */
+ new Vector4f(x1, y2, z1),
+ new Vector4f(x1, y2, z2),
+ new Vector4f(x1, y1, z2),
+
+ new Vector4f(x1, y1, z1), /* top */
+ new Vector4f(x1, y1, z2),
+ new Vector4f(x2, y1, z2),
+ new Vector4f(x2, y1, z1),
+
+ new Vector4f(x1, y2, z1), /* bottom */
+ new Vector4f(x1, y2, z2),
+ new Vector4f(x2, y2, z2),
+ new Vector4f(x2, y2, z1))
+ .normals(new Vector4f(0, 0, 1, 0),
+ new Vector4f(0, 0, 1, 0),
+
+ new Vector4f(1, 0, 0, 0),
+ new Vector4f(1, 0, 0, 0),
+
+ new Vector4f(0, 0, -1, 0),
+ new Vector4f(0, 0, -1, 0),
+
+ new Vector4f(-1, 0, 0, 0),
+ new Vector4f(-1, 0, 0, 0),
+
+ new Vector4f(0, -1, 0, 0),
+ new Vector4f(0, -1, 0, 0),
+
+ new Vector4f(0, 1, 0, 0),
+ new Vector4f(0, 1, 0, 0))
+ .texCoords(new Vector2f(tx, ty),
+ new Vector2f(tx, ty - tspanY),
+ new Vector2f(tx + tspanX, ty - tspanY),
+ new Vector2f(tx + tspanX, ty),
+
+ new Vector2f(tx + tspanX, ty),
+ new Vector2f(tx + tspanX, ty - tspanY),
+ new Vector2f(tx + tspanX + tspanZ, ty - tspanY),
+ new Vector2f(tx + tspanX + tspanZ, ty),
+
+ new Vector2f(tx + tspanX + tspanZ, ty),
+ new Vector2f(tx + tspanX + tspanZ, ty - tspanY),
+ new Vector2f(tx + 2 * tspanX + tspanZ, ty - tspanY),
+ new Vector2f(tx + 2 * tspanX + tspanZ, ty),
+
+ new Vector2f(tx - tspanZ, ty),
+ new Vector2f(tx - tspanZ, ty - tspanY),
+ new Vector2f(tx, ty - tspanY),
+ new Vector2f(tx, ty),
+
+ new Vector2f(tx, ty + tspanZ),
+ new Vector2f(tx, ty),
+ new Vector2f(tx + tspanX, ty),
+ new Vector2f(tx + tspanX, ty + (tspanZ / aspect)),
+
+ new Vector2f(tx + tspanX, ty + (tspanZ / aspect)),
+ new Vector2f(tx + tspanX, ty),
+ new Vector2f(tx + 2 * tspanX, ty),
+ new Vector2f(tx + 2 * tspanX, ty + tspanZ))
+ .indices(ibase, ibase + 1, ibase + 2, ibase, ibase + 2, ibase + 3,
+ ibase + 4, ibase + 5, ibase + 6, ibase + 4, ibase + 6, ibase + 7,
+ ibase + 8, ibase + 9, ibase + 10, ibase + 8, ibase + 10, ibase + 11,
+ ibase + 12, ibase + 13, ibase + 14, ibase + 12, ibase + 14, ibase + 15,
+ ibase + 16, ibase + 17, ibase + 18, ibase + 16, ibase + 18, ibase + 19,
+ ibase + 20, ibase + 21, ibase + 22, ibase + 20, ibase + 22, ibase + 23);
+ }
+
+ public Mesh.Builder toBuilder(Texture tex, int base) {
+ Mesh.Builder builder = new Mesh.Builder().texture(tex);
+ addCuboid(builder, xMin, yMin, zMin, xMax, yMax, zMax, texBaseX, texBaseY, texSpanX, texSpanY, texSpanZ, tex.calcAspect(), base);
+ return builder;
+ }
}
}
diff --git a/src/main/java/dev/figboot/cuberender/test/TestWindow.java b/src/main/java/dev/figboot/cuberender/test/TestWindow.java
index 770648f..f7f94bd 100644
--- a/src/main/java/dev/figboot/cuberender/test/TestWindow.java
+++ b/src/main/java/dev/figboot/cuberender/test/TestWindow.java
@@ -1,7 +1,6 @@
package dev.figboot.cuberender.test;
import javax.swing.*;
-import java.awt.*;
public class TestWindow extends JFrame {
public TestWindow() {
@@ -10,52 +9,15 @@ public class TestWindow extends JFrame {
setSize(300, 300);
setLocationRelativeTo(null);
- JPanel panel = new JPanel();
- JSlider sliderY = new JSlider();
- JSlider sliderX = new JSlider();
- JSlider sliderCape = new JSlider();
GraphicsPanel gp = new GraphicsPanel();
- sliderY.setMinimum(-180);
- sliderY.setMaximum(180);
+ setContentPane(gp);
- sliderX.setMinimum(-180);
- sliderX.setMaximum(180);
- sliderX.setOrientation(JSlider.VERTICAL);
-
- sliderCape.setMinimum(-180);
- sliderCape.setMaximum(180);
- sliderCape.setOrientation(JSlider.VERTICAL);
-
- sliderX.setValue(0);
- sliderY.setValue(0);
- sliderCape.setValue(0);
-
- panel.setLayout(new BorderLayout());
- panel.add(gp, BorderLayout.CENTER);
- panel.add(sliderY, BorderLayout.SOUTH);
- panel.add(sliderX, BorderLayout.EAST);
- panel.add(sliderCape, BorderLayout.WEST);
-
- setContentPane(panel);
-
- sliderY.addChangeListener(e -> {
- gp.setYRot((float)Math.toRadians(sliderY.getValue()));
- gp.repaint();
- });
-
- sliderX.addChangeListener(e -> {
- gp.setXRot((float)Math.toRadians(sliderX.getValue()));
- gp.repaint();
- });
-
- sliderCape.addChangeListener(e -> {
- gp.setCapeRot((float)Math.toRadians(sliderCape.getValue()));
- gp.repaint();
- });
+ TestWindowControl control = new TestWindowControl(gp);
+ control.setVisible(true);
}
public static void main(String[] args) {
- new TestWindow().setVisible(true);
+ SwingUtilities.invokeLater(() -> new TestWindow().setVisible(true));
}
}
diff --git a/src/main/java/dev/figboot/cuberender/test/TestWindowControl.java b/src/main/java/dev/figboot/cuberender/test/TestWindowControl.java
new file mode 100644
index 0000000..22218b5
--- /dev/null
+++ b/src/main/java/dev/figboot/cuberender/test/TestWindowControl.java
@@ -0,0 +1,204 @@
+package dev.figboot.cuberender.test;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import javax.swing.*;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ChangeEvent;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.util.EnumMap;
+import java.util.Map;
+
+public class TestWindowControl extends JFrame {
+ private final GraphicsPanel graphicsPanel;
+
+ private final JRadioButton radOverlayOpaque, radOverlayTranslucent;
+ private final JRadioButton radModelNormal, radModelSlim;
+
+ private final ButtonGroup bgRenderOverlay;
+ private final ButtonGroup bgModelType;
+
+ private final JSlider sldWalkAngle;
+ private final JSlider sldCapeAngle;
+ private final JSlider sldYRot;
+ private final JSlider sldXRot;
+ private final JSlider sldHeadPitch;
+
+ private final EnumMap<OverlayPart, JCheckBox> cbxOverlayParts = new EnumMap<>(OverlayPart.class);
+
+ TestWindowControl(GraphicsPanel gpanel) {
+ this.graphicsPanel = gpanel;
+
+ setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ setTitle("Control");
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new GridBagLayout());
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.insets = new Insets(5, 5, 5, 5);
+
+ setContentPane(panel);
+
+ JPanel overlayRenderPanel = new JPanel();
+ overlayRenderPanel.setLayout(new BoxLayout(overlayRenderPanel, BoxLayout.Y_AXIS));
+
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1.0;
+ c.weighty = 1.0;
+ panel.add(overlayRenderPanel, c);
+
+ bgRenderOverlay = new ButtonGroup();
+ radOverlayOpaque = new JRadioButton("Opaque overlay");
+ radOverlayTranslucent = new JRadioButton("Translucent overlay");
+ radOverlayTranslucent.setSelected(true);
+
+ radOverlayOpaque.addActionListener(this::handleAction);
+ radOverlayTranslucent.addActionListener(this::handleAction);
+
+ overlayRenderPanel.setBorder(new LineBorder(Color.BLACK, 1));
+ bgRenderOverlay.add(radOverlayOpaque);
+ bgRenderOverlay.add(radOverlayTranslucent);
+
+ overlayRenderPanel.add(radOverlayOpaque);
+ overlayRenderPanel.add(radOverlayTranslucent);
+
+ JPanel modelPanel = new JPanel();
+ modelPanel.setLayout(new BoxLayout(modelPanel, BoxLayout.Y_AXIS));
+ modelPanel.setBorder(new LineBorder(Color.BLACK, 1));
+ panel.add(modelPanel, c);
+
+ bgModelType = new ButtonGroup();
+ radModelNormal = new JRadioButton("Normal");
+ radModelSlim = new JRadioButton("Slim");
+ radModelNormal.setSelected(true);
+
+ radModelNormal.addActionListener(this::handleAction);
+ radModelSlim.addActionListener(this::handleAction);
+
+ modelPanel.add(radModelNormal);
+ modelPanel.add(radModelSlim);
+
+ bgModelType.add(radModelNormal);
+ bgModelType.add(radModelSlim);
+
+ JPanel limbControlPanel = new JPanel();
+ limbControlPanel.setLayout(new BoxLayout(limbControlPanel, BoxLayout.Y_AXIS));
+ limbControlPanel.setBorder(new LineBorder(Color.BLACK, 1));
+
+ c.gridx = 0;
+ c.gridy = 1;
+
+ panel.add(limbControlPanel, c);
+
+ sldWalkAngle = new JSlider(-180, 180, 0);
+ sldCapeAngle = new JSlider(-180, 180, 0);
+ sldYRot = new JSlider(-180, 180, 0);
+ sldXRot = new JSlider(-180, 180, 0);
+ sldHeadPitch = new JSlider(-180, 180, 0);
+
+ limbControlPanel.add(new JLabel("Walk animation"));
+ limbControlPanel.add(sldWalkAngle);
+
+ limbControlPanel.add(new JLabel("Cape angle"));
+ limbControlPanel.add(sldCapeAngle);
+
+ limbControlPanel.add(new JLabel("Y rotation"));
+ limbControlPanel.add(sldYRot);
+
+ limbControlPanel.add(new JLabel("X rotation"));
+ limbControlPanel.add(sldXRot);
+
+ limbControlPanel.add(new JLabel("Head pitch"));
+ limbControlPanel.add(sldHeadPitch);
+
+ sldWalkAngle.addChangeListener(this::handleSlider);
+ sldCapeAngle.addChangeListener(this::handleSlider);
+ sldYRot.addChangeListener(this::handleSlider);
+ sldXRot.addChangeListener(this::handleSlider);
+ sldHeadPitch.addChangeListener(this::handleSlider);
+
+ JPanel skinPartsPanel = new JPanel();
+ skinPartsPanel.setLayout(new BoxLayout(skinPartsPanel, BoxLayout.Y_AXIS));
+ skinPartsPanel.setBorder(new LineBorder(Color.BLACK, 1));
+
+ c.gridx = GridBagConstraints.RELATIVE;
+ panel.add(skinPartsPanel, c);
+
+ for (OverlayPart part : OverlayPart.values()) {
+ JCheckBox cbxPart = new JCheckBox(part.name());
+ cbxPart.setSelected(true);
+ cbxPart.addActionListener(this::handleOverlayPartsAction);
+
+ skinPartsPanel.add(cbxPart);
+
+ cbxOverlayParts.put(part, cbxPart);
+ }
+
+ pack();
+ setResizable(false);
+ setLocationRelativeTo(null);
+
+ updateGraphics(true, true);
+ }
+
+ private void updateGraphics(boolean slider, boolean overlay) {
+ graphicsPanel.setTranslucentModel(radOverlayTranslucent.isSelected());
+ graphicsPanel.setNormalModel(radModelNormal.isSelected());
+
+ if (slider) {
+ graphicsPanel.setWalkAngle((float)Math.toRadians(sldWalkAngle.getValue()));
+ graphicsPanel.setCapeAngle((float)Math.toRadians(sldCapeAngle.getValue()));
+ graphicsPanel.setWorldRotY((float)Math.toRadians(sldYRot.getValue()));
+ graphicsPanel.setWorldRotX((float)Math.toRadians(sldXRot.getValue()));
+ graphicsPanel.setHeadPitch((float)Math.toRadians(sldHeadPitch.getValue()));
+ graphicsPanel.updateTransform();
+ }
+
+ if (overlay) {
+ int flags = 0;
+ for (Map.Entry<OverlayPart, JCheckBox> entry : cbxOverlayParts.entrySet()) {
+ if (entry.getValue().isSelected()) {
+ flags |= entry.getKey().getFlag();
+ }
+ }
+
+ graphicsPanel.setRenderOverlayFlags(flags);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private void handleAction(ActionEvent e) {
+ updateGraphics(false, false);
+ graphicsPanel.repaint();
+ }
+
+ @SuppressWarnings("unused")
+ private void handleOverlayPartsAction(ActionEvent e) {
+ updateGraphics(false, true);
+ graphicsPanel.repaint();
+ }
+
+ @SuppressWarnings("unused")
+ private void handleSlider(ChangeEvent e) {
+ updateGraphics(true, false);
+ graphicsPanel.repaint();
+ }
+
+ @RequiredArgsConstructor
+ @Getter
+ private enum OverlayPart {
+ HAT("Hat", GraphicsPanel.OVERLAY_HAT),
+ TORSO("Torso overlay", GraphicsPanel.OVERLAY_TORSO),
+ LEFT_ARM("Left arm overlay", GraphicsPanel.OVERLAY_LEFT_ARM),
+ RIGHT_ARM("Right arm overlay", GraphicsPanel.OVERLAY_RIGHT_ARM),
+ LEFT_LEG("Left leg overlay", GraphicsPanel.OVERLAY_LEFT_LEG),
+ RIGHT_LEG("Right leg overlay", GraphicsPanel.OVERLAY_RIGHT_LEG),
+ CAPE("Cape", GraphicsPanel.OVERLAY_CAPE);
+
+ private final String name;
+ private final int flag;
+ }
+}