mirror of
https://github.com/GeyserMC/Rainbow.git
synced 2025-12-19 14:59:16 +00:00
Proper 3rd-person animation mapping
This commit is contained in:
@@ -94,13 +94,13 @@ public class BedrockItemMapper {
|
||||
|
||||
ResourceLocation texture = itemModelLocation;
|
||||
Material layer0Texture = itemModel.getTopTextureSlots().getMaterial("layer0");
|
||||
Optional<SimpleUnbakedGeometry> customGeometry = Optional.empty();
|
||||
Optional<ResolvedModel> 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<SimpleUnbakedGeometry> customGeometry) {
|
||||
Optional<ResolvedModel> 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> bedrockGeometry = customGeometry.map(geometry -> GeometryMapper.mapGeometry(definition.textureName(), geometry));
|
||||
Optional<BedrockGeometryContext> geometryContext = bedrockGeometry.map(geometry -> new BedrockGeometryContext(geometry.definitions().getFirst(), texture));
|
||||
Optional<BedrockAnimationContext> animationContext = geometryContext.map(
|
||||
geometry -> AnimationMapper.mapAnimation(definition.textureName(), geometry.geometry().bones().getFirst().name(), null));
|
||||
|
||||
Optional<BedrockGeometryContext> 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<BedrockAttachable> mapItem(DataComponentPatch components, ResourceLocation bedrockIdentifier, Optional<BedrockGeometryContext> customGeometry,
|
||||
Optional<BedrockAnimationContext> animations, Consumer<ResourceLocation> textureConsumer) {
|
||||
Consumer<ResourceLocation> 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<Equippable>) 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");
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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<Direction, BlockElementFace> 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);
|
||||
|
||||
Reference in New Issue
Block a user