feat: dynamic flower crown model + other thingsss
Some checks are pending
Build / build (push) Waiting to run

This commit is contained in:
SushiCannibale 2025-08-11 23:57:31 +02:00
parent 1f633e88be
commit f73affdb2c
15 changed files with 128 additions and 81 deletions

View file

@ -4,11 +4,13 @@ import fr.sushi.charmsnfabrics.CharmsAndFabrics;
import fr.sushi.charmsnfabrics.client.datagen.CnFBlockLootProvider; import fr.sushi.charmsnfabrics.client.datagen.CnFBlockLootProvider;
import fr.sushi.charmsnfabrics.client.datagen.CnFBlockTagsProvider; import fr.sushi.charmsnfabrics.client.datagen.CnFBlockTagsProvider;
import fr.sushi.charmsnfabrics.client.datagen.CnFModelProvider; import fr.sushi.charmsnfabrics.client.datagen.CnFModelProvider;
import fr.sushi.charmsnfabrics.client.model.FlowerCrownItemModel;
import fr.sushi.charmsnfabrics.client.model.FlowerCrownModel; import fr.sushi.charmsnfabrics.client.model.FlowerCrownModel;
import fr.sushi.charmsnfabrics.client.renderer.FloralWorkbenchRenderer; import fr.sushi.charmsnfabrics.client.renderer.FloralWorkbenchRenderer;
import fr.sushi.charmsnfabrics.client.renderer.FlowerCrownRenderer; import fr.sushi.charmsnfabrics.client.renderer.FlowerCrownRenderer;
import fr.sushi.charmsnfabrics.common.CnFRegistries; import fr.sushi.charmsnfabrics.common.CnFRegistries;
import net.minecraft.data.loot.LootTableProvider; import net.minecraft.data.loot.LootTableProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.bus.api.SubscribeEvent;
@ -17,6 +19,7 @@ import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.Mod; import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent;
import net.neoforged.neoforge.client.event.RegisterItemModelsEvent;
import net.neoforged.neoforge.client.gui.ConfigurationScreen; import net.neoforged.neoforge.client.gui.ConfigurationScreen;
import net.neoforged.neoforge.client.gui.IConfigScreenFactory; import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.data.event.GatherDataEvent;
@ -39,7 +42,8 @@ public class CnFClient
private static void onClientSetup(final FMLClientSetupEvent event) private static void onClientSetup(final FMLClientSetupEvent event)
{ {
ICurioRenderer.register(CnFRegistries.Items.FLOWER_CROWN.get(), ICurioRenderer.register(CnFRegistries.Items.FLOWER_CROWN.get(),
FlowerCrownRenderer::new); () -> new FlowerCrownRenderer(
FlowerCrownRenderer.createModel()));
} }
@SubscribeEvent @SubscribeEvent
@ -70,4 +74,22 @@ public class CnFClient
LootContextParamSets.BLOCK)), lookup)); LootContextParamSets.BLOCK)), lookup));
event.createProvider(CnFBlockTagsProvider::new); event.createProvider(CnFBlockTagsProvider::new);
} }
@SubscribeEvent
private static void onRegisterItemModels(final RegisterItemModelsEvent event)
{
event.register(
ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID,
"flower_crown_loader"),
FlowerCrownItemModel.Unbaked.MAP_CODEC);
}
// @SubscribeEvent
// private static void onRegisterStandaloneModels(
// ModelEvent.RegisterStandalone event)
// {
// event.register(new StandaloneModelKey<>(
// ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID,
// "item/flower")), StandaloneModelBaker.quadCollection());
// }
} }

View file

@ -5,19 +5,20 @@ import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import fr.sushi.charmsnfabrics.CharmsAndFabrics; import fr.sushi.charmsnfabrics.CharmsAndFabrics;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.BlockElement; import net.minecraft.client.renderer.block.model.BlockElement;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.renderer.block.model.TextureSlots;
import net.minecraft.client.renderer.item.*; import net.minecraft.client.renderer.item.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*; import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.client.ClientHooks; import net.neoforged.neoforge.client.ClientHooks;
import net.neoforged.neoforge.client.NeoForgeRenderTypes;
import net.neoforged.neoforge.client.RenderTypeGroup;
import net.neoforged.neoforge.client.model.ComposedModelState; import net.neoforged.neoforge.client.model.ComposedModelState;
import net.neoforged.neoforge.client.model.UnbakedElementsHelper; import net.neoforged.neoforge.client.model.UnbakedElementsHelper;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -29,70 +30,76 @@ import java.util.List;
public class FlowerCrownItemModel implements ItemModel public class FlowerCrownItemModel implements ItemModel
{ {
/* TODO: Center flower0&1 textures and play with transform to get them at the right place for every layer */
private static final Transformation TRANSFORM =
new Transformation(new Vector3f(), new Quaternionf(),
new Vector3f(1.0f, 1.0f, 1.004f), new Quaternionf());
private static final ModelDebugName DEBUG_NAME = private static final ModelDebugName DEBUG_NAME =
() -> "FlowerCrownItemModel"; () -> "FlowerCrownItemModel";
// private final FlowerCrownItemModel.Unbaked unbakedModel; private final Unbaked rawModel;
// private final ItemTransforms transforms; private final BakingContext context;
private final ItemModel bakedBase; private ModelRenderProperties renderProperties;
private final BakingContext bakingContext;
private final ItemTransforms baseTransform;
public FlowerCrownItemModel(ItemModel bakedBase, BakingContext context) public FlowerCrownItemModel(Unbaked rawModel, BakingContext context)
{ {
this.bakedBase = bakedBase; this.rawModel = rawModel;
this.bakingContext = context; this.context = context;
ResolvedModel baseItemModel = bakingContext.blockModelBaker().getModel(
ResourceLocation.withDefaultNamespace("item/generated"));
this.baseTransform = baseItemModel.getTopTransforms();
} }
private ResourceLocation getFlowerLocation(int layer) private ResourceLocation getFlowerLocation(int layer)
{ {
return ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID, return ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID,
"flowers/flower" + layer); "flower/flower" + layer);
} }
/* Prevent Z fighting */ private static RenderTypeGroup getLayerRenderTypes(boolean unlit)
private Transformation getLayerTransform(int layer)
{ {
return new Transformation(new Vector3f(), new Quaternionf(), return new RenderTypeGroup(RenderType.translucent(), unlit ?
new Vector3f(0.0f, 0.0f, 1.0f + (layer / 100f)), NeoForgeRenderTypes.ITEM_UNSORTED_UNLIT_TRANSLUCENT.get() :
new Quaternionf()); NeoForgeRenderTypes.ITEM_UNSORTED_TRANSLUCENT.get());
} }
public ItemModel bakeForColors(List<DyeColor> colors) /**
* Bakes the base and updates the LayerRenderState quads
*/
private ItemModel bakeBase()
{ {
ModelState state = BlockModelRotation.X0_Y0; ModelBaker baker = this.context.blockModelBaker();
SpriteGetter sprites = this.bakingContext.blockModelBaker().sprites(); ResolvedModel resolvedModel = baker.getModel(this.rawModel.base);
TextureSlots slots = resolvedModel.getTopTextureSlots();
List<ItemModel> submodels = new ArrayList<>(); List<BakedQuad> quads = resolvedModel
submodels.add(this.bakedBase); .bakeTopGeometry(slots, baker, BlockModelRotation.X0_Y0)
.getAll();
this.renderProperties =
ModelRenderProperties.fromResolvedModel(baker, resolvedModel,
slots);
return new BlockModelWrapper(List.of(), quads, this.renderProperties);
}
for (int layer = 0; layer < colors.size(); layer++) private ItemModel bakeLayer(int layer)
{ {
ResourceLocation texture = this.getFlowerLocation(layer); SpriteGetter sprites = this.context.blockModelBaker().sprites();
Material material = ClientHooks.getBlockMaterial(texture); Material material =
TextureAtlasSprite sprite = sprites.get(material, DEBUG_NAME); ClientHooks.getBlockMaterial(this.getFlowerLocation(layer));
TextureAtlasSprite sprite = sprites.get(material, DEBUG_NAME);
/* Create all the bits that makes the model */
List<BlockElement> bits =
UnbakedElementsHelper.createUnbakedItemElements(0, sprite);
List<BakedQuad> quads =
UnbakedElementsHelper.bakeElements(bits, $ -> sprite,
new ComposedModelState(BlockModelRotation.X0_Y0,
TRANSFORM));
return new BlockModelWrapper(List.of(), quads, this.renderProperties);
}
/* ***** TODO: Multiply pixels with dye color **** */ private CompositeModel composeModel()
{
List<BlockElement> unbaked = List<ItemModel> models = new ArrayList<>();
UnbakedElementsHelper.createUnbakedItemElements(0, sprite); models.add(this.bakeBase());
List<BakedQuad> quads = models.add(this.bakeLayer(0));
UnbakedElementsHelper.bakeElements(unbaked, mat -> sprite, return new CompositeModel(models);
new ComposedModelState(state,
this.getLayerTransform(layer)));
ModelRenderProperties renderProperties =
new ModelRenderProperties(false, sprite,
this.baseTransform);
submodels.add(
new BlockModelWrapper(List.of(), quads, renderProperties));
}
/* ****************************************************************** */
return new CompositeModel(submodels);
} }
@Override @Override
@ -101,18 +108,18 @@ public class FlowerCrownItemModel implements ItemModel
ItemDisplayContext displayContext, @Nullable ClientLevel level, ItemDisplayContext displayContext, @Nullable ClientLevel level,
@Nullable LivingEntity entity, int seed) @Nullable LivingEntity entity, int seed)
{ {
ItemStackRenderState.LayerRenderState freshLayer = this.composeModel()
renderState.newLayer(); .update(renderState, stack, itemModelResolver, displayContext,
level, entity, seed);
} }
public record Unbaked( public record Unbaked(ResourceLocation base) implements ItemModel.Unbaked
ResourceLocation baseModel) implements ItemModel.Unbaked
{ {
public static final MapCodec<FlowerCrownItemModel.Unbaked> MAP_CODEC = public static final MapCodec<FlowerCrownItemModel.Unbaked> MAP_CODEC =
RecordCodecBuilder.mapCodec(unbaked -> unbaked RecordCodecBuilder.mapCodec(unbaked -> unbaked
.group(ResourceLocation.CODEC.fieldOf("base").forGetter( .group(ResourceLocation.CODEC.fieldOf("base").forGetter(
FlowerCrownItemModel.Unbaked::baseModel)) FlowerCrownItemModel.Unbaked::base))
.apply(unbaked, FlowerCrownItemModel.Unbaked::new)); .apply(unbaked, FlowerCrownItemModel.Unbaked::new));
@Override @Override
@ -124,32 +131,13 @@ public class FlowerCrownItemModel implements ItemModel
@Override @Override
public ItemModel bake(BakingContext context) public ItemModel bake(BakingContext context)
{ {
ModelBaker modelbaker = context.blockModelBaker(); return new FlowerCrownItemModel(this, context);
ResolvedModel resolved = modelbaker.getModel(this.baseModel);
List<BakedQuad> quads = resolved
.bakeTopGeometry(resolved.getTopTextureSlots(), modelbaker,
BlockModelRotation.X0_Y0).getAll();
ItemModel bakedBase = new BlockModelWrapper(List.of(), quads,
this.getRenderProperties(context));
return new FlowerCrownItemModel(bakedBase, context);
} }
@Override @Override
public void resolveDependencies(Resolver resolver) public void resolveDependencies(Resolver resolver)
{ {
resolver.markDependency(this.baseModel); resolver.markDependency(this.base);
}
/* A vérifier, pas sûr sûr de celle là Y-Y */
private ModelRenderProperties getRenderProperties(
ItemModel.BakingContext context)
{
ModelBaker modelbaker = context.blockModelBaker();
ResolvedModel resolvedmodel = modelbaker.getModel(this.baseModel);
TextureSlots textureslots = resolvedmodel.getTopTextureSlots();
return ModelRenderProperties.fromResolvedModel(modelbaker,
resolvedmodel, textureslots);
} }
} }
} }

View file

@ -4,15 +4,19 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import fr.sushi.charmsnfabrics.client.CnFLayers; import fr.sushi.charmsnfabrics.client.CnFLayers;
import fr.sushi.charmsnfabrics.client.model.FlowerCrownModel; import fr.sushi.charmsnfabrics.client.model.FlowerCrownModel;
import fr.sushi.charmsnfabrics.common.data.SavedColors;
import fr.sushi.charmsnfabrics.common.item.FlowerCrown; import fr.sushi.charmsnfabrics.common.item.FlowerCrown;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.EntityModel;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.renderer.entity.RenderLayerParent; import net.minecraft.client.renderer.entity.RenderLayerParent;
import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; import net.minecraft.client.renderer.entity.state.LivingEntityRenderState;
import net.minecraft.client.renderer.item.BlockModelWrapper;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@ -24,11 +28,15 @@ public class FlowerCrownRenderer implements ICurioRenderer
{ {
private final FlowerCrownModel model; private final FlowerCrownModel model;
public FlowerCrownRenderer() public FlowerCrownRenderer(FlowerCrownModel model)
{ {
this.model = new FlowerCrownModel( this.model = model;
Minecraft.getInstance().getEntityModels() }
.bakeLayer(CnFLayers.CROWN_LAYER));
public static FlowerCrownModel createModel()
{
EntityModelSet modelSet = Minecraft.getInstance().getEntityModels();
return new FlowerCrownModel(modelSet.bakeLayer(CnFLayers.CROWN_LAYER));
} }
@Override @Override

View file

@ -2,8 +2,6 @@ package fr.sushi.charmsnfabrics.common.item;
import fr.sushi.charmsnfabrics.CharmsAndFabrics; import fr.sushi.charmsnfabrics.CharmsAndFabrics;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import top.theillusivec4.curios.api.SlotContext; import top.theillusivec4.curios.api.SlotContext;
@ -31,6 +29,28 @@ public class FlowerCrown extends Item implements ICurioItem
public ResourceLocation getModelTexture() public ResourceLocation getModelTexture()
{ {
return ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID, return ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID,
"textures/models/accessory/flower_crown.png"); "textures/models/accessory/flower_crown.png");
} }
// @Override
// public InteractionResult useOn(UseOnContext context)
// {
// var player = context.getPlayer();
// var level = context.getLevel();
// var hand = context.getHand();
//
// ItemStack stack = player.getItemInHand(hand);
// SavedColors comp =
// stack.get(CnFRegistries.DataComponents.SAVED_FLOWERS.get());
//
// stack.set(CnFRegistries.DataComponents.SAVED_FLOWERS.get(),
// new SavedColors(List.of(DyeColor.PURPLE, DyeColor.LIME)));
// if (comp != null && !comp.flowers().isEmpty())
// {
// player.displayClientMessage(Component.literal(
// comp.flowers().stream().map(DyeColor::toString)
// .collect(Collectors.joining(" "))), false);
// }
// return InteractionResult.SUCCESS;
// }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 201 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

View file

@ -0,0 +1,9 @@
{
"sources": [
{
"type": "minecraft:directory",
"prefix": "flower/",
"source": "flower"
}
]
}