diff --git a/src/main/java/org/geysermc/packgenerator/mapping/BedrockItemMapper.java b/src/main/java/org/geysermc/packgenerator/mapping/BedrockItemMapper.java index 000ce23..8c1ac37 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/BedrockItemMapper.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/BedrockItemMapper.java @@ -94,13 +94,13 @@ public class BedrockItemMapper { ResourceLocation texture = itemModelLocation; Material layer0Texture = itemModel.getTopTextureSlots().getMaterial("layer0"); - Optional customGeometry = Optional.empty(); + Optional customGeometry = Optional.empty(); if (layer0Texture != null) { texture = layer0Texture.texture(); } else { // Unknown texture (doesn't use layer0), so we immediately assume the geometry is custom // This check should probably be done differently - customGeometry = Optional.of((SimpleUnbakedGeometry) itemModel.getTopGeometry()); + customGeometry = Optional.of(itemModel); } context.create(bedrockIdentifier, texture, handheld, customGeometry); }, () -> context.reporter.report(() -> "missing block model " + itemModelLocation)); @@ -169,7 +169,7 @@ public class BedrockItemMapper { } public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld, - Optional customGeometry) { + Optional customModel) { GeyserSingleDefinition definition = new GeyserSingleDefinition(Optional.of(model), bedrockIdentifier, Optional.of(displayName), predicateStack, new GeyserSingleDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, protectionValue), componentPatch); try { @@ -180,14 +180,10 @@ public class BedrockItemMapper { } // TODO Should probably get a better way to get geometry texture - Optional bedrockGeometry = customGeometry.map(geometry -> GeometryMapper.mapGeometry(definition.textureName(), geometry)); - Optional geometryContext = bedrockGeometry.map(geometry -> new BedrockGeometryContext(geometry.definitions().getFirst(), texture)); - Optional animationContext = geometryContext.map( - geometry -> AnimationMapper.mapAnimation(definition.textureName(), geometry.geometry().bones().getFirst().name(), null)); - + Optional bedrockGeometry = customModel.map(model -> GeometryMapper.mapGeometry(definition.textureName(), model, texture)); itemConsumer.accept(new BedrockItem(bedrockIdentifier, definition.textureName(), texture, - AttachableMapper.mapItem(componentPatch, bedrockIdentifier, geometryContext, animationContext, additionalTextureConsumer), - bedrockGeometry, animationContext.map(BedrockAnimationContext::animation))); + AttachableMapper.mapItem(componentPatch, bedrockIdentifier, bedrockGeometry, additionalTextureConsumer), bedrockGeometry.map(BedrockGeometryContext::geometry), + bedrockGeometry.map(BedrockGeometryContext::animation).map(BedrockAnimationContext::animation))); } } } diff --git a/src/main/java/org/geysermc/packgenerator/mapping/animation/AnimationMapper.java b/src/main/java/org/geysermc/packgenerator/mapping/animation/AnimationMapper.java index f64066a..2dc184f 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/animation/AnimationMapper.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/animation/AnimationMapper.java @@ -1,17 +1,26 @@ package org.geysermc.packgenerator.mapping.animation; +import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.client.renderer.block.model.ItemTransforms; import org.geysermc.packgenerator.pack.animation.BedrockAnimation; import org.joml.Vector3f; +import org.joml.Vector3fc; public class AnimationMapper { + private static final Vector3fc POSITION_OFFSET = new Vector3f(0.0F, 13.0F, -3.0F); + private static final Vector3fc ROTATION_OFFSET = new Vector3f(90.0F, -90.0F, 0.0F); public static BedrockAnimationContext mapAnimation(String identifier, String bone, ItemTransforms transforms) { + ItemTransform thirdPerson = transforms.thirdPersonLeftHand(); + Vector3f thirdPersonPosition = POSITION_OFFSET.add(thirdPerson.translation(), new Vector3f()); + Vector3f thirdPersonRotation = ROTATION_OFFSET.add(-thirdPerson.rotation().x(), thirdPerson.rotation().y(), thirdPerson.rotation().z(), new Vector3f()); + Vector3f thirdPersonScale = new Vector3f(thirdPerson.scale()); + return new BedrockAnimationContext(BedrockAnimation.builder() .withAnimation(identifier + ".hold_first_person", BedrockAnimation.animation()) .withAnimation(identifier + ".hold_third_person", BedrockAnimation.animation() .withLoopMode(BedrockAnimation.LoopMode.LOOP) - .withBone(bone, new Vector3f(0.0F, 40.0F, 0.0F), new Vector3f(), new Vector3f())) + .withBone(bone, thirdPersonPosition, thirdPersonRotation, thirdPersonScale)) .build(), "animation." + identifier + ".hold_first_person", "animation." + identifier + ".hold_third_person"); } } diff --git a/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java b/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java index e93182d..c53a320 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/attachable/AttachableMapper.java @@ -9,7 +9,6 @@ import net.minecraft.core.component.DataComponents; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.equipment.Equippable; -import org.geysermc.packgenerator.mapping.animation.BedrockAnimationContext; import org.geysermc.packgenerator.mapping.geometry.BedrockGeometryContext; import org.geysermc.packgenerator.mixin.EntityRenderDispatcherAccessor; import org.geysermc.packgenerator.pack.attachable.BedrockAttachable; @@ -21,11 +20,11 @@ import java.util.function.Consumer; public class AttachableMapper { public static Optional mapItem(DataComponentPatch components, ResourceLocation bedrockIdentifier, Optional customGeometry, - Optional animations, Consumer textureConsumer) { + Consumer textureConsumer) { // Crazy optional statement // Unfortunately we can't have both equippables and custom models, so we prefer the latter :( return customGeometry - .map(geometry -> BedrockAttachable.geometry(bedrockIdentifier, geometry.geometry(), geometry.texture().getPath())) + .map(geometry -> BedrockAttachable.geometry(bedrockIdentifier, geometry.geometry().definitions().getFirst(), geometry.texture().getPath())) .or(() -> Optional.ofNullable(components.get(DataComponents.EQUIPPABLE)) .flatMap(optional -> (Optional) optional) .flatMap(equippable -> { @@ -42,7 +41,7 @@ public class AttachableMapper { return BedrockAttachable.equipment(bedrockIdentifier, assetInfo.getFirst(), texture.getPath()); })) .map(attachable -> { - animations.ifPresent(context -> { + customGeometry.map(BedrockGeometryContext::animation).ifPresent(context -> { attachable.withAnimation("first_person", context.firstPerson()); attachable.withAnimation("third_person", context.thirdPerson()); attachable.withScript("animate", "first_person", "context.is_first_person == 1.0"); diff --git a/src/main/java/org/geysermc/packgenerator/mapping/geometry/BedrockGeometryContext.java b/src/main/java/org/geysermc/packgenerator/mapping/geometry/BedrockGeometryContext.java index 121702b..9f106af 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/geometry/BedrockGeometryContext.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/geometry/BedrockGeometryContext.java @@ -1,6 +1,7 @@ package org.geysermc.packgenerator.mapping.geometry; import net.minecraft.resources.ResourceLocation; +import org.geysermc.packgenerator.mapping.animation.BedrockAnimationContext; import org.geysermc.packgenerator.pack.geometry.BedrockGeometry; -public record BedrockGeometryContext(BedrockGeometry.GeometryDefinition geometry, ResourceLocation texture) {} +public record BedrockGeometryContext(BedrockGeometry geometry, BedrockAnimationContext animation, ResourceLocation texture) {} diff --git a/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryMapper.java b/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryMapper.java index 70d7428..5f1b5ef 100644 --- a/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryMapper.java +++ b/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryMapper.java @@ -4,16 +4,21 @@ import net.minecraft.client.renderer.block.model.BlockElement; import net.minecraft.client.renderer.block.model.BlockElementFace; import net.minecraft.client.renderer.block.model.BlockElementRotation; import net.minecraft.client.renderer.block.model.SimpleUnbakedGeometry; +import net.minecraft.client.resources.model.ResolvedModel; import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import org.geysermc.packgenerator.mapping.animation.AnimationMapper; import org.geysermc.packgenerator.pack.geometry.BedrockGeometry; import org.joml.Vector2f; import org.joml.Vector3f; +import org.joml.Vector3fc; import java.util.Map; public class GeometryMapper { + private static final Vector3fc CENTRE_OFFSET = new Vector3f(8.0F, 0.0F, 8.0F); - public static BedrockGeometry mapGeometry(String identifier, SimpleUnbakedGeometry geometry) { + public static BedrockGeometryContext mapGeometry(String identifier, ResolvedModel model, ResourceLocation texture) { BedrockGeometry.Builder builder = BedrockGeometry.builder(identifier); // Blockbench seems to always use these values builder.withVisibleBoundsWidth(2.0F); @@ -25,21 +30,30 @@ public class GeometryMapper { builder.withTextureHeight(16); BedrockGeometry.Bone.Builder bone = BedrockGeometry.bone(identifier); - // Blockbench always moves the pivot by 8 in the X and Z direction, and then moves the cubes back - bone.withPivot(new Vector3f(8.0F, 0.0F, -8.0F)); + Vector3f min = new Vector3f(Float.MAX_VALUE); + Vector3f max = new Vector3f(Float.MIN_VALUE); + + SimpleUnbakedGeometry geometry = (SimpleUnbakedGeometry) model.getTopGeometry(); for (BlockElement element : geometry.elements()) { - bone.withCube(mapBlockElement(element)); + BedrockGeometry.Cube cube = mapBlockElement(element).build(); + bone.withCube(cube); + min.min(cube.origin()); + max.max(cube.origin().add(cube.size(), new Vector3f())); } + // Calculate the pivot to be at the centre of the bone + // This is important for animations later, display animations rotate around the centre on Java + bone.withPivot(min.add(max.sub(min).div(2.0F))); + // Bind to the bone of the current item slot bone.withBinding("q.item_slot_to_bone_name(context.item_slot)"); - return builder.withBone(bone).build(); + return new BedrockGeometryContext(builder.withBone(bone).build(), AnimationMapper.mapAnimation(identifier, identifier, model.getTopTransforms()), texture); } private static BedrockGeometry.Cube.Builder mapBlockElement(BlockElement element) { - // Move the cube back by 8 in the X and Z direction - BedrockGeometry.Cube.Builder builder = BedrockGeometry.cube(element.from().sub(8.0F, 0.0F, 8.0F, 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 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 (Map.Entry faceEntry : element.faces().entrySet()) { // TODO texture key @@ -68,7 +82,7 @@ public class GeometryMapper { BlockElementRotation rotation = element.rotation(); if (rotation != null) { - builder.withPivot(rotation.origin()); + builder.withPivot(rotation.origin().sub(CENTRE_OFFSET, new Vector3f())); Vector3f bedrockRotation = switch (rotation.axis()) { case X -> new Vector3f(rotation.angle(), 0.0F, 0.0F);