From 1ebfc59fc44fa5b4372c66dee4c132aa03220b1e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 Mar 2023 17:08:26 -0700 Subject: [PATCH 1/4] Improve AABB performance. --- .../java/se/llbit/chunky/model/AABBModel.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/model/AABBModel.java b/chunky/src/java/se/llbit/chunky/model/AABBModel.java index a78da34c31..87a5212529 100644 --- a/chunky/src/java/se/llbit/chunky/model/AABBModel.java +++ b/chunky/src/java/se/llbit/chunky/model/AABBModel.java @@ -7,8 +7,6 @@ import se.llbit.math.Ray; import se.llbit.math.Vector3; -import java.util.Arrays; -import java.util.List; import java.util.Random; /** @@ -81,44 +79,45 @@ public boolean intersect(Ray ray, Scene scene) { for (int i = 0; i < boxes.length; ++i) { if (boxes[i].intersect(ray)) { Tint[] tintedFacesBox = tintedFaces != null ? tintedFaces[i] : null; + UVMapping[] mappingBox = mapping != null ? mapping[i] : null; Vector3 n = ray.getNormal(); if (n.y > 0) { // top ray.v = 1 - ray.v; - if (intersectFace(ray, scene, textures[i][4], - mapping != null ? mapping[i][4] : null + if (intersectFace(ray, textures[i][4], + mappingBox != null ? mappingBox[4] : null )) { tint = tintedFacesBox != null ? tintedFacesBox[4] : Tint.NONE; hit = true; } } else if (n.y < 0) { // bottom - if (intersectFace(ray, scene, textures[i][5], - mapping != null ? mapping[i][5] : null)) { + if (intersectFace(ray, textures[i][5], + mappingBox != null ? mappingBox[5] : null)) { hit = true; tint = tintedFacesBox != null ? tintedFacesBox[5] : Tint.NONE; } } else if (n.z < 0) { // north - if (intersectFace(ray, scene, textures[i][0], - mapping != null ? mapping[i][0] : null + if (intersectFace(ray, textures[i][0], + mappingBox != null ? mappingBox[0] : null )) { hit = true; tint = tintedFacesBox != null ? tintedFacesBox[0] : Tint.NONE; } } else if (n.z > 0) { // south - if (intersectFace(ray, scene, textures[i][2], - mapping != null ? mapping[i][2] : null + if (intersectFace(ray, textures[i][2], + mappingBox != null ? mappingBox[2] : null )) { hit = true; tint = tintedFacesBox != null ? tintedFacesBox[2] : Tint.NONE; } } else if (n.x < 0) { // west - if (intersectFace(ray, scene, textures[i][3], - mapping != null ? mapping[i][3] : null)) { + if (intersectFace(ray, textures[i][3], + mappingBox != null ? mappingBox[3] : null)) { hit = true; tint = tintedFacesBox != null ? tintedFacesBox[3] : Tint.NONE; } } else if (n.x > 0) { // east - if (intersectFace(ray, scene, textures[i][1], - mapping != null ? mapping[i][1] : null)) { + if (intersectFace(ray, textures[i][1], + mappingBox != null ? mappingBox[1] : null)) { hit = true; tint = tintedFacesBox != null ? tintedFacesBox[1] : Tint.NONE; } @@ -140,7 +139,7 @@ public boolean intersect(Ray ray, Scene scene) { return hit; } - private boolean intersectFace(Ray ray, Scene scene, Texture texture, UVMapping mapping) { + private boolean intersectFace(Ray ray, Texture texture, UVMapping mapping) { // This is the method that handles intersecting faces of all AABB-based models. // Do normal mapping, parallax occlusion mapping, specular maps and all the good stuff here! From 69080fd84fe0c5e689ee33e1e923a701712a9841 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 Mar 2023 20:55:02 -0700 Subject: [PATCH 2/4] Use textured block model for simple oriented blocks. --- .../FixedTopBottomRotatableTexturedBlock.java | 27 +-- .../block/TopBottomOrientedTexturedBlock.java | 5 +- .../chunky/model/TexturedBlockModel.java | 162 ++++++++++++------ chunky/src/java/se/llbit/math/Octree.java | 49 +++++- 4 files changed, 174 insertions(+), 69 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/block/FixedTopBottomRotatableTexturedBlock.java b/chunky/src/java/se/llbit/chunky/block/FixedTopBottomRotatableTexturedBlock.java index 5132017cc9..043f9b58aa 100644 --- a/chunky/src/java/se/llbit/chunky/block/FixedTopBottomRotatableTexturedBlock.java +++ b/chunky/src/java/se/llbit/chunky/block/FixedTopBottomRotatableTexturedBlock.java @@ -1,6 +1,6 @@ package se.llbit.chunky.block; -import se.llbit.chunky.model.FixedTopBottomRotatableTexturedBlockModel; +import se.llbit.chunky.model.TexturedBlockModel; import se.llbit.chunky.resources.Texture; /** @@ -9,18 +9,19 @@ * If the top and bottom should rotate with the rest of the block, use TopBottomOrientedTexturedBlock. */ public class FixedTopBottomRotatableTexturedBlock extends AbstractModelBlock { - public FixedTopBottomRotatableTexturedBlock(String name, String facing, Texture front, Texture side, Texture top) { - this(name, facing, front, side, side, side, top, top); - } + public FixedTopBottomRotatableTexturedBlock(String name, String facing, Texture front, Texture side, Texture top) { + this(name, facing, front, side, side, side, top, top); + } - public FixedTopBottomRotatableTexturedBlock(String name, String facing, Texture front, Texture side, Texture top, Texture bottom) { - this(name, facing, front, side, side, side, top, bottom); - } + public FixedTopBottomRotatableTexturedBlock(String name, String facing, Texture front, Texture side, Texture top, Texture bottom) { + this(name, facing, front, side, side, side, top, bottom); + } - public FixedTopBottomRotatableTexturedBlock(String name, String facing, Texture front, Texture south, Texture east, Texture west, Texture top, Texture bottom) { - super(name, front); - this.model = new FixedTopBottomRotatableTexturedBlockModel(facing, front, south, east, west, top, bottom); - solid = true; - opaque = true; - } + public FixedTopBottomRotatableTexturedBlock(String name, String facing, Texture front, Texture south, Texture east, Texture west, Texture top, Texture bottom) { + super(name, front); + this.model = new TexturedBlockModel(TexturedBlockModel.Orientation.fromFacing(facing, true), + front, east, south, west, top, bottom, null); + solid = true; + opaque = true; + } } diff --git a/chunky/src/java/se/llbit/chunky/block/TopBottomOrientedTexturedBlock.java b/chunky/src/java/se/llbit/chunky/block/TopBottomOrientedTexturedBlock.java index e5f5e271c3..fb4e7fec2c 100644 --- a/chunky/src/java/se/llbit/chunky/block/TopBottomOrientedTexturedBlock.java +++ b/chunky/src/java/se/llbit/chunky/block/TopBottomOrientedTexturedBlock.java @@ -1,6 +1,6 @@ package se.llbit.chunky.block; -import se.llbit.chunky.model.TopBottomOrientedTexturedBlockModel; +import se.llbit.chunky.model.TexturedBlockModel; import se.llbit.chunky.resources.Texture; /** @@ -19,7 +19,8 @@ public TopBottomOrientedTexturedBlock(String name, String facing, Texture front, public TopBottomOrientedTexturedBlock(String name, String facing, Texture front, Texture south, Texture east, Texture west, Texture top, Texture bottom) { super(name, front); - this.model = new TopBottomOrientedTexturedBlockModel(facing, front, south, east, west, top, bottom); + this.model = new TexturedBlockModel(TexturedBlockModel.Orientation.fromFacing(facing, false), + front, east, south, west, top, bottom, null); solid = true; opaque = true; } diff --git a/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java b/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java index 1c6bfda3d5..28e6702712 100644 --- a/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java +++ b/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java @@ -16,12 +16,13 @@ */ package se.llbit.chunky.model; -import se.llbit.chunky.block.Air; +import se.llbit.chunky.renderer.scene.Scene; import se.llbit.chunky.resources.Texture; +import se.llbit.log.Log; import se.llbit.math.AABB; -import se.llbit.math.QuickMath; import se.llbit.math.Ray; -import se.llbit.math.Vector3; +import se.llbit.util.annotation.NotNull; +import se.llbit.util.annotation.Nullable; /** * A textured block. @@ -29,68 +30,125 @@ * @author Jesper Öqvist */ public class TexturedBlockModel extends AABBModel { - private static final AABB[] boxes = { new AABB(0, 1, 0, 1, 0, 1) }; + public enum Orientation { + NORTH, SOUTH, EAST, WEST, + SIDE_NORTH, SIDE_SOUTH, SIDE_EAST, SIDE_WEST, - private final Texture[][] textures; + ; + public static Orientation fromFacing(String facing, boolean side) { + switch (facing) { + case "north": + return side ? SIDE_NORTH : NORTH; + case "south": + return side ? SIDE_SOUTH : SOUTH; + case "east": + return side ? SIDE_EAST : EAST; + case "west": + return side ? SIDE_WEST : WEST; + default: + throw new IllegalArgumentException("Invalid facing: " + facing); + } + } + } + + protected static final AABB box = new AABB(0, 1, 0, 1, 0, 1); + protected final Texture[] textures; + protected final UVMapping[] mappings; + protected final Tint[] tints; public TexturedBlockModel(Texture north, Texture east, Texture south, Texture west, Texture top, Texture bottom) { - this.textures = new Texture[][] {{ - north, east, south, west, top, bottom - }}; + this(new Texture[] { north, east, south, west, top, bottom }, null, null); } + public TexturedBlockModel(Orientation orientation, Texture north, Texture east, Texture south, Texture west, + Texture top, Texture bottom, @Nullable Tint[] tints) { + this( + mapTextures(orientation, north, east, south, west, top, bottom), + mapUV(orientation), + tints + ); + } + + public TexturedBlockModel(@NotNull Texture[] textures, @Nullable UVMapping[] mappings, @Nullable Tint[] tints) { + this.textures = textures; + this.textures2 = new Texture[][] { textures }; + + this.mappings = mappings; + this.mappings2 = mappings == null ? null : new UVMapping[][] { mappings }; + + this.tints = tints; + this.tints2 = tints == null ? null : new Tint[][] { tints }; + } + + private static Texture[] mapTextures(Orientation orientation, Texture north, Texture east, Texture south, + Texture west, Texture top, Texture bottom) { + switch (orientation) { + default: + Log.warn("Unknown orientation: " + orientation); + case NORTH: + case SIDE_NORTH: + return new Texture[] { north, east, south, west, top, bottom }; + case SOUTH: + case SIDE_SOUTH: + return new Texture[] { south, west, north, east, top, bottom }; + case EAST: + case SIDE_EAST: + return new Texture[] { west, north, east, south, top, bottom }; + case WEST: + case SIDE_WEST: + return new Texture[] { east, south, west, north, top, bottom }; + } + } + + private static UVMapping[] mapUV(Orientation orientation) { + switch (orientation) { + default: + Log.warn("Unknown orientation: " + orientation); + case NORTH: + return null; + case SOUTH: + return new UVMapping[] {null, null, null, null, UVMapping.ROTATE_180, UVMapping.ROTATE_180}; + case EAST: + return new UVMapping[] {null, null, null, null, UVMapping.ROTATE_90, UVMapping.ROTATE_90}; + case WEST: + return new UVMapping[] {null, null, null, null, UVMapping.ROTATE_270, UVMapping.ROTATE_270}; + case SIDE_EAST: + case SIDE_SOUTH: + case SIDE_NORTH: + case SIDE_WEST: + return null; + } + } + + @Override + public boolean intersect(Ray ray, Scene scene) { + // TODO: Optimize + return super.intersect(ray, scene); + } + + // This stuff is just to adhere to the interface. + private static final AABB[] boxes = { box }; + private final Texture[][] textures2; + private final UVMapping[][] mappings2; + private final Tint[][] tints2; + @Override - public AABB[] getBoxes() { + public final AABB[] getBoxes() { return boxes; } @Override - public Texture[][] getTextures() { - return textures; + public final Texture[][] getTextures() { + return textures2; } - /** - * Find the color of the object at the intersection point. - * - * @param ray ray to test - */ - public static void getIntersectionColor(Ray ray) { - if (ray.getCurrentMaterial() == Air.INSTANCE) { - ray.color.x = 1; - ray.color.y = 1; - ray.color.z = 1; - ray.color.w = 0; - return; - } - getTextureCoordinates(ray); - ray.getCurrentMaterial().getColor(ray); + @Override + public final UVMapping[][] getUVMapping() { + return mappings2; } - /** - * Calculate the UV coordinates for the ray on the intersected block. - * - * @param ray ray to test - */ - private static void getTextureCoordinates(Ray ray) { - int bx = (int) QuickMath.floor(ray.o.x); - int by = (int) QuickMath.floor(ray.o.y); - int bz = (int) QuickMath.floor(ray.o.z); - Vector3 n = ray.getNormal(); - if (n.y != 0) { - ray.u = ray.o.x - bx; - ray.v = ray.o.z - bz; - } else if (n.x != 0) { - ray.u = ray.o.z - bz; - ray.v = ray.o.y - by; - } else { - ray.u = ray.o.x - bx; - ray.v = ray.o.y - by; - } - if (n.x > 0 || n.z < 0) { - ray.u = 1 - ray.u; - } - if (n.y > 0) { - ray.v = 1 - ray.v; - } + @Override + public final Tint[][] getTints() { + return tints2; } } diff --git a/chunky/src/java/se/llbit/math/Octree.java b/chunky/src/java/se/llbit/math/Octree.java index 3243d9d725..2f57be32e2 100644 --- a/chunky/src/java/se/llbit/math/Octree.java +++ b/chunky/src/java/se/llbit/math/Octree.java @@ -560,7 +560,7 @@ public boolean enterBlock(Scene scene, Ray ray, BlockPalette palette) { // Origin and distance of ray need to be updated ray.o.scaleAdd(distance, ray.d); ray.distance += distance; - TexturedBlockModel.getIntersectionColor(ray); + getIntersectionColor(ray); if (currentBlock.opaque) { ray.color.w = 1; } @@ -670,7 +670,7 @@ public boolean exitWater(Scene scene, Ray ray, BlockPalette palette) { } return true; } else if (currentBlock != Air.INSTANCE) { - TexturedBlockModel.getIntersectionColor(ray); + getIntersectionColor(ray); if (currentBlock.opaque) { ray.color.w = 1; } @@ -831,6 +831,51 @@ public void switchImplementation(String newImplementation, TaskTracker.Task task } } + /** + * Find the color of the object at the intersection point. + * + * @param ray ray to test + */ + public static void getIntersectionColor(Ray ray) { + if (ray.getCurrentMaterial() == Air.INSTANCE) { + ray.color.x = 1; + ray.color.y = 1; + ray.color.z = 1; + ray.color.w = 0; + return; + } + getTextureCoordinates(ray); + ray.getCurrentMaterial().getColor(ray); + } + + /** + * Calculate the UV coordinates for the ray on the intersected block. + * + * @param ray ray to test + */ + private static void getTextureCoordinates(Ray ray) { + int bx = (int) QuickMath.floor(ray.o.x); + int by = (int) QuickMath.floor(ray.o.y); + int bz = (int) QuickMath.floor(ray.o.z); + Vector3 n = ray.getNormal(); + if (n.y != 0) { + ray.u = ray.o.x - bx; + ray.v = ray.o.z - bz; + } else if (n.x != 0) { + ray.u = ray.o.z - bz; + ray.v = ray.o.y - by; + } else { + ray.u = ray.o.x - bx; + ray.v = ray.o.y - by; + } + if (n.x > 0 || n.z < 0) { + ray.u = 1 - ray.u; + } + if (n.y > 0) { + ray.v = 1 - ray.v; + } + } + @PluginApi public OctreeImplementation getImplementation() { return implementation; From 20568bbfb1f2350e0951a4e6bfb4f3428af41248 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 Mar 2023 21:22:10 -0700 Subject: [PATCH 3/4] Use default implementation for AABB in textured block. --- .../chunky/model/TexturedBlockModel.java | 47 ++++++------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java b/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java index 28e6702712..53ee2a550b 100644 --- a/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java +++ b/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java @@ -16,20 +16,20 @@ */ package se.llbit.chunky.model; -import se.llbit.chunky.renderer.scene.Scene; import se.llbit.chunky.resources.Texture; import se.llbit.log.Log; import se.llbit.math.AABB; -import se.llbit.math.Ray; import se.llbit.util.annotation.NotNull; import se.llbit.util.annotation.Nullable; /** - * A textured block. - * - * @author Jesper Öqvist + * A textured block. This can either be statically textured or be oriented. */ public class TexturedBlockModel extends AABBModel { + /** + * A block orientation. Orientations with the {@code SIDE_} prefix do not rotate + * the top and bottom faces of the block. + */ public enum Orientation { NORTH, SOUTH, EAST, WEST, SIDE_NORTH, SIDE_SOUTH, SIDE_EAST, SIDE_WEST, @@ -51,10 +51,10 @@ public static Orientation fromFacing(String facing, boolean side) { } } - protected static final AABB box = new AABB(0, 1, 0, 1, 0, 1); - protected final Texture[] textures; - protected final UVMapping[] mappings; - protected final Tint[] tints; + protected static final AABB[] boxes = { new AABB(0, 1, 0, 1, 0, 1) }; + protected final Texture[][] textures; + protected final UVMapping[][] mappings; + protected final Tint[][] tints; public TexturedBlockModel(Texture north, Texture east, Texture south, Texture west, Texture top, Texture bottom) { this(new Texture[] { north, east, south, west, top, bottom }, null, null); @@ -70,14 +70,9 @@ public TexturedBlockModel(Orientation orientation, Texture north, Texture east, } public TexturedBlockModel(@NotNull Texture[] textures, @Nullable UVMapping[] mappings, @Nullable Tint[] tints) { - this.textures = textures; - this.textures2 = new Texture[][] { textures }; - - this.mappings = mappings; - this.mappings2 = mappings == null ? null : new UVMapping[][] { mappings }; - - this.tints = tints; - this.tints2 = tints == null ? null : new Tint[][] { tints }; + this.textures = new Texture[][] { textures }; + this.mappings = mappings == null ? null : new UVMapping[][] { mappings }; + this.tints = tints == null ? null : new Tint[][] { tints }; } private static Texture[] mapTextures(Orientation orientation, Texture north, Texture east, Texture south, @@ -120,18 +115,6 @@ private static UVMapping[] mapUV(Orientation orientation) { } } - @Override - public boolean intersect(Ray ray, Scene scene) { - // TODO: Optimize - return super.intersect(ray, scene); - } - - // This stuff is just to adhere to the interface. - private static final AABB[] boxes = { box }; - private final Texture[][] textures2; - private final UVMapping[][] mappings2; - private final Tint[][] tints2; - @Override public final AABB[] getBoxes() { return boxes; @@ -139,16 +122,16 @@ public final AABB[] getBoxes() { @Override public final Texture[][] getTextures() { - return textures2; + return textures; } @Override public final UVMapping[][] getUVMapping() { - return mappings2; + return mappings; } @Override public final Tint[][] getTints() { - return tints2; + return tints; } } From 70e91464c3eb16ac13585f884a844128c537875e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 Mar 2023 21:49:58 -0700 Subject: [PATCH 4/4] Remove top bottom oriented texture block model. --- .../llbit/chunky/model/DecoratedPotModel.java | 9 +- ...dTopBottomRotatableTexturedBlockModel.java | 72 ----------- .../llbit/chunky/model/OrientedQuadModel.java | 121 ++++++++++++++++++ .../java/se/llbit/chunky/model/QuadModel.java | 38 ------ .../chunky/model/TexturedBlockModel.java | 30 +++-- .../TopBottomOrientedTexturedBlockModel.java | 58 --------- 6 files changed, 143 insertions(+), 185 deletions(-) delete mode 100644 chunky/src/java/se/llbit/chunky/model/FixedTopBottomRotatableTexturedBlockModel.java create mode 100644 chunky/src/java/se/llbit/chunky/model/OrientedQuadModel.java delete mode 100644 chunky/src/java/se/llbit/chunky/model/TopBottomOrientedTexturedBlockModel.java diff --git a/chunky/src/java/se/llbit/chunky/model/DecoratedPotModel.java b/chunky/src/java/se/llbit/chunky/model/DecoratedPotModel.java index f37499943b..310b0b4138 100644 --- a/chunky/src/java/se/llbit/chunky/model/DecoratedPotModel.java +++ b/chunky/src/java/se/llbit/chunky/model/DecoratedPotModel.java @@ -1,6 +1,5 @@ package se.llbit.chunky.model; -import se.llbit.chunky.entity.SporeBlossom; import se.llbit.chunky.resources.Texture; import se.llbit.log.Log; import se.llbit.math.Quad; @@ -18,7 +17,7 @@ import java.util.Collection; import java.util.LinkedList; -public class DecoratedPotModel extends TopBottomOrientedTexturedBlockModel { +public class DecoratedPotModel extends OrientedQuadModel { private static final Vector4 BASE_UV_MAP = new Vector4(0 / 32., 14 / 32., 8 / 32., 22 / 32.); private static final Vector4 SIDE_UV_MAP = new Vector4(1 / 16., 15 / 16., 0., 1.); @@ -103,7 +102,7 @@ public Collection primitives(Vector3 offset) { Collection primitives = new LinkedList<>(); Transform transform = Transform.NONE .translate(position.x + offset.x, position.y + offset.y, position.z + offset.z); - for (Quad quad : rotateToFacing(facing, QUADS)) { + for (Quad quad : rotateToFacing(TexturedBlockModel.Orientation.fromFacing(facing, false), QUADS)) { quad.addTriangles(primitives, material, transform); } return primitives; @@ -127,7 +126,7 @@ public JsonValue toJson() { } public DecoratedPotModel(String facing, String[] shards) { - super(facing, DEFAULT_QUADS, new Texture[]{ + super(TexturedBlockModel.Orientation.fromFacing(facing, false), DEFAULT_QUADS, new Texture[]{ // shards[0] top crafting slot -> north getTextureForShard(shards[0]), // shards[3] bottom crafting slot -> south @@ -138,7 +137,7 @@ public DecoratedPotModel(String facing, String[] shards) { getTextureForShard(shards[2]), Texture.decoratedPotBase, // top Texture.decoratedPotBase // bottom - }); + }, null); } private static Texture getTextureForShard(String shard) { diff --git a/chunky/src/java/se/llbit/chunky/model/FixedTopBottomRotatableTexturedBlockModel.java b/chunky/src/java/se/llbit/chunky/model/FixedTopBottomRotatableTexturedBlockModel.java deleted file mode 100644 index 2f5509ad0a..0000000000 --- a/chunky/src/java/se/llbit/chunky/model/FixedTopBottomRotatableTexturedBlockModel.java +++ /dev/null @@ -1,72 +0,0 @@ -package se.llbit.chunky.model; - -import se.llbit.chunky.resources.Texture; -import se.llbit.math.Quad; - -/** - * A quad-based BlockModel which can be rotated horizontally (around y-axis) using a given facing. - * Unlike TopBottomOrientedTexturedBlockModel, this ONLY rotates the sides - the top and bottom are not rotated. - * Defaults to a full block, but can be customized by providing own quads. - */ -public class FixedTopBottomRotatableTexturedBlockModel extends QuadModel { - - protected final Quad[] quads; - protected final Texture[] textures; - - /** - * @param facing accepted values: "north", "south", "west", "east" - * @param quads structured like: [north, south, west, east, top, bottom] - * @param textures structured like: [north, south, west, east, top, bottom] - */ - public FixedTopBottomRotatableTexturedBlockModel( - String facing, - Quad[] quads, - Texture[] textures - ) { - this.quads = rotateToFacing(facing, quads); - this.textures = textures; - } - - public FixedTopBottomRotatableTexturedBlockModel(String facing, - Texture north, Texture east, Texture south, Texture west, Texture top, Texture bottom) { - this(facing, FULL_BLOCK_QUADS, new Texture[]{north, south, west, east, top, bottom}); - } - - public static Quad[] rotateToFacing(String facing, Quad[] quads) { - // if either top or bottom is missing, ignore - Quad top = quads.length > 4 ? quads[4] : null; - Quad bottom = quads.length > 5 ? quads[5] : null; - switch (facing) { - case "north": - break; - case "south": - quads = Model.rotateY(Model.rotateY(quads)); - break; - case "east": - quads = Model.rotateY(quads, -Math.toRadians(90)); - break; - case "west": - quads = Model.rotateNegY(quads); - break; - default: - throw new IllegalArgumentException("Invalid facing: " + facing); - } - if(top != null) { - quads[4] = top; - } - if(bottom != null) { - quads[5] = bottom; - } - return quads; - } - - @Override - public Quad[] getQuads() { - return quads; - } - - @Override - public Texture[] getTextures() { - return textures; - } -} diff --git a/chunky/src/java/se/llbit/chunky/model/OrientedQuadModel.java b/chunky/src/java/se/llbit/chunky/model/OrientedQuadModel.java new file mode 100644 index 0000000000..ac70f29820 --- /dev/null +++ b/chunky/src/java/se/llbit/chunky/model/OrientedQuadModel.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Chunky contributors + * + * This file is part of Chunky. + * + * Chunky is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chunky is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with Chunky. If not, see . + */ + +package se.llbit.chunky.model; + +import se.llbit.chunky.resources.Texture; +import se.llbit.log.Log; +import se.llbit.math.Quad; +import se.llbit.math.Vector3; +import se.llbit.math.Vector4; +import se.llbit.util.annotation.Nullable; + +import java.util.Arrays; + +public class OrientedQuadModel extends QuadModel { + + public static final Quad FULL_BLOCK_NORTH_SIDE = new Quad( + new Vector3(1, 0, 0), + new Vector3(0, 0, 0), + new Vector3(1, 1, 0), + new Vector4(0, 1, 0, 1)); + public static final Quad FULL_BLOCK_SOUTH_SIDE = new Quad( + new Vector3(0, 0, 1), + new Vector3(1, 0, 1), + new Vector3(0, 1, 1), + new Vector4(0, 1, 0, 1)); + public static final Quad FULL_BLOCK_WEST_SIDE = new Quad( + new Vector3(0, 0, 0), + new Vector3(0, 0, 1), + new Vector3(0, 1, 0), + new Vector4(0, 1, 0, 1)); + public static final Quad FULL_BLOCK_EAST_SIDE = new Quad( + new Vector3(1, 0, 1), + new Vector3(1, 0, 0), + new Vector3(1, 1, 1), + new Vector4(0, 1, 0, 1)); + public static final Quad FULL_BLOCK_TOP_SIDE = new Quad( + new Vector3(1, 1, 0), + new Vector3(0, 1, 0), + new Vector3(1, 1, 1), + new Vector4(1, 0, 1, 0)); + public static final Quad FULL_BLOCK_BOTTOM_SIDE = new Quad( + new Vector3(0, 0, 0), + new Vector3(1, 0, 0), + new Vector3(0, 0, 1), + new Vector4(0, 1, 0, 1)); + + public static final Quad[] FULL_BLOCK_QUADS = { + FULL_BLOCK_NORTH_SIDE, FULL_BLOCK_SOUTH_SIDE, + FULL_BLOCK_WEST_SIDE, FULL_BLOCK_EAST_SIDE, + FULL_BLOCK_TOP_SIDE, FULL_BLOCK_BOTTOM_SIDE + }; + + public static Quad[] rotateToFacing(TexturedBlockModel.Orientation orientation, Quad[] quads) { + switch (orientation.reduce()) { + default: + Log.warn("Unknown orientation: " + orientation); + case NORTH: + return quads; + case SOUTH: + return Model.rotateY(Model.rotateY(quads)); + case EAST: + return Model.rotateY(quads, -Math.toRadians(90)); + case WEST: + return Model.rotateNegY(quads); + } + } + + protected final Quad[] quads; + protected final Texture[] textures; + protected final Tint[] tints; + + public OrientedQuadModel(TexturedBlockModel.Orientation orientation, Quad[] quads, Texture[] textures, + @Nullable Tint[] tints) { + this.quads = rotateToFacing(orientation, quads); + this.textures = textures; + this.tints = tints; + } + + public OrientedQuadModel(TexturedBlockModel.Orientation orientation, Quad[] quads, Quad[] topBottomQuads, + Texture[] textures, @Nullable Tint[] tints) { + quads = rotateToFacing(orientation, quads); + topBottomQuads = orientation.side ? topBottomQuads : rotateToFacing(orientation, topBottomQuads); + + this.quads = new Quad[quads.length + topBottomQuads.length]; + System.arraycopy(quads, 0, this.quads, 0, quads.length); + System.arraycopy(topBottomQuads, 0, this.quads, quads.length, topBottomQuads.length); + this.textures = textures; + this.tints = tints; + } + + @Override + public Quad[] getQuads() { + return quads; + } + + @Override + public Texture[] getTextures() { + return new Texture[0]; + } + + @Override + public Tint[] getTints() { + return tints; + } +} diff --git a/chunky/src/java/se/llbit/chunky/model/QuadModel.java b/chunky/src/java/se/llbit/chunky/model/QuadModel.java index 87d2bd2fb2..189c2f68b6 100644 --- a/chunky/src/java/se/llbit/chunky/model/QuadModel.java +++ b/chunky/src/java/se/llbit/chunky/model/QuadModel.java @@ -15,44 +15,6 @@ */ @PluginApi public abstract class QuadModel implements BlockModel { - - public static final Quad FULL_BLOCK_NORTH_SIDE = new Quad( - new Vector3(1, 0, 0), - new Vector3(0, 0, 0), - new Vector3(1, 1, 0), - new Vector4(0, 1, 0, 1)); - public static final Quad FULL_BLOCK_SOUTH_SIDE = new Quad( - new Vector3(0, 0, 1), - new Vector3(1, 0, 1), - new Vector3(0, 1, 1), - new Vector4(0, 1, 0, 1)); - public static final Quad FULL_BLOCK_WEST_SIDE = new Quad( - new Vector3(0, 0, 0), - new Vector3(0, 0, 1), - new Vector3(0, 1, 0), - new Vector4(0, 1, 0, 1)); - public static final Quad FULL_BLOCK_EAST_SIDE = new Quad( - new Vector3(1, 0, 1), - new Vector3(1, 0, 0), - new Vector3(1, 1, 1), - new Vector4(0, 1, 0, 1)); - public static final Quad FULL_BLOCK_TOP_SIDE = new Quad( - new Vector3(1, 1, 0), - new Vector3(0, 1, 0), - new Vector3(1, 1, 1), - new Vector4(1, 0, 1, 0)); - public static final Quad FULL_BLOCK_BOTTOM_SIDE = new Quad( - new Vector3(0, 0, 0), - new Vector3(1, 0, 0), - new Vector3(0, 0, 1), - new Vector4(0, 1, 0, 1)); - - public static final Quad[] FULL_BLOCK_QUADS = { - FULL_BLOCK_NORTH_SIDE, FULL_BLOCK_SOUTH_SIDE, - FULL_BLOCK_WEST_SIDE, FULL_BLOCK_EAST_SIDE, - FULL_BLOCK_TOP_SIDE, FULL_BLOCK_BOTTOM_SIDE - }; - // Epsilons to clip ray intersections to the current block. protected static final double E0 = -Ray.EPSILON; protected static final double E1 = 1 + Ray.EPSILON; diff --git a/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java b/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java index 53ee2a550b..4f969af30a 100644 --- a/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java +++ b/chunky/src/java/se/llbit/chunky/model/TexturedBlockModel.java @@ -31,8 +31,8 @@ public class TexturedBlockModel extends AABBModel { * the top and bottom faces of the block. */ public enum Orientation { - NORTH, SOUTH, EAST, WEST, - SIDE_NORTH, SIDE_SOUTH, SIDE_EAST, SIDE_WEST, + NORTH(false, null), SOUTH(false, null), EAST(false, null), WEST(false, null), + SIDE_NORTH(true, NORTH), SIDE_SOUTH(true, SOUTH), SIDE_EAST(true, EAST), SIDE_WEST(true, WEST), ; public static Orientation fromFacing(String facing, boolean side) { @@ -49,6 +49,18 @@ public static Orientation fromFacing(String facing, boolean side) { throw new IllegalArgumentException("Invalid facing: " + facing); } } + + /** Only rotate the side faces */ + public final boolean side; + private final Orientation reduced; + Orientation(boolean side, @Nullable Orientation reduced) { + this.side = side; + this.reduced = reduced; + } + + public Orientation reduce() { + return reduced == null ? this : reduced; + } } protected static final AABB[] boxes = { new AABB(0, 1, 0, 1, 0, 1) }; @@ -77,25 +89,24 @@ public TexturedBlockModel(@NotNull Texture[] textures, @Nullable UVMapping[] map private static Texture[] mapTextures(Orientation orientation, Texture north, Texture east, Texture south, Texture west, Texture top, Texture bottom) { - switch (orientation) { + switch (orientation.reduce()) { default: Log.warn("Unknown orientation: " + orientation); case NORTH: - case SIDE_NORTH: return new Texture[] { north, east, south, west, top, bottom }; case SOUTH: - case SIDE_SOUTH: return new Texture[] { south, west, north, east, top, bottom }; case EAST: - case SIDE_EAST: return new Texture[] { west, north, east, south, top, bottom }; case WEST: - case SIDE_WEST: return new Texture[] { east, south, west, north, top, bottom }; } } private static UVMapping[] mapUV(Orientation orientation) { + if (orientation.side) { + return null; + } switch (orientation) { default: Log.warn("Unknown orientation: " + orientation); @@ -107,11 +118,6 @@ private static UVMapping[] mapUV(Orientation orientation) { return new UVMapping[] {null, null, null, null, UVMapping.ROTATE_90, UVMapping.ROTATE_90}; case WEST: return new UVMapping[] {null, null, null, null, UVMapping.ROTATE_270, UVMapping.ROTATE_270}; - case SIDE_EAST: - case SIDE_SOUTH: - case SIDE_NORTH: - case SIDE_WEST: - return null; } } diff --git a/chunky/src/java/se/llbit/chunky/model/TopBottomOrientedTexturedBlockModel.java b/chunky/src/java/se/llbit/chunky/model/TopBottomOrientedTexturedBlockModel.java deleted file mode 100644 index 1339e97fb1..0000000000 --- a/chunky/src/java/se/llbit/chunky/model/TopBottomOrientedTexturedBlockModel.java +++ /dev/null @@ -1,58 +0,0 @@ -package se.llbit.chunky.model; - -import se.llbit.chunky.resources.Texture; -import se.llbit.math.Quad; - -/** - * A quad-based BlockModel which can be rotated horizontally (around y-axis) using a given facing. - * Defaults to a full block, but can be customized by providing own quads. - */ -public class TopBottomOrientedTexturedBlockModel extends QuadModel { - - protected final Quad[] quads; - protected final Texture[] textures; - - /** - * @param facing accepted values: "north", "south", "west", "east" - * @param quads structured like: [north, south, west, east, top, bottom] - * @param textures structured like: [north, south, west, east, top, bottom] - */ - public TopBottomOrientedTexturedBlockModel( - String facing, - Quad[] quads, - Texture[] textures - ) { - this.quads = rotateToFacing(facing, quads); - this.textures = textures; - } - - public TopBottomOrientedTexturedBlockModel(String facing, - Texture north, Texture east, Texture south, Texture west, Texture top, Texture bottom) { - this(facing, FULL_BLOCK_QUADS, new Texture[]{north, south, west, east, top, bottom}); - } - - public static Quad[] rotateToFacing(String facing, Quad[] quads) { - switch (facing) { - case "north": - return quads; - case "south": - return Model.rotateY(Model.rotateY(quads)); - case "east": - return Model.rotateY(quads, -Math.toRadians(90)); - case "west": - return Model.rotateNegY(quads); - default: - throw new IllegalArgumentException("Invalid facing: " + facing); - } - } - - @Override - public Quad[] getQuads() { - return quads; - } - - @Override - public Texture[] getTextures() { - return textures; - } -}