1
0
mirror of https://github.com/GeyserMC/Rainbow.git synced 2025-12-19 14:59:16 +00:00

Improve 3D model conversion (close to perfection?)

This commit is contained in:
Eclipse
2025-10-18 17:23:19 +00:00
parent 95da47e824
commit 2724b4811f
2 changed files with 36 additions and 12 deletions

View File

@@ -41,7 +41,6 @@ public class GeometryMapper {
SimpleUnbakedGeometry geometry = (SimpleUnbakedGeometry) top;
for (BlockElement element : geometry.elements()) {
// TODO the origin here is wrong, some models seem to be mirrored weirdly in blockbench
BedrockGeometry.Cube cube = mapBlockElement(element, textures).build();
bone.withCube(cube);
min.min(cube.origin());
@@ -57,9 +56,25 @@ public class GeometryMapper {
return builder.withBone(bone).build();
}
// After hours of painfully suffering and 40 test builds of Rainbow, I finally got the right formula together and somehow made this mess of a code
// work properly, or at least, I think it is. I physically jumped in the air and cheered as I saw my models convert properly.
// Now, make sure you are ready to witness my deformed creation
private static BedrockGeometry.Cube.Builder mapBlockElement(BlockElement element, StitchedTextures textures) {
// The centre of the model is back by 8 in the X and Z direction on Java, so move the origin of the cube and the pivot like that
BedrockGeometry.Cube.Builder builder = BedrockGeometry.cube(element.from().sub(CENTRE_OFFSET, new Vector3f()), element.to().sub(element.from(), new Vector3f()));
// For some reason the X axis is inverted on bedrock (thanks Blockbench!!)
// The centre of the model is back by 8 in the X and Z direction on bedrock, so start by move the from and to points of the cube, and later the pivot, like that
Vector3f from = element.from().sub(CENTRE_OFFSET, new Vector3f());
Vector3f to = element.to().sub(CENTRE_OFFSET, new Vector3f());
// Bedrock wants the origin to be the smallest X/Y/Z of the cube, so we do a min here
Vector3f origin = from.min(to, new Vector3f());
// Bedrock also wants a cube size instead of a second cube, so calculate the max and subtract the min/origin
Vector3fc size = from.max(to, new Vector3f()).sub(origin);
// The X-axis is inverted for some reason on bedrock, so we have to do this (thanks Blockbench!!)
origin.x = -(origin.x + size.x());
BedrockGeometry.Cube.Builder builder = BedrockGeometry.cube(origin, size);
for (Map.Entry<Direction, BlockElementFace> faceEntry : element.faces().entrySet()) {
Direction direction = faceEntry.getKey();
@@ -82,8 +97,8 @@ public class GeometryMapper {
uvSize = new Vector2f(uvs.maxU() - uvs.minU(), uvs.maxV() - uvs.minV());
}
// If the texture was stitched (which it should have been, unless it doesn't exist), s UV values on Java are always in the [0;16] range, adjust the values properly to the texture size,
// and offset the UVs by the texture's starting UV
// UV values on Java are always in the [0;16] range, so if the texture was stitched (which it should have been, unless it doesn't exist),
// adjust the values properly to the texture size, and offset the UVs by the texture's starting UV
textures.getSprite(face.texture()).ifPresent(sprite -> {
float widthMultiplier = sprite.contents().width() / 16.0F;
float heightMultiplier = sprite.contents().height() / 16.0F;
@@ -97,12 +112,15 @@ public class GeometryMapper {
BlockElementRotation rotation = element.rotation();
if (rotation != null) {
// MC multiplies model origin by 0.0625 when loading rotation origin
builder.withPivot(rotation.origin().div(0.0625F, new Vector3f()).sub(CENTRE_OFFSET));
// Same as above for inverting the X axis (thanks again Blockbench!!)
builder.withPivot(rotation.origin().div(0.0625F, new Vector3f()).sub(CENTRE_OFFSET).mul(-1.0F, 1.0F, 1.0F));
// Same as above but for some reason the Z axis too (thanks again, so much, Blockbench!!!)
Vector3f bedrockRotation = switch (rotation.axis()) {
case X -> new Vector3f(rotation.angle(), 0.0F, 0.0F);
case X -> new Vector3f(-rotation.angle(), 0.0F, 0.0F);
case Y -> new Vector3f(0.0F, rotation.angle(), 0.0F);
case Z -> new Vector3f(0.0F, 0.0F, rotation.angle());
case Z -> new Vector3f(0.0F, 0.0F, -rotation.angle());
};
builder.withRotation(bedrockRotation);
}

View File

@@ -2,6 +2,7 @@ package org.geysermc.rainbow.pack.geometry;
import com.mojang.math.Quadrant;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Direction;
import org.geysermc.rainbow.CodecUtil;
@@ -141,8 +142,8 @@ public record BedrockGeometry(BedrockVersion formatVersion, List<GeometryDefinit
Codec.STRING.fieldOf("name").forGetter(Bone::name),
Codec.STRING.optionalFieldOf("parent").forGetter(Bone::parent),
Codec.STRING.optionalFieldOf("binding").forGetter(Bone::binding),
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("pivot", VECTOR3F_ZERO).forGetter(Bone::pivot),
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("rotation", VECTOR3F_ZERO).forGetter(Bone::rotation),
defaultToZeroCodec("pivot").forGetter(Bone::pivot),
defaultToZeroCodec("rotation").forGetter(Bone::rotation),
Codec.BOOL.optionalFieldOf("mirror", false).forGetter(Bone::mirror),
Codec.FLOAT.optionalFieldOf("inflate", 0.0F).forGetter(Bone::inflate),
Cube.CODEC.listOf().optionalFieldOf("cubes", List.of()).forGetter(Bone::cubes)
@@ -216,8 +217,8 @@ public record BedrockGeometry(BedrockVersion formatVersion, List<GeometryDefinit
instance.group(
CodecUtil.VECTOR3F_CODEC.fieldOf("origin").forGetter(Cube::origin),
CodecUtil.VECTOR3F_CODEC.fieldOf("size").forGetter(Cube::size),
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("rotation", VECTOR3F_ZERO).forGetter(Cube::rotation),
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("pivot", VECTOR3F_ZERO).forGetter(Cube::pivot),
defaultToZeroCodec("rotation").forGetter(Cube::rotation),
defaultToZeroCodec("pivot").forGetter(Cube::pivot),
Codec.FLOAT.optionalFieldOf("inflate", 0.0F).forGetter(Cube::inflate),
Codec.BOOL.optionalFieldOf("mirror", false).forGetter(Cube::mirror),
FACE_MAP_CODEC.optionalFieldOf("uv", Map.of()).forGetter(Cube::faces)
@@ -286,4 +287,9 @@ public record BedrockGeometry(BedrockVersion formatVersion, List<GeometryDefinit
).apply(instance, Face::new)
);
}
private static MapCodec<Vector3fc> defaultToZeroCodec(String name) {
return CodecUtil.VECTOR3F_CODEC.optionalFieldOf(name).xmap(optional -> optional.orElse(VECTOR3F_ZERO),
vector -> vector.length() == 0.0F ? Optional.empty() : Optional.of(vector));
}
}