From 1b2cac71cd69e48ae9cddf8907fc48224ae9df6b Mon Sep 17 00:00:00 2001 From: Maik Marschner Date: Sat, 6 Apr 2024 01:53:11 +0200 Subject: [PATCH 1/3] Add a box model builder and re-implement the piglin head model with it. --- .../se/llbit/chunky/entity/SkullEntity.java | 294 ++++-------------- .../chunky/model/builder/BoxModelBuilder.java | 238 ++++++++++++++ .../builder}/UVMapHelper.java | 17 +- 3 files changed, 312 insertions(+), 237 deletions(-) create mode 100644 chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java rename chunky/src/java/se/llbit/chunky/{entity => model/builder}/UVMapHelper.java (81%) diff --git a/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java b/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java index 32eebab625..b3885b4f0f 100644 --- a/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java +++ b/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java @@ -17,7 +17,8 @@ */ package se.llbit.chunky.entity; -import se.llbit.chunky.model.Model; +import se.llbit.chunky.model.builder.BoxModelBuilder; +import se.llbit.chunky.model.builder.UVMapHelper; import se.llbit.chunky.resources.EntityTexture; import se.llbit.chunky.resources.Texture; import se.llbit.chunky.world.Material; @@ -252,238 +253,65 @@ public enum Kind { //#endregion //#region Piglin head - private static final UVMapHelper piglinHeadCube1 = new UVMapHelper(10, 8, 8, 0, 0).flipX(); - private static final UVMapHelper piglinHeadCube2 = new UVMapHelper(4, 1, 4, 31, 1); - private static final UVMapHelper piglinHeadCube3 = new UVMapHelper(1, 1, 2, 2, 0); - private static final UVMapHelper piglinHeadCube4 = new UVMapHelper(1, 1, 2, 2, 4); - private static final UVMapHelper piglinHeadEarLeft = new UVMapHelper(1, 4, 5, 39, 6).flipY(); - private static final UVMapHelper piglinHeadEarRight = new UVMapHelper(1, 4, 5, 51, 6).flipY(); - private static final Quad[] piglinHead = Model.join( - new Quad[]{ - new Quad( - new Vector3(-5 / 16.0, 8 / 16.0, 4 / 16.0), - new Vector3(5 / 16.0, 8 / 16.0, 4 / 16.0), - new Vector3(-5 / 16.0, 8 / 16.0, -4 / 16.0), - piglinHeadCube1.top().toVectorForQuad() - ), - new Quad( - new Vector3(-5 / 16.0, 0 / 16.0, -4 / 16.0), - new Vector3(5 / 16.0, 0 / 16.0, -4 / 16.0), - new Vector3(-5 / 16.0, 0 / 16.0, 4 / 16.0), - piglinHeadCube1.bottom().flipY().toVectorForQuad() - ), - new Quad( - new Vector3(-5 / 16.0, 8 / 16.0, 4 / 16.0), - new Vector3(-5 / 16.0, 8 / 16.0, -4 / 16.0), - new Vector3(-5 / 16.0, 0 / 16.0, 4 / 16.0), - piglinHeadCube1.left().toVectorForQuad() - ), - new Quad( - new Vector3(5 / 16.0, 8 / 16.0, -4 / 16.0), - new Vector3(5 / 16.0, 8 / 16.0, 4 / 16.0), - new Vector3(5 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube1.right().toVectorForQuad() - ), - new Quad( - new Vector3(-5 / 16.0, 8 / 16.0, -4 / 16.0), - new Vector3(5 / 16.0, 8 / 16.0, -4 / 16.0), - new Vector3(-5 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube1.front().toVectorForQuad() - ), - new Quad( - new Vector3(5 / 16.0, 8 / 16.0, 4 / 16.0), - new Vector3(-5 / 16.0, 8 / 16.0, 4 / 16.0), - new Vector3(5 / 16.0, 0 / 16.0, 4 / 16.0), - piglinHeadCube1.back().toVectorForQuad() - ) - }, - new Quad[]{ - new Quad( - new Vector3(-2 / 16.0, 4 / 16.0, -4 / 16.0), - new Vector3(2 / 16.0, 4 / 16.0, -4 / 16.0), - new Vector3(-2 / 16.0, 4 / 16.0, -5 / 16.0), - piglinHeadCube2.top().toVectorForQuad() - ), - new Quad( - new Vector3(-2 / 16.0, 0 / 16.0, -5 / 16.0), - new Vector3(2 / 16.0, 0 / 16.0, -5 / 16.0), - new Vector3(-2 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube2.bottom().toVectorForQuad() - ), - new Quad( - new Vector3(-2 / 16.0, 4 / 16.0, -4 / 16.0), - new Vector3(-2 / 16.0, 4 / 16.0, -5 / 16.0), - new Vector3(-2 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube2.left().toVectorForQuad() - ), - new Quad( - new Vector3(2 / 16.0, 4 / 16.0, -5 / 16.0), - new Vector3(2 / 16.0, 4 / 16.0, -4 / 16.0), - new Vector3(2 / 16.0, 0 / 16.0, -5 / 16.0), - piglinHeadCube2.right().toVectorForQuad() - ), - new Quad( - new Vector3(-2 / 16.0, 4 / 16.0, -5 / 16.0), - new Vector3(2 / 16.0, 4 / 16.0, -5 / 16.0), - new Vector3(-2 / 16.0, 0 / 16.0, -5 / 16.0), - piglinHeadCube2.front().toVectorForQuad() - ) - }, - new Quad[]{ - new Quad( - new Vector3(2 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(3 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(2 / 16.0, 2 / 16.0, -5 / 16.0), - piglinHeadCube3.top().toVectorForQuad() - ), - new Quad( - new Vector3(2 / 16.0, 0 / 16.0, -5 / 16.0), - new Vector3(3 / 16.0, 0 / 16.0, -5 / 16.0), - new Vector3(2 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube3.bottom().toVectorForQuad() - ), - new Quad( - new Vector3(2 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(2 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(2 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube3.left().toVectorForQuad() - ), - new Quad( - new Vector3(3 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(3 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(3 / 16.0, 0 / 16.0, -5 / 16.0), - piglinHeadCube3.right().toVectorForQuad() - ), - new Quad( - new Vector3(2 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(3 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(2 / 16.0, 0 / 16.0, -5 / 16.0), - piglinHeadCube3.front().toVectorForQuad() - ) - }, - new Quad[]{ - new Quad( - new Vector3(-3 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(-2 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(-3 / 16.0, 2 / 16.0, -5 / 16.0), - piglinHeadCube4.top().toVectorForQuad() - ), - new Quad( - new Vector3(-3 / 16.0, 0 / 16.0, -5 / 16.0), - new Vector3(-2 / 16.0, 0 / 16.0, -5 / 16.0), - new Vector3(-3 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube4.bottom().toVectorForQuad() - ), - new Quad( - new Vector3(-3 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(-3 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(-3 / 16.0, 0 / 16.0, -4 / 16.0), - piglinHeadCube4.left().toVectorForQuad() - ), - new Quad( - new Vector3(-2 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(-2 / 16.0, 2 / 16.0, -4 / 16.0), - new Vector3(-2 / 16.0, 0 / 16.0, -5 / 16.0), - piglinHeadCube4.right().toVectorForQuad() - ), - new Quad( - new Vector3(-3 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(-2 / 16.0, 2 / 16.0, -5 / 16.0), - new Vector3(-3 / 16.0, 0 / 16.0, -5 / 16.0), - piglinHeadCube4.front().toVectorForQuad() - ) - }, - Model.transform( - new Quad[]{ - new Quad( - new Vector3(0 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(1 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(0 / 16.0, 5 / 16.0, -2 / 16.0), - piglinHeadEarLeft.bottom().toVectorForQuad() - ), - new Quad( - new Vector3(0 / 16.0, 0 / 16.0, -2 / 16.0), - new Vector3(1 / 16.0, 0 / 16.0, -2 / 16.0), - new Vector3(0 / 16.0, 0 / 16.0, 2 / 16.0), - piglinHeadEarLeft.top().toVectorForQuad() - ), - new Quad( - new Vector3(0 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(0 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(0 / 16.0, 0 / 16.0, 2 / 16.0), - piglinHeadEarLeft.left().toVectorForQuad() - ), - new Quad( - new Vector3(1 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(1 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(1 / 16.0, 0 / 16.0, -2 / 16.0), - piglinHeadEarLeft.right().toVectorForQuad() - ), - new Quad( - new Vector3(0 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(1 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(0 / 16.0, 0 / 16.0, -2 / 16.0), - piglinHeadEarLeft.front().toVectorForQuad() - ), - new Quad( - new Vector3(1 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(0 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(1 / 16.0, 0 / 16.0, 2 / 16.0), - piglinHeadEarLeft.back().toVectorForQuad() - ) - }, - Transform.NONE - .translate(0.5 - 1 / 16., 0.5, 0.5) - .rotateZ(Math.toRadians(220)) - .translate(-0.5, -0.5, -0.5) - .translate(4.5 / 16.0, 6 / 16.0, 0 / 16.0) - ), - Model.transform( - new Quad[]{ - new Quad( - new Vector3(-1 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(0 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(-1 / 16.0, 5 / 16.0, -2 / 16.0), - piglinHeadEarRight.bottom().toVectorForQuad() - ), - new Quad( - new Vector3(-1 / 16.0, 0 / 16.0, -2 / 16.0), - new Vector3(0 / 16.0, 0 / 16.0, -2 / 16.0), - new Vector3(-1 / 16.0, 0 / 16.0, 2 / 16.0), - piglinHeadEarRight.top().toVectorForQuad() - ), - new Quad( - new Vector3(-1 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(-1 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(-1 / 16.0, 0 / 16.0, 2 / 16.0), - piglinHeadEarRight.left().toVectorForQuad() - ), - new Quad( - new Vector3(0 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(0 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(0 / 16.0, 0 / 16.0, -2 / 16.0), - piglinHeadEarRight.right().toVectorForQuad() - ), - new Quad( - new Vector3(-1 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(0 / 16.0, 5 / 16.0, -2 / 16.0), - new Vector3(-1 / 16.0, 0 / 16.0, -2 / 16.0), - piglinHeadEarRight.front().toVectorForQuad() - ), - new Quad( - new Vector3(0 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(-1 / 16.0, 5 / 16.0, 2 / 16.0), - new Vector3(0 / 16.0, 0 / 16.0, 2 / 16.0), - piglinHeadEarRight.back().toVectorForQuad() - ) - }, - Transform.NONE - .translate(0.5 + 1 / 16., 0.5, 0.5) - .rotateZ(Math.toRadians(140)) - .translate(-0.5, -0.5, -0.5) - .translate(-4.5 / 16.0, 6 / 16.0, 0 / 16.0) - ) - ); + private static final Quad[] piglinHead = new BoxModelBuilder() + .addBox(new Vector3(-5 / 16., 0, -4 / 16.), new Vector3(5 / 16., 8 / 16., 4 / 16.), box -> + box.useEntityTexture(Texture.piglin).atUVCoordinates(0, 0).flipX() + .addTopFace(UVMapHelper.Side::flipX) + .addBottomFace(UVMapHelper.Side::flipY) + .addLeftFace() + .addRightFace() + .addFrontFace() + .addBackFace()) + .addBox(new Vector3(-2 / 16., 0, -5 / 16.), new Vector3(2 / 16., 4 / 16., -4 / 16.), box -> + box.useEntityTexture(Texture.piglin).atUVCoordinates(31, 1) + .addTopFace() + .addBottomFace() + .addLeftFace() + .addRightFace() + .addFrontFace()) + .addBox(new Vector3(2 / 16., 0, -5 / 16.), new Vector3(3 / 16., 2 / 16., -4 / 16.), box -> + box.useEntityTexture(Texture.piglin).atUVCoordinates(2, 0) + .addTopFace() + .addBottomFace() + .addLeftFace() + .addRightFace() + .addFrontFace()) + .addBox(new Vector3(-3 / 16., 0, -5 / 16.), new Vector3(-2 / 16., 2 / 16., -4 / 16.), box -> + box.useEntityTexture(Texture.piglin).atUVCoordinates(2, 4) + .addTopFace() + .addBottomFace() + .addLeftFace() + .addRightFace() + .addFrontFace()) + .addBox(new Vector3(0, 0, -2 / 16.), new Vector3(1 / 16., 5 / 16., 2 / 16.), box -> + box.useEntityTexture(Texture.piglin).atUVCoordinates(39, 6).flipY() + .addTopFace() + .addBottomFace() + .addLeftFace() + .addRightFace() + .addFrontFace() + .addBackFace() + .transform(Transform.NONE + .translate(0.5 - 1 / 16., 0.5, 0.5) + .rotateZ(Math.toRadians(220)) + .translate(-0.5, -0.5, -0.5) + .translate(4.5 / 16.0, 6 / 16.0, 0 / 16.0) + )) + .addBox(new Vector3(-1 / 16., 0, -2 / 16.), new Vector3(0, 5 / 16., 2 / 16.), box -> + box.useEntityTexture(Texture.piglin).atUVCoordinates(51, 6).flipY() + .addTopFace() + .addBottomFace() + .addLeftFace() + .addRightFace() + .addFrontFace() + .addBackFace() + .transform(Transform.NONE + .translate(0.5 + 1 / 16., 0.5, 0.5) + .rotateZ(Math.toRadians(140)) + .translate(-0.5, -0.5, -0.5) + .translate(-4.5 / 16.0, 6 / 16.0, 0 / 16.0) + )) + .toQuads(); //#endregion /** diff --git a/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java b/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java new file mode 100644 index 0000000000..92483479fe --- /dev/null +++ b/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java @@ -0,0 +1,238 @@ +package se.llbit.chunky.model.builder; + +import se.llbit.chunky.model.Model; +import se.llbit.chunky.resources.Texture; +import se.llbit.math.Quad; +import se.llbit.math.Transform; +import se.llbit.math.Vector3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +/** + * A helper for building models that are made out of boxes. + */ +public class BoxModelBuilder { + private final List boxes = new ArrayList<>(); + + public BoxModelBuilder addBox(Vector3 from, Vector3 to, Consumer boxConsumer) { + BoxBuilder box = new BoxBuilder(from, to); + boxConsumer.accept(box); + boxes.add(box); + return this; + } + + public Quad[] toQuads() { + List quads = new ArrayList<>(); + for (BoxBuilder box : boxes) { + List boxQuads = new ArrayList<>(); + UVMapHelper.Side top = box.sides[0]; + if (top != null) { + boxQuads.add( + new Quad( + new Vector3(box.from.x, box.to.y, box.to.z), + new Vector3(box.to.x, box.to.y, box.to.z), + new Vector3(box.from.x, box.to.y, box.from.z), + top.toVectorForQuad() + ) + ); + } + UVMapHelper.Side bottom = box.sides[1]; + if (bottom != null) { + boxQuads.add( + new Quad( + new Vector3(box.from.x, box.from.y, box.from.z), + new Vector3(box.to.x, box.from.y, box.from.z), + new Vector3(box.from.x, box.from.y, box.to.z), + bottom.toVectorForQuad() + ) + ); + } + UVMapHelper.Side left = box.sides[2]; + if (left != null) { + boxQuads.add( + new Quad( + new Vector3(box.from.x, box.to.y, box.to.z), + new Vector3(box.from.x, box.to.y, box.from.z), + new Vector3(box.from.x, box.from.y, box.to.z), + left.toVectorForQuad() + ) + ); + } + UVMapHelper.Side right = box.sides[3]; + if (right != null) { + boxQuads.add( + new Quad( + new Vector3(box.to.x, box.to.y, box.from.z), + new Vector3(box.to.x, box.to.y, box.to.z), + new Vector3(box.to.x, box.from.y, box.from.z), + right.toVectorForQuad() + ) + ); + } + UVMapHelper.Side front = box.sides[4]; + if (front != null) { + boxQuads.add( + new Quad( + new Vector3(box.from.x, box.to.y, box.from.z), + new Vector3(box.to.x, box.to.y, box.from.z), + new Vector3(box.from.x, box.from.y, box.from.z), + front.toVectorForQuad() + ) + ); + } + UVMapHelper.Side back = box.sides[5]; + if (back != null) { + boxQuads.add( + new Quad( + new Vector3(box.to.x, box.to.y, box.to.z), + new Vector3(box.from.x, box.to.y, box.to.z), + new Vector3(box.to.x, box.from.y, box.to.z), + back.toVectorForQuad() + ) + ); + } + + Quad[] boxQuadsArray = boxQuads.toArray(new Quad[0]); + boxQuadsArray = Model.transform(boxQuadsArray, box.transform); + quads.addAll(Arrays.asList(boxQuadsArray)); + } + return quads.toArray(new Quad[0]); + } + + public static class BoxBuilder { + private final Vector3 from; + private final Vector3 to; + private final int widthOnTexture; + private final int lengthOnTexture; + private final int heightOnTexture; + private final UVMapHelper.Side[] sides = new UVMapHelper.Side[6]; + private Texture texture = Texture.EMPTY_TEXTURE; + private int textureWidth = 16; + private int textureHeight = 16; + private int boxU = 0; + private int boxV = 0; + private boolean flipX = false; + private boolean flipY = false; + private Transform transform = Transform.NONE; + + private BoxBuilder(Vector3 from, Vector3 to) { + this.from = from; + this.to = to; + widthOnTexture = (int) ((to.x - from.x) * 16); + heightOnTexture = (int) ((to.y - from.y) * 16); + lengthOnTexture = (int) ((to.z - from.z) * 16); + } + + private UVMapHelper getUVMapHelper() { + UVMapHelper helper = new UVMapHelper(textureWidth, textureHeight, widthOnTexture, lengthOnTexture, heightOnTexture, boxU, boxV); + if (this.flipX) { + helper.flipX(); + } + if (this.flipY) { + helper.flipY(); + } + return helper; + } + + public BoxBuilder addTopFace() { + sides[0] = getUVMapHelper().top(); + return this; + } + + public BoxBuilder addTopFace(Consumer consumer) { + addTopFace(); + consumer.accept(sides[0]); + return this; + } + + public BoxBuilder addBottomFace() { + sides[1] = getUVMapHelper().bottom(); + return this; + } + + public BoxBuilder addBottomFace(Consumer consumer) { + addBottomFace(); + consumer.accept(sides[1]); + return this; + } + + public BoxBuilder addLeftFace() { + sides[2] = getUVMapHelper().left(); + return this; + } + + public BoxBuilder addLeftFace(Consumer consumer) { + addLeftFace(); + consumer.accept(sides[2]); + return this; + } + + public BoxBuilder addRightFace() { + sides[3] = getUVMapHelper().right(); + return this; + } + + public BoxBuilder addRightFace(Consumer consumer) { + addRightFace(); + consumer.accept(sides[3]); + return this; + } + + public BoxBuilder addFrontFace() { + sides[4] = getUVMapHelper().front(); + return this; + } + + public BoxBuilder addFrontFace(Consumer consumer) { + addFrontFace(); + consumer.accept(sides[4]); + return this; + } + + public BoxBuilder addBackFace() { + sides[5] = getUVMapHelper().back(); + return this; + } + + public BoxBuilder addBackFace(Consumer consumer) { + addBackFace(); + consumer.accept(sides[5]); + return this; + } + + public BoxBuilder useEntityTexture(Texture texture) { + return forTextureSize(texture, 64, 64); + } + + public BoxBuilder forTextureSize(Texture texture, int width, int height) { + this.texture = texture; + this.textureWidth = width; + this.textureHeight = height; + return this; + } + + public BoxBuilder atUVCoordinates(int u, int v) { + boxU = u; + boxV = v; + return this; + } + + public BoxBuilder flipX() { + flipX = !flipX; + return this; + } + + public BoxBuilder flipY() { + flipY = !flipY; + return this; + } + + public BoxBuilder transform(Transform transform) { + this.transform = this.transform.chain(transform); + return this; + } + } +} diff --git a/chunky/src/java/se/llbit/chunky/entity/UVMapHelper.java b/chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java similarity index 81% rename from chunky/src/java/se/llbit/chunky/entity/UVMapHelper.java rename to chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java index 1d2be3cf06..fdbe51704d 100644 --- a/chunky/src/java/se/llbit/chunky/entity/UVMapHelper.java +++ b/chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java @@ -1,8 +1,10 @@ -package se.llbit.chunky.entity; +package se.llbit.chunky.model.builder; import se.llbit.math.Vector4; public class UVMapHelper { + private final int textureWidth; + private final int textureHeight; private final int width; private final int length; private final int height; @@ -12,7 +14,9 @@ public class UVMapHelper { private boolean flipX = false; private boolean flipY = false; - public UVMapHelper(int width, int length, int height, int boxU, int boxV) { + public UVMapHelper(int textureWidth, int textureHeight, int width, int length, int height, int boxU, int boxV) { + this.textureWidth = textureWidth; + this.textureHeight = textureHeight; this.width = width; this.length = length; this.height = height; @@ -20,6 +24,11 @@ public UVMapHelper(int width, int length, int height, int boxU, int boxV) { this.boxV = boxV; } + @Deprecated + public UVMapHelper(int width, int length, int height, int boxU, int boxV) { + this(64, 64, width, length, height, boxU, boxV); + } + public UVMapHelper flipX() { flipX = !flipX; return this; @@ -58,7 +67,7 @@ protected Side createSide(int x0, int x1, int y0, int y1) { return new Side(flipX ? x1 : x0, flipX ? x0 : x1, flipY ? y1 : y0, flipY ? y0 : y1); } - public static class Side { + public class Side { private double x0; private double x1; private double y0; @@ -91,7 +100,7 @@ public Side flipY() { * @return UV vector t hat can be used to construct a Quad */ public Vector4 toVectorForQuad() { - return new Vector4(x0 / 64., x1 / 64., 1 - y0 / 64., 1 - y1 / 64.); + return new Vector4(x0 / textureWidth, x1 / textureWidth, 1 - y0 / textureHeight, 1 - y1 / textureHeight); } } } From 089c356c08978ddff217bcfbf4c35c8ce0b0349a Mon Sep 17 00:00:00 2001 From: Maik Marschner Date: Sat, 6 Apr 2024 18:20:22 +0200 Subject: [PATCH 2/3] Add a method to mirror the model across the x axis, remove useEntityTexture and add javadoc. --- .../se/llbit/chunky/entity/SkullEntity.java | 12 +- .../chunky/model/builder/BoxModelBuilder.java | 169 +++++++++++++++++- .../chunky/model/builder/UVMapHelper.java | 10 ++ 3 files changed, 178 insertions(+), 13 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java b/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java index b3885b4f0f..a9660df221 100644 --- a/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java +++ b/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java @@ -255,7 +255,7 @@ public enum Kind { //#region Piglin head private static final Quad[] piglinHead = new BoxModelBuilder() .addBox(new Vector3(-5 / 16., 0, -4 / 16.), new Vector3(5 / 16., 8 / 16., 4 / 16.), box -> - box.useEntityTexture(Texture.piglin).atUVCoordinates(0, 0).flipX() + box.forTextureSize(Texture.piglin, 64, 64).atUVCoordinates(0, 0).flipX() .addTopFace(UVMapHelper.Side::flipX) .addBottomFace(UVMapHelper.Side::flipY) .addLeftFace() @@ -263,28 +263,28 @@ public enum Kind { .addFrontFace() .addBackFace()) .addBox(new Vector3(-2 / 16., 0, -5 / 16.), new Vector3(2 / 16., 4 / 16., -4 / 16.), box -> - box.useEntityTexture(Texture.piglin).atUVCoordinates(31, 1) + box.forTextureSize(Texture.piglin, 64, 64).atUVCoordinates(31, 1) .addTopFace() .addBottomFace() .addLeftFace() .addRightFace() .addFrontFace()) .addBox(new Vector3(2 / 16., 0, -5 / 16.), new Vector3(3 / 16., 2 / 16., -4 / 16.), box -> - box.useEntityTexture(Texture.piglin).atUVCoordinates(2, 0) + box.forTextureSize(Texture.piglin, 64, 64).atUVCoordinates(2, 0) .addTopFace() .addBottomFace() .addLeftFace() .addRightFace() .addFrontFace()) .addBox(new Vector3(-3 / 16., 0, -5 / 16.), new Vector3(-2 / 16., 2 / 16., -4 / 16.), box -> - box.useEntityTexture(Texture.piglin).atUVCoordinates(2, 4) + box.forTextureSize(Texture.piglin, 64, 64).atUVCoordinates(2, 4) .addTopFace() .addBottomFace() .addLeftFace() .addRightFace() .addFrontFace()) .addBox(new Vector3(0, 0, -2 / 16.), new Vector3(1 / 16., 5 / 16., 2 / 16.), box -> - box.useEntityTexture(Texture.piglin).atUVCoordinates(39, 6).flipY() + box.forTextureSize(Texture.piglin, 64, 64).atUVCoordinates(39, 6).flipY() .addTopFace() .addBottomFace() .addLeftFace() @@ -298,7 +298,7 @@ public enum Kind { .translate(4.5 / 16.0, 6 / 16.0, 0 / 16.0) )) .addBox(new Vector3(-1 / 16., 0, -2 / 16.), new Vector3(0, 5 / 16., 2 / 16.), box -> - box.useEntityTexture(Texture.piglin).atUVCoordinates(51, 6).flipY() + box.forTextureSize(Texture.piglin, 64, 64).atUVCoordinates(51, 6).flipY() .addTopFace() .addBottomFace() .addLeftFace() diff --git a/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java b/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java index 92483479fe..9290e9ebad 100644 --- a/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java +++ b/chunky/src/java/se/llbit/chunky/model/builder/BoxModelBuilder.java @@ -17,6 +17,14 @@ public class BoxModelBuilder { private final List boxes = new ArrayList<>(); + /** + * Add a box to this model. A box is defined by two vectors: one for the start position (from) and one for the end position (to). + * + * @param from Start position (ie. the lower x, y, z coordinates) + * @param to End position (ie. the upper x, y, z coordinates) + * @param boxConsumer Function to customize the box + * @return This box model builder + */ public BoxModelBuilder addBox(Vector3 from, Vector3 to, Consumer boxConsumer) { BoxBuilder box = new BoxBuilder(from, to); boxConsumer.accept(box); @@ -24,6 +32,11 @@ public BoxModelBuilder addBox(Vector3 from, Vector3 to, Consumer box return this; } + /** + * Create quads from all boxes, including all previously configured faces and taking the specified UV maps into account. + * + * @return Quad array + */ public Quad[] toQuads() { List quads = new ArrayList<>(); for (BoxBuilder box : boxes) { @@ -105,9 +118,9 @@ public Quad[] toQuads() { public static class BoxBuilder { private final Vector3 from; private final Vector3 to; - private final int widthOnTexture; - private final int lengthOnTexture; - private final int heightOnTexture; + private int widthOnTexture; + private int lengthOnTexture; + private int heightOnTexture; private final UVMapHelper.Side[] sides = new UVMapHelper.Side[6]; private Texture texture = Texture.EMPTY_TEXTURE; private int textureWidth = 16; @@ -116,6 +129,7 @@ public static class BoxBuilder { private int boxV = 0; private boolean flipX = false; private boolean flipY = false; + private boolean mirrorX = false; private Transform transform = Transform.NONE; private BoxBuilder(Vector3 from, Vector3 to) { @@ -126,6 +140,22 @@ private BoxBuilder(Vector3 from, Vector3 to) { lengthOnTexture = (int) ((to.z - from.z) * 16); } + /** + * Set the texture dimension of this box, ie. the width (x axis), height (y axis) and length (z axis) this box has + * on the texture, in pixels. + * + * @param width Width (x axis) of the box in the texture, ie. the width of the front part of the box texture, in pixels + * @param height Height (y axis) of the box in the texture, ie. the height of the front part of the box texture, in pixels + * @param length Length (z axis) of the box in the texture, ie. the height of the top part of the box texture, in pixels + * @return This box builder + */ + public BoxBuilder withBoxTextureDimensions(int width, int height, int length) { + widthOnTexture = width; + heightOnTexture = height; + lengthOnTexture = length; + return this; + } + private UVMapHelper getUVMapHelper() { UVMapHelper helper = new UVMapHelper(textureWidth, textureHeight, widthOnTexture, lengthOnTexture, heightOnTexture, boxU, boxV); if (this.flipX) { @@ -137,76 +167,148 @@ private UVMapHelper getUVMapHelper() { return helper; } + /** + * Add a top face (facing up, towards positive y). + * + * @return This box builder + */ public BoxBuilder addTopFace() { sides[0] = getUVMapHelper().top(); return this; } + /** + * Add a top face (facing up, towards positive y). + * + * @param consumer Function to customize the added face + * @return This box builder + */ public BoxBuilder addTopFace(Consumer consumer) { addTopFace(); consumer.accept(sides[0]); return this; } + /** + * Add a bottom face (facing down, towards negative y). + * + * @return This box builder + */ public BoxBuilder addBottomFace() { sides[1] = getUVMapHelper().bottom(); return this; } + /** + * Add a bottom face (facing down, towards negative y). + * + * @param consumer Function to customize the added face + * @return This box builder + */ public BoxBuilder addBottomFace(Consumer consumer) { addBottomFace(); consumer.accept(sides[1]); return this; } + /** + * Add a left face (facing west, towards negative x). + * + * @return This box builder + */ public BoxBuilder addLeftFace() { sides[2] = getUVMapHelper().left(); return this; } + /** + * Add a left face (facing west, towards negative x). + * + * @param consumer Function to customize the added face + * @return This box builder + */ public BoxBuilder addLeftFace(Consumer consumer) { addLeftFace(); consumer.accept(sides[2]); return this; } + /** + * Add a right face (facing east, towards positive x). + * + * @return This box builder + */ public BoxBuilder addRightFace() { sides[3] = getUVMapHelper().right(); return this; } + /** + * Add a right face (facing east, towards positive x). + * + * @param consumer Function to customize the added face + * @return This box builder + */ public BoxBuilder addRightFace(Consumer consumer) { addRightFace(); consumer.accept(sides[3]); return this; } + /** + * Add a front face (facing north, towards negative z). + * + * @return This box builder + */ public BoxBuilder addFrontFace() { sides[4] = getUVMapHelper().front(); return this; } + /** + * Add a front face (facing north, towards negative z). + * + * @param consumer Function to customize the added face + * @return This box builder + */ public BoxBuilder addFrontFace(Consumer consumer) { addFrontFace(); consumer.accept(sides[4]); return this; } + /** + * Add a back face (facing south, towards positive z). + * + * @return This box builder + */ public BoxBuilder addBackFace() { sides[5] = getUVMapHelper().back(); return this; } + /** + * Add a back face (facing south, towards positive z). + * + * @param consumer Function to customize the added face + * @return This box builder + */ public BoxBuilder addBackFace(Consumer consumer) { addBackFace(); consumer.accept(sides[5]); return this; } - public BoxBuilder useEntityTexture(Texture texture) { - return forTextureSize(texture, 64, 64); - } - + /** + * Configure this box to use the given texture with the given dimensions. + * These dimensions are used for convenient UV calculations, it's recommended to set them to the Vanilla Minecraft texture dimensions. + * The actual size of the texture can be different, as all values are normalized to [0..1] in for the generated models. + * + * @param texture Texture to be used + * @param width Texture width, eg. 64 for piglin texture, 256 for enderdragon texture + * @param width Texture height, eg. 64 for piglin texture, 256 for enderdragon texture + * @return This box builder + */ public BoxBuilder forTextureSize(Texture texture, int width, int height) { this.texture = texture; this.textureWidth = width; @@ -214,25 +316,78 @@ public BoxBuilder forTextureSize(Texture texture, int width, int height) { return this; } + /** + * Set the UV coordinates where this boxes textures start. Minecraft uses the same texture layout for all boxes, + * this specifies the top/left position of that layout for this box. + * + * @param u X coordinate of the box texture starting point + * @param v Y coordinate of the box texture starting point + * @return This box builder + */ public BoxBuilder atUVCoordinates(int u, int v) { boxU = u; boxV = v; return this; } + /** + * Mirror all textures horizontally. This applies to all sides that were already added to this box as well as + * to sides that will be added after calling this method. + * + * @return This box builder + */ public BoxBuilder flipX() { + for (UVMapHelper.Side side : sides) { + if (side != null) { + side.flipX(); + } + } + flipX = !flipX; return this; } + /** + * Mirror all textures vertically. This applies to all sides that were already added to this box as well as + * to sides that will be added after calling this method. + * + * @return This box builder + */ public BoxBuilder flipY() { + for (UVMapHelper.Side side : sides) { + if (side != null) { + side.flipY(); + } + } + flipY = !flipY; return this; } + /** + * Add a transformation that will be applied to this box when it is converted into a model. + * Multiple calls to this method chain the transformations. + * + * @param transform Transformation + * @return This box builder + */ public BoxBuilder transform(Transform transform) { this.transform = this.transform.chain(transform); return this; } + + /** + * Mirror this box accross the x axis (ie. west/east axis). + * + * @return This box builder + */ + public BoxBuilder mirrorX() { + // NOTE: once we support rotating faces, we actually have to mirror the model instead of just changing textures + flipX(); + UVMapHelper.Side tmp = this.sides[2]; + this.sides[2] = this.sides[3]; + this.sides[3] = tmp; + return this; + } } } diff --git a/chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java b/chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java index fdbe51704d..54e676f200 100644 --- a/chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java +++ b/chunky/src/java/se/llbit/chunky/model/builder/UVMapHelper.java @@ -80,6 +80,11 @@ protected Side(double x0, double x1, double y0, double y1) { this.y1 = y1; } + /** + * Flip the texture horizontally. + * + * @return This side + */ public Side flipX() { double tmp = x0; x0 = x1; @@ -87,6 +92,11 @@ public Side flipX() { return this; } + /** + * Flip the texture vertically. + * + * @return This side + */ public Side flipY() { double tmp = y0; y0 = y1; From 4e5f900766519cd878067e2ba121231a35fded40 Mon Sep 17 00:00:00 2001 From: Maik Marschner Date: Sat, 6 Apr 2024 20:19:35 +0200 Subject: [PATCH 3/3] Re-implement dragon head model with BoxModelBuilder and fix incorrect dimensions and jaw angle. --- .../se/llbit/chunky/entity/SkullEntity.java | 243 ++++-------------- 1 file changed, 43 insertions(+), 200 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java b/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java index a9660df221..9800b64b24 100644 --- a/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java +++ b/chunky/src/java/se/llbit/chunky/entity/SkullEntity.java @@ -25,7 +25,10 @@ import se.llbit.chunky.world.material.TextureMaterial; import se.llbit.json.JsonObject; import se.llbit.json.JsonValue; -import se.llbit.math.*; +import se.llbit.math.Quad; +import se.llbit.math.QuickMath; +import se.llbit.math.Transform; +import se.llbit.math.Vector3; import se.llbit.math.primitive.Box; import se.llbit.math.primitive.Primitive; @@ -51,205 +54,45 @@ public enum Kind { //#region Dragon head //String dragonHeadJson = "{\"elements\":[{\"from\":[2,0,2],\"to\":[14,12,14],\"faces\":{\"up\":{\"uv\":[8,2.875,9,1.875],\"texture\":\"entity/enderdragon/dragon\"},\"down\":{\"uv\":[8.9375,1.875,10,2.875],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[7,2.875,8,3.875],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[9,2.875,10,3.875],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[8,2.875,9,3.875],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[10,2.875,11,3.875],\"texture\":\"entity/enderdragon/dragon\"}}},{\"from\":[4,3,-8],\"to\":[12,6.5,3],\"faces\":{\"up\":{\"uv\":[12.75,3.75,12,2.75],\"texture\":\"entity/enderdragon/dragon\"},\"down\":{\"uv\":[12.8125,2.6875,13.4375,3.625],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[11,3.75,12.0625,4.0625],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[12.75,3.75,13.75,4.0625],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[12,3.75,12.75,4.0625],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[13.75,3.75,14.5,4.0625],\"texture\":\"entity/enderdragon/dragon\"}}},{\"from\":[4,0,-8],\"to\":[12,2.5,3],\"faces\":{\"up\":{\"uv\":[12.75,5.0625,12,4.0625],\"texture\":\"entity/enderdragon/dragon\"},\"down\":{\"uv\":[13.5,4.0625,12.75,5.0625],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[11,5.0625,12,5.3125],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[12.75,5.0625,13.75,5.3125],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[12,5.0625,12.75,5.3125],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[13.75,5.0625,14.5,5.3125],\"texture\":\"entity/enderdragon/dragon\"}}},{\"from\":[10.3,12,6.6],\"to\":[11.7,15,10.9],\"faces\":{\"up\":{\"uv\":[0.375,0.375,0.5,0],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[0.875,0.625,0.5,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[0.375,0.375,0,0.625],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[0.5,0.375,0.375,0.625],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[1,0.375,0.875,0.625],\"texture\":\"entity/enderdragon/dragon\"}}},{\"from\":[4.3,12,6.6],\"to\":[5.7,15,10.9],\"faces\":{\"up\":{\"uv\":[0.5,0.375,0.375,0],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[0,0.375,0.375,0.625],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[0.5,0.375,0.875,0.625],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[0.375,0.375,0.5,0.625],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[0.875,0.375,1,0.625],\"texture\":\"entity/enderdragon/dragon\"}}},{\"from\":[10.3,6.5,-5],\"to\":[11.7,8,-2],\"faces\":{\"up\":{\"uv\":[7.375,0,7.25,0.25],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[7.625,0.25,7.375,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[7.25,0.25,7,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[7.375,0.25,7.25,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[7.75,0.25,7.625,0.375],\"texture\":\"entity/enderdragon/dragon\"}}},{\"from\":[4.3,6.5,-5],\"to\":[5.7,8,-2],\"faces\":{\"up\":{\"uv\":[7.25,0,7.375,0.25],\"texture\":\"entity/enderdragon/dragon\"},\"east\":{\"uv\":[7,0.25,7.25,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"west\":{\"uv\":[7.375,0.25,7.625,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"north\":{\"uv\":[7.25,0.25,7.375,0.375],\"texture\":\"entity/enderdragon/dragon\"},\"south\":{\"uv\":[7.625,0.25,7.75,0.375],\"texture\":\"entity/enderdragon/dragon\"}}}]}"; - private static final Quad[] dragonHead = { - // face - new Quad( - new Vector3(2 / 16.0, 12 / 16.0, 14 / 16.0), - new Vector3(14 / 16.0, 12 / 16.0, 14 / 16.0), - new Vector3(2 / 16.0, 12 / 16.0, 2 / 16.0), - new Vector4(8 / 16.0, 9 / 16.0, 14.125 / 16.0, 13.125 / 16.0)), - new Quad( - new Vector3(2 / 16.0, 0, 2 / 16.0), - new Vector3(14 / 16.0, 0, 2 / 16.0), - new Vector3(2 / 16.0, 0, 14 / 16.0), - new Vector4(8.9375 / 16.0, 10 / 16.0, 13.125 / 16.0, 14.125 / 16.0)), - new Quad( - new Vector3(14 / 16.0, 0, 14 / 16.0), - new Vector3(14 / 16.0, 0, 2 / 16.0), - new Vector3(14 / 16.0, 12 / 16.0, 14 / 16.0), - new Vector4(7 / 16.0, 8 / 16.0, 12.125 / 16.0, 13.125 / 16.0)), - new Quad( - new Vector3(2 / 16.0, 0, 2 / 16.0), - new Vector3(2 / 16.0, 0, 14 / 16.0), - new Vector3(2 / 16.0, 12 / 16.0, 2 / 16.0), - new Vector4(9 / 16.0, 10 / 16.0, 12.125 / 16.0, 13.125 / 16.0)), - new Quad( - new Vector3(14 / 16.0, 0, 2 / 16.0), - new Vector3(2 / 16.0, 0, 2 / 16.0), - new Vector3(14 / 16.0, 12 / 16.0, 2 / 16.0), - new Vector4(8 / 16.0, 9 / 16.0, 12.125 / 16.0, 13.125 / 16.0)), - new Quad( - new Vector3(2 / 16.0, 0, 14 / 16.0), - new Vector3(14 / 16.0, 0, 14 / 16.0), - new Vector3(2 / 16.0, 12 / 16.0, 14 / 16.0), - new Vector4(10 / 16.0, 11 / 16.0, 12.125 / 16.0, 13.125 / 16.0)), - // mouth_upper - new Quad( - new Vector3(4 / 16.0, 6.5 / 16.0, 3 / 16.0), - new Vector3(12 / 16.0, 6.5 / 16.0, 3 / 16.0), - new Vector3(4 / 16.0, 6.5 / 16.0, -8 / 16.0), - new Vector4(12.75 / 16.0, 12 / 16.0, 13.25 / 16.0, 12.25 / 16.0)), - new Quad( - new Vector3(4 / 16.0, 3 / 16.0, -8 / 16.0), - new Vector3(12 / 16.0, 3 / 16.0, -8 / 16.0), - new Vector3(4 / 16.0, 3 / 16.0, 3 / 16.0), - new Vector4(12.8125 / 16.0, 13.4375 / 16.0, 12.375 / 16.0, 13.3125 / 16.0)), - new Quad( - new Vector3(12 / 16.0, 3 / 16.0, 3 / 16.0), - new Vector3(12 / 16.0, 3 / 16.0, -8 / 16.0), - new Vector3(12 / 16.0, 6.5 / 16.0, 3 / 16.0), - new Vector4(11 / 16.0, 12.0625 / 16.0, 11.9375 / 16.0, 12.25 / 16.0)), - new Quad( - new Vector3(4 / 16.0, 3 / 16.0, -8 / 16.0), - new Vector3(4 / 16.0, 3 / 16.0, 3 / 16.0), - new Vector3(4 / 16.0, 6.5 / 16.0, -8 / 16.0), - new Vector4(12.75 / 16.0, 13.75 / 16.0, 11.9375 / 16.0, 12.25 / 16.0)), - new Quad( - new Vector3(12 / 16.0, 3 / 16.0, -8 / 16.0), - new Vector3(4 / 16.0, 3 / 16.0, -8 / 16.0), - new Vector3(12 / 16.0, 6.5 / 16.0, -8 / 16.0), - new Vector4(12 / 16.0, 12.75 / 16.0, 11.9375 / 16.0, 12.25 / 16.0)), - new Quad( - new Vector3(4 / 16.0, 3 / 16.0, 3 / 16.0), - new Vector3(12 / 16.0, 3 / 16.0, 3 / 16.0), - new Vector3(4 / 16.0, 6.5 / 16.0, 3 / 16.0), - new Vector4(13.75 / 16.0, 14.5 / 16.0, 11.9375 / 16.0, 12.25 / 16.0)), - // mouth_lower - new Quad( - new Vector3(4 / 16.0, 2.5 / 16.0, 3 / 16.0), - new Vector3(12 / 16.0, 2.5 / 16.0, 3 / 16.0), - new Vector3(4 / 16.0, 2.5 / 16.0, -8 / 16.0), - new Vector4(12.75 / 16.0, 12 / 16.0, 11.9375 / 16.0, 10.9375 / 16.0)), - new Quad( - new Vector3(4 / 16.0, 0, -8 / 16.0), - new Vector3(12 / 16.0, 0, -8 / 16.0), - new Vector3(4 / 16.0, 0, 3 / 16.0), - new Vector4(13.5 / 16.0, 12.75 / 16.0, 10.9375 / 16.0, 11.9375 / 16.0)), - new Quad( - new Vector3(12 / 16.0, 0, 3 / 16.0), - new Vector3(12 / 16.0, 0, -8 / 16.0), - new Vector3(12 / 16.0, 2.5 / 16.0, 3 / 16.0), - new Vector4(11 / 16.0, 12 / 16.0, 10.6875 / 16.0, 10.9375 / 16.0)), - new Quad( - new Vector3(4 / 16.0, 0, -8 / 16.0), - new Vector3(4 / 16.0, 0, 3 / 16.0), - new Vector3(4 / 16.0, 2.5 / 16.0, -8 / 16.0), - new Vector4(12.75 / 16.0, 13.75 / 16.0, 10.6875 / 16.0, 10.9375 / 16.0)), - new Quad( - new Vector3(12 / 16.0, 0, -8 / 16.0), - new Vector3(4 / 16.0, 0, -8 / 16.0), - new Vector3(12 / 16.0, 2.5 / 16.0, -8 / 16.0), - new Vector4(12 / 16.0, 12.75 / 16.0, 10.6875 / 16.0, 10.9375 / 16.0)), - new Quad( - new Vector3(4 / 16.0, 0, 3 / 16.0), - new Vector3(12 / 16.0, 0, 3 / 16.0), - new Vector3(4 / 16.0, 2.5 / 16.0, 3 / 16.0), - new Vector4(13.75 / 16.0, 14.5 / 16.0, 10.6875 / 16.0, 10.9375 / 16.0)), - // ear_right - new Quad( - new Vector3(10.3 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector3(11.7 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector3(10.3 / 16.0, 15 / 16.0, 6.6 / 16.0), - new Vector4(0.375 / 16.0, 0.5 / 16.0, 16 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(11.7 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(11.7 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(11.7 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector4(0.875 / 16.0, 0.5 / 16.0, 15.625 / 16.0, 15.375 / 16.0)), - new Quad( - new Vector3(10.3 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(10.3 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(10.3 / 16.0, 15 / 16.0, 6.6 / 16.0), - new Vector4(0.375 / 16.0, 0, 15.375 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(11.7 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(10.3 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(11.7 / 16.0, 15 / 16.0, 6.6 / 16.0), - new Vector4(0.5 / 16.0, 0.375 / 16.0, 15.375 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(10.3 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(11.7 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(10.3 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector4(1 / 16.0, 0.875 / 16.0, 15.375 / 16.0, 15.625 / 16.0)), - // ear_left - new Quad( - new Vector3(4.3 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector3(5.7 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector3(4.3 / 16.0, 15 / 16.0, 6.6 / 16.0), - new Vector4(0.5 / 16.0, 0.375 / 16.0, 16 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(5.7 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(5.7 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(5.7 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector4(0, 0.375 / 16.0, 15.375 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(4.3 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(4.3 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(4.3 / 16.0, 15 / 16.0, 6.6 / 16.0), - new Vector4(0.5 / 16.0, 0.875 / 16.0, 15.375 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(5.7 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(4.3 / 16.0, 12 / 16.0, 6.6 / 16.0), - new Vector3(5.7 / 16.0, 15 / 16.0, 6.6 / 16.0), - new Vector4(0.375 / 16.0, 0.5 / 16.0, 15.375 / 16.0, 15.625 / 16.0)), - new Quad( - new Vector3(4.3 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(5.7 / 16.0, 12 / 16.0, 10.9 / 16.0), - new Vector3(4.3 / 16.0, 15 / 16.0, 10.9 / 16.0), - new Vector4(0.875 / 16.0, 1 / 16.0, 15.375 / 16.0, 15.625 / 16.0)), - // nose right - new Quad( - new Vector3(10.3 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector3(11.7 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector3(10.3 / 16.0, 8 / 16.0, -5 / 16.0), - new Vector4(7.375 / 16.0, 7.25 / 16.0, 15.75 / 16.0, 16 / 16.0)), - new Quad( - new Vector3(11.7 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(11.7 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(11.7 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector4(7.625 / 16.0, 7.375 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - new Quad( - new Vector3(10.3 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(10.3 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(10.3 / 16.0, 8 / 16.0, -5 / 16.0), - new Vector4(7.25 / 16.0, 7 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - new Quad( - new Vector3(11.7 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(10.3 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(11.7 / 16.0, 8 / 16.0, -5 / 16.0), - new Vector4(7.375 / 16.0, 7.25 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - new Quad( - new Vector3(10.3 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(11.7 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(10.3 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector4(7.75 / 16.0, 7.625 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - // nose_left - new Quad( - new Vector3(4.3 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector3(5.7 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector3(4.3 / 16.0, 8 / 16.0, -5 / 16.0), - new Vector4(7.25 / 16.0, 7.375 / 16.0, 15.75 / 16.0, 16 / 16.0)), - new Quad( - new Vector3(5.7 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(5.7 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(5.7 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector4(7 / 16.0, 7.25 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - new Quad( - new Vector3(4.3 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(4.3 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(4.3 / 16.0, 8 / 16.0, -5 / 16.0), - new Vector4(7.375 / 16.0, 7.625 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - new Quad( - new Vector3(5.7 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(4.3 / 16.0, 6.5 / 16.0, -5 / 16.0), - new Vector3(5.7 / 16.0, 8 / 16.0, -5 / 16.0), - new Vector4(7.25 / 16.0, 7.375 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - new Quad( - new Vector3(4.3 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(5.7 / 16.0, 6.5 / 16.0, -2 / 16.0), - new Vector3(4.3 / 16.0, 8 / 16.0, -2 / 16.0), - new Vector4(7.625 / 16.0, 7.75 / 16.0, 15.625 / 16.0, 15.75 / 16.0)), - }; + private static final Quad[] dragonHead = new BoxModelBuilder() + // head + .addBox(new Vector3(2 / 16., 0, 2 / 16.), new Vector3(14 / 16., 12 / 16., 14 / 16.), box -> + box.withBoxTextureDimensions(16, 16, 16) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(112, 30) + .addTopFace().addBottomFace().addFrontFace().addBackFace().addLeftFace().addRightFace()) + // jaw + .addBox(new Vector3(3.5 / 16., 3 / 16., -8.5 / 16.), new Vector3(12.5 / 16., 6.75 / 16., 3.5 / 16.), box -> + box.withBoxTextureDimensions(12, 5, 16) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(176, 44) + .addTopFace().addBottomFace(UVMapHelper.Side::flipY).addFrontFace().addBackFace().addLeftFace().addRightFace()) + .addBox(new Vector3(3.5 / 16., 0 / 16., -8.5 / 16.), new Vector3(12.5 / 16., 3 / 16., 3.5 / 16.), box -> + box.withBoxTextureDimensions(12, 4, 16) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(176, 65) + .addTopFace().addBottomFace().addFrontFace().addBackFace().addLeftFace().addRightFace() + .transform(Transform.NONE + .translate(0, 0.5 - 3 / 16., 0.5 - 3.5 / 16.) + .rotateX(Math.toRadians(-11.25)) + .translate(0, -0.5 + 3 / 16., -0.5 + 3.5 / 16.) + )) + // ears + .addBox(new Vector3(10.3 / 16., 12 / 16., 6.6 / 16.), new Vector3(11.7 / 16., 15 / 16., 10.9 / 16.), box -> + box.withBoxTextureDimensions(2, 4, 6) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(0, 0) + .addTopFace().addFrontFace().addBackFace().addLeftFace().addRightFace().flipX().mirrorX()) + .addBox(new Vector3(4.3 / 16., 12 / 16., 6.6 / 16.), new Vector3(5.7 / 16., 15 / 16., 10.9 / 16.), box -> + box.withBoxTextureDimensions(2, 4, 6) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(0, 0) + .addTopFace().addFrontFace().addBackFace().addLeftFace().addRightFace().flipX()) + // nose + .addBox(new Vector3(10.25 / 16., 6.5 / 16., -7 / 16.), new Vector3(11.75 / 16., 8 / 16., -4 / 16.), box -> + box.withBoxTextureDimensions(2, 2, 4) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(112, 0) + .addTopFace().addFrontFace().addBackFace().addLeftFace().addRightFace().mirrorX()) + .addBox(new Vector3(4.25 / 16., 6.5 / 16., -7 / 16.), new Vector3(5.75 / 16., 8 / 16., -4 / 16.), box -> + box.withBoxTextureDimensions(2, 2, 4) + .forTextureSize(Texture.dragon, 256, 256).atUVCoordinates(112, 0) + .addTopFace().addFrontFace().addBackFace().addLeftFace().addRightFace()) + .toQuads(); //#endregion //#region Piglin head