diff --git a/common/src/main/java/org/figuramc/figura/mixin/compat/GeckolibGeoRendererMixin.java b/common/src/main/java/org/figuramc/figura/mixin/compat/GeckolibGeoRendererMixin.java index d12f6611d..0c2f278f6 100644 --- a/common/src/main/java/org/figuramc/figura/mixin/compat/GeckolibGeoRendererMixin.java +++ b/common/src/main/java/org/figuramc/figura/mixin/compat/GeckolibGeoRendererMixin.java @@ -8,8 +8,10 @@ import net.minecraft.world.item.Item; import org.figuramc.figura.avatar.Avatar; import org.figuramc.figura.ducks.GeckolibGeoArmorAccessor; +import org.figuramc.figura.lua.api.vanilla_model.VanillaPart; import org.figuramc.figura.model.ParentType; import org.figuramc.figura.permissions.Permissions; +import org.figuramc.figura.utils.RenderUtils; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -103,12 +105,17 @@ default void actuallyRender(PoseStack poseStack, T animatable, BakedGeoModel mod } } - // Returns the true if the pivot failed to render to match HumanoidArmorLayerMixin + // Returns true if the pivot failed to render, false if it was successful to match HumanoidArmorLayerMixin @Unique default boolean figura$renderPivot(GeoArmorRenderer armorRenderer, Avatar avatar, ParentType parentType, GeoAnimatable geoAnimatable, GeoBone geoBone, RenderType renderType, MultiBufferSource multiBufferSource, VertexConsumer vertexConsumer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { if (geoBone == null) return true; + // Returns successfully but skips rendering if the part is hidden + VanillaPart part = RenderUtils.pivotToPart(avatar, parentType); + if (avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && part != null && !part.checkVisible()) + return false; + return !avatar.pivotPartRender(parentType, stack -> { geoBone.setRotX(0); geoBone.setRotY(0); diff --git a/common/src/main/java/org/figuramc/figura/mixin/render/layers/HumanoidArmorLayerMixin.java b/common/src/main/java/org/figuramc/figura/mixin/render/layers/HumanoidArmorLayerMixin.java index 7a543182a..1b343d26e 100644 --- a/common/src/main/java/org/figuramc/figura/mixin/render/layers/HumanoidArmorLayerMixin.java +++ b/common/src/main/java/org/figuramc/figura/mixin/render/layers/HumanoidArmorLayerMixin.java @@ -109,8 +109,6 @@ public void postRenderArmorPiece(PoseStack poseStack, MultiBufferSource multiBuf @Unique private void figura$tryRenderArmorPart(EquipmentSlot slot, FiguraArmorPartRenderer renderer, PoseStack vanillaPoseStack, T entity, MultiBufferSource vertexConsumers, int light, ParentType... parentTypes) { if (slot == null) return; // ? - VanillaPart part = RenderUtils.partFromSlot(figura$avatar, slot); - if (figura$avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && part != null && !part.checkVisible()) return; ItemStack itemStack = entity.getItemBySlot(slot); @@ -134,12 +132,18 @@ public void postRenderArmorPiece(PoseStack poseStack, MultiBufferSource multiBuf armorModel.rightArm.z = 0.0f; boolean allFailed = true; + VanillaPart mainPart = RenderUtils.partFromSlot(figura$avatar, slot); + if (figura$avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && mainPart != null && !mainPart.checkVisible()) return; // Don't render armor if GeckoLib is already doing the rendering if (!GeckoLibCompat.armorHasCustomModel(itemStack)) { // Go through each parent type needed to render the current piece of armor for (ParentType parentType : parentTypes) { - // Try to render the pivot part + // Skip the part if it's hidden + VanillaPart part = RenderUtils.pivotToPart(figura$avatar, parentType); + if (figura$avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && part != null && !part.checkVisible()) continue; + + // Try to render the pivot part boolean renderedPivot = figura$avatar.pivotPartRender(parentType, stack -> { stack.pushPose(); figura$prepareArmorRender(stack); diff --git a/common/src/main/java/org/figuramc/figura/mixin/render/layers/elytra/ElytraLayerMixin.java b/common/src/main/java/org/figuramc/figura/mixin/render/layers/elytra/ElytraLayerMixin.java index d2c3ad9dd..2973ad446 100644 --- a/common/src/main/java/org/figuramc/figura/mixin/render/layers/elytra/ElytraLayerMixin.java +++ b/common/src/main/java/org/figuramc/figura/mixin/render/layers/elytra/ElytraLayerMixin.java @@ -25,6 +25,7 @@ import org.figuramc.figura.model.ParentType; import org.figuramc.figura.permissions.Permissions; import org.figuramc.figura.utils.PlatformUtils; +import org.figuramc.figura.utils.RenderUtils; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -81,7 +82,16 @@ public void onRender(PoseStack poseStack, MultiBufferSource multiBufferSource, i public void cancelVanillaPart(PoseStack poseStack, MultiBufferSource multiBufferSource, int light, T livingEntity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch, CallbackInfo ci) { if (vanillaPart != null) vanillaPart.restore(elytraModel); + renderedPivot = true; + renderElytraPivot(poseStack, multiBufferSource, light, livingEntity, limbAngle, limbDistance, tickDelta, animationProgress); + if (renderedPivot) { + poseStack.popPose(); + ci.cancel(); + } + } + + public void renderElytraPivot(PoseStack poseStack, MultiBufferSource multiBufferSource, int light, T livingEntity, float limbAngle, float limbDistance, float tickDelta, float animationProgress) { ItemStack itemStack = livingEntity.getItemBySlot(EquipmentSlot.CHEST); if (!itemStack.is(Items.ELYTRA) && !PlatformUtils.isModLoaded("origins")) { @@ -89,30 +99,44 @@ public void cancelVanillaPart(PoseStack poseStack, MultiBufferSource multiBuffer } if (figura$avatar != null && figura$avatar.luaRuntime != null && figura$avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && figura$avatar.luaRuntime.vanilla_model.ELYTRA.checkVisible()) { // Try to render the pivot part - renderedPivot = figura$avatar.pivotPartRender(ParentType.ElytraPivot, stack -> { - stack.pushPose(); - stack.scale(16, 16, 16); - stack.mulPose(Axis.XP.rotationDegrees(180f)); - stack.mulPose(Axis.YP.rotationDegrees(180f)); - stack.translate(0.0f, 0.0f, 0.125f); - this.elytraModel.setupAnim(livingEntity, limbAngle, limbDistance, tickDelta, light, animationProgress); - ResourceLocation resourceLocation = livingEntity instanceof AbstractClientPlayer abstractClientPlayer ? (abstractClientPlayer.isElytraLoaded() && abstractClientPlayer.getElytraTextureLocation() != null ? abstractClientPlayer.getElytraTextureLocation() : (abstractClientPlayer.isCapeLoaded() && abstractClientPlayer.getCloakTextureLocation() != null && abstractClientPlayer.isModelPartShown(PlayerModelPart.CAPE) ? abstractClientPlayer.getCloakTextureLocation() : WINGS_LOCATION)) : WINGS_LOCATION; - VertexConsumer vertexConsumer = ItemRenderer.getArmorFoilBuffer(multiBufferSource, RenderType.armorCutoutNoCull(resourceLocation), false, itemStack.hasFoil()); - this.elytraModel.renderToBuffer(stack, vertexConsumer, light, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f); - stack.popPose(); - }); - } else if (figura$avatar != null && figura$avatar.luaRuntime != null && figura$avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && !figura$avatar.luaRuntime.vanilla_model.ELYTRA.checkVisible()){ - renderedPivot = true; - poseStack.popPose(); - ci.cancel(); - return; - } else { - renderedPivot = false; - } + this.elytraModel.setupAnim(livingEntity, limbAngle, limbDistance, tickDelta, light, animationProgress); - if (renderedPivot) { - poseStack.popPose(); - ci.cancel(); - } + VanillaPart part = RenderUtils.pivotToPart(figura$avatar, ParentType.LeftElytraPivot); + + ResourceLocation resourceLocation = livingEntity instanceof AbstractClientPlayer abstractClientPlayer ? (abstractClientPlayer.isElytraLoaded() && abstractClientPlayer.getElytraTextureLocation() != null ? abstractClientPlayer.getElytraTextureLocation() : (abstractClientPlayer.isCapeLoaded() && abstractClientPlayer.getCloakTextureLocation() != null && abstractClientPlayer.isModelPartShown(PlayerModelPart.CAPE) ? abstractClientPlayer.getCloakTextureLocation() : WINGS_LOCATION)) : WINGS_LOCATION; + VertexConsumer vertexConsumer = ItemRenderer.getArmorFoilBuffer(multiBufferSource, RenderType.armorCutoutNoCull(resourceLocation), false, itemStack.hasFoil()); + + if (part != null && part.checkVisible()) { + boolean leftWing = figura$avatar.pivotPartRender(ParentType.LeftElytraPivot, stack -> { + stack.pushPose(); + stack.scale(16, 16, 16); + stack.mulPose(Axis.XP.rotationDegrees(180f)); + stack.mulPose(Axis.YP.rotationDegrees(180f)); + stack.translate(0.0f, 0.0f, 0.125f); + + ((ElytraModelAccessor)this.elytraModel).getLeftWing().render(stack, vertexConsumer, light, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f); + stack.popPose(); + }); + if (!leftWing) { + ((ElytraModelAccessor)this.elytraModel).getLeftWing().render(poseStack, vertexConsumer, light, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f); + } + } + part = RenderUtils.pivotToPart(figura$avatar, ParentType.RightElytraPivot); + if (part != null && part.checkVisible()) { + boolean rightWing = figura$avatar.pivotPartRender(ParentType.RightElytraPivot, stack -> { + stack.pushPose(); + stack.scale(16, 16, 16); + stack.mulPose(Axis.XP.rotationDegrees(180f)); + stack.mulPose(Axis.YP.rotationDegrees(180f)); + stack.translate(0.0f, 0.0f, 0.125f); + + ((ElytraModelAccessor)this.elytraModel).getRightWing().render(stack, vertexConsumer, light, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f); + stack.popPose(); + }); + if (!rightWing) { + ((ElytraModelAccessor)this.elytraModel).getRightWing().render(poseStack, vertexConsumer, light, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f); + } + } + } else renderedPivot = figura$avatar != null && figura$avatar.luaRuntime != null && figura$avatar.permissions.get(Permissions.VANILLA_MODEL_EDIT) == 1 && !figura$avatar.luaRuntime.vanilla_model.ELYTRA.checkVisible(); } } diff --git a/common/src/main/java/org/figuramc/figura/model/ParentType.java b/common/src/main/java/org/figuramc/figura/model/ParentType.java index 291a02744..24f8daa04 100644 --- a/common/src/main/java/org/figuramc/figura/model/ParentType.java +++ b/common/src/main/java/org/figuramc/figura/model/ParentType.java @@ -45,11 +45,12 @@ public enum ParentType { // Armor HelmetItemPivot(false, true,"HELMET_ITEM_PIVOT"), HelmetPivot(false, true, "HELMET_PIVOT", "HelmetPivot"), - ChestplatePivot(false, true, "CHESTPLATE_PIVOT", "ChestplatePivot"), - ElytraPivot(false, true, "ELYTRA_PIVOT", "ElytraPivot"), + ChestplatePivot(false, true, "CHESTPLATE_PIVOT", "ChestplatePivot", "ChestplateBodyPivot", "CHESTPLATE_BODY_PIVOT"), + LeftElytraPivot(false, true, "LEFT_ELYTRA_PIVOT", "LeftElytraPivot", "LeftWingPivot", "LEFT_WING_PIVOT"), + RightElytraPivot(false, true, "RIGHT_ELYTRA_PIVOT", "RightElytraPivot", "RightWingPivot", "RIGHT_WING_PIVOT"), LeftShoulderPivot(false, true, "LEFT_SHOULDER_PIVOT", "LeftShoulderPivot"), RightShoulderPivot(false, true, "RIGHT_SHOULDER_PIVOT", "RightShoulderPivot"), - LeggingsPivot(false, true, "LEGGINGS_PIVOT", "LeggingsPivot"), + LeggingsPivot(false, true, "LEGGINGS_PIVOT", "LeggingsPivot", "BeltPivot", "BELT_PIVOT"), LeftBootPivot(false, true, "LEFT_BOOT_PIVOT", "LeftBootPivot"), RightBootPivot(false, true, "RIGHT_BOOT_PIVOT", "RightBootPivot"); diff --git a/common/src/main/java/org/figuramc/figura/utils/RenderUtils.java b/common/src/main/java/org/figuramc/figura/utils/RenderUtils.java index 33e307b67..e06514c23 100644 --- a/common/src/main/java/org/figuramc/figura/utils/RenderUtils.java +++ b/common/src/main/java/org/figuramc/figura/utils/RenderUtils.java @@ -64,12 +64,32 @@ public static VanillaPart partFromSlot(Avatar avatar, EquipmentSlot equipmentSlo }; } + public static VanillaPart pivotToPart(Avatar avatar, ParentType type) { + if (!RenderUtils.vanillaModelAndScript(avatar)) + return null; + + return switch (type) { + case HelmetPivot -> avatar.luaRuntime.vanilla_model.HELMET; + case ChestplatePivot -> avatar.luaRuntime.vanilla_model.CHESTPLATE; + case LeftShoulderPivot -> avatar.luaRuntime.vanilla_model.CHESTPLATE_LEFT_ARM; + case RightShoulderPivot -> avatar.luaRuntime.vanilla_model.CHESTPLATE_RIGHT_ARM; + case LeggingsPivot -> avatar.luaRuntime.vanilla_model.LEGGINGS; + case LeftLeggingPivot -> avatar.luaRuntime.vanilla_model.LEGGINGS_LEFT_LEG; + case RightLeggingPivot -> avatar.luaRuntime.vanilla_model.LEGGINGS_RIGHT_LEG; + case LeftBootPivot -> avatar.luaRuntime.vanilla_model.BOOTS_LEFT_LEG; + case RightBootPivot -> avatar.luaRuntime.vanilla_model.BOOTS_RIGHT_LEG; + case LeftElytraPivot -> avatar.luaRuntime.vanilla_model.LEFT_ELYTRA; + case RightElytraPivot -> avatar.luaRuntime.vanilla_model.RIGHT_ELYTRA; + default -> null; + }; + } + public static EquipmentSlot slotFromPart(ParentType type) { switch (type){ case Head, HelmetItemPivot, HelmetPivot, Skull -> { return EquipmentSlot.HEAD; } - case Body, ChestplatePivot, LeftShoulderPivot, RightShoulderPivot, LeftElytra, RightElytra, ElytraPivot -> { + case Body, ChestplatePivot, LeftShoulderPivot, RightShoulderPivot, LeftElytra, RightElytra, RightElytraPivot, LeftElytraPivot -> { return EquipmentSlot.CHEST; } case LeftArm, LeftItemPivot, LeftSpyglassPivot -> { diff --git a/common/src/main/java/org/figuramc/figura/wizards/AvatarWizard.java b/common/src/main/java/org/figuramc/figura/wizards/AvatarWizard.java index 6eda19578..920c6d422 100644 --- a/common/src/main/java/org/figuramc/figura/wizards/AvatarWizard.java +++ b/common/src/main/java/org/figuramc/figura/wizards/AvatarWizard.java @@ -314,7 +314,8 @@ else if (hasCapeOrElytra) if (hasArmor) { model.addGroup(HelmetPivot, FiguraVec3.of(0, 24, 0), head); model.addGroup(ChestplatePivot, FiguraVec3.of(0, 24, 0), body); - model.addGroup(ElytraPivot, FiguraVec3.of(0, 24, 0), body); + model.addGroup(LeftElytra, FiguraVec3.of(0, 24, 0), body); + model.addGroup(RightElytra, FiguraVec3.of(0, 24, 0), body); model.addGroup(LeftShoulderPivot, FiguraVec3.of(-6, 24, 0), leftArm); model.addGroup(RightShoulderPivot, FiguraVec3.of(6, 24, 0), rightArm); model.addGroup(LeggingsPivot, FiguraVec3.of(0, 12, 0), body);