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:
@@ -41,7 +41,6 @@ public class GeometryMapper {
|
|||||||
|
|
||||||
SimpleUnbakedGeometry geometry = (SimpleUnbakedGeometry) top;
|
SimpleUnbakedGeometry geometry = (SimpleUnbakedGeometry) top;
|
||||||
for (BlockElement element : geometry.elements()) {
|
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();
|
BedrockGeometry.Cube cube = mapBlockElement(element, textures).build();
|
||||||
bone.withCube(cube);
|
bone.withCube(cube);
|
||||||
min.min(cube.origin());
|
min.min(cube.origin());
|
||||||
@@ -57,9 +56,25 @@ public class GeometryMapper {
|
|||||||
return builder.withBone(bone).build();
|
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) {
|
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
|
// For some reason the X axis is inverted on bedrock (thanks Blockbench!!)
|
||||||
BedrockGeometry.Cube.Builder builder = BedrockGeometry.cube(element.from().sub(CENTRE_OFFSET, new Vector3f()), element.to().sub(element.from(), new Vector3f()));
|
|
||||||
|
// 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()) {
|
for (Map.Entry<Direction, BlockElementFace> faceEntry : element.faces().entrySet()) {
|
||||||
Direction direction = faceEntry.getKey();
|
Direction direction = faceEntry.getKey();
|
||||||
@@ -82,8 +97,8 @@ public class GeometryMapper {
|
|||||||
uvSize = new Vector2f(uvs.maxU() - uvs.minU(), uvs.maxV() - uvs.minV());
|
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,
|
// 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),
|
||||||
// and offset the UVs by the texture's starting UV
|
// adjust the values properly to the texture size, and offset the UVs by the texture's starting UV
|
||||||
textures.getSprite(face.texture()).ifPresent(sprite -> {
|
textures.getSprite(face.texture()).ifPresent(sprite -> {
|
||||||
float widthMultiplier = sprite.contents().width() / 16.0F;
|
float widthMultiplier = sprite.contents().width() / 16.0F;
|
||||||
float heightMultiplier = sprite.contents().height() / 16.0F;
|
float heightMultiplier = sprite.contents().height() / 16.0F;
|
||||||
@@ -97,12 +112,15 @@ public class GeometryMapper {
|
|||||||
BlockElementRotation rotation = element.rotation();
|
BlockElementRotation rotation = element.rotation();
|
||||||
if (rotation != null) {
|
if (rotation != null) {
|
||||||
// MC multiplies model origin by 0.0625 when loading rotation origin
|
// 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()) {
|
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 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);
|
builder.withRotation(bedrockRotation);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.geysermc.rainbow.pack.geometry;
|
|||||||
|
|
||||||
import com.mojang.math.Quadrant;
|
import com.mojang.math.Quadrant;
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import org.geysermc.rainbow.CodecUtil;
|
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.fieldOf("name").forGetter(Bone::name),
|
||||||
Codec.STRING.optionalFieldOf("parent").forGetter(Bone::parent),
|
Codec.STRING.optionalFieldOf("parent").forGetter(Bone::parent),
|
||||||
Codec.STRING.optionalFieldOf("binding").forGetter(Bone::binding),
|
Codec.STRING.optionalFieldOf("binding").forGetter(Bone::binding),
|
||||||
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("pivot", VECTOR3F_ZERO).forGetter(Bone::pivot),
|
defaultToZeroCodec("pivot").forGetter(Bone::pivot),
|
||||||
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("rotation", VECTOR3F_ZERO).forGetter(Bone::rotation),
|
defaultToZeroCodec("rotation").forGetter(Bone::rotation),
|
||||||
Codec.BOOL.optionalFieldOf("mirror", false).forGetter(Bone::mirror),
|
Codec.BOOL.optionalFieldOf("mirror", false).forGetter(Bone::mirror),
|
||||||
Codec.FLOAT.optionalFieldOf("inflate", 0.0F).forGetter(Bone::inflate),
|
Codec.FLOAT.optionalFieldOf("inflate", 0.0F).forGetter(Bone::inflate),
|
||||||
Cube.CODEC.listOf().optionalFieldOf("cubes", List.of()).forGetter(Bone::cubes)
|
Cube.CODEC.listOf().optionalFieldOf("cubes", List.of()).forGetter(Bone::cubes)
|
||||||
@@ -216,8 +217,8 @@ public record BedrockGeometry(BedrockVersion formatVersion, List<GeometryDefinit
|
|||||||
instance.group(
|
instance.group(
|
||||||
CodecUtil.VECTOR3F_CODEC.fieldOf("origin").forGetter(Cube::origin),
|
CodecUtil.VECTOR3F_CODEC.fieldOf("origin").forGetter(Cube::origin),
|
||||||
CodecUtil.VECTOR3F_CODEC.fieldOf("size").forGetter(Cube::size),
|
CodecUtil.VECTOR3F_CODEC.fieldOf("size").forGetter(Cube::size),
|
||||||
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("rotation", VECTOR3F_ZERO).forGetter(Cube::rotation),
|
defaultToZeroCodec("rotation").forGetter(Cube::rotation),
|
||||||
CodecUtil.VECTOR3F_CODEC.optionalFieldOf("pivot", VECTOR3F_ZERO).forGetter(Cube::pivot),
|
defaultToZeroCodec("pivot").forGetter(Cube::pivot),
|
||||||
Codec.FLOAT.optionalFieldOf("inflate", 0.0F).forGetter(Cube::inflate),
|
Codec.FLOAT.optionalFieldOf("inflate", 0.0F).forGetter(Cube::inflate),
|
||||||
Codec.BOOL.optionalFieldOf("mirror", false).forGetter(Cube::mirror),
|
Codec.BOOL.optionalFieldOf("mirror", false).forGetter(Cube::mirror),
|
||||||
FACE_MAP_CODEC.optionalFieldOf("uv", Map.of()).forGetter(Cube::faces)
|
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)
|
).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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user