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.CnFBlockTagsProvider;
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.renderer.FloralWorkbenchRenderer;
import fr.sushi.charmsnfabrics.client.renderer.FlowerCrownRenderer;
import fr.sushi.charmsnfabrics.common.CnFRegistries;
import net.minecraft.data.loot.LootTableProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.neoforged.api.distmarker.Dist;
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.event.lifecycle.FMLClientSetupEvent;
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.IConfigScreenFactory;
import net.neoforged.neoforge.data.event.GatherDataEvent;
@ -39,7 +42,8 @@ public class CnFClient
private static void onClientSetup(final FMLClientSetupEvent event)
{
ICurioRenderer.register(CnFRegistries.Items.FLOWER_CROWN.get(),
FlowerCrownRenderer::new);
() -> new FlowerCrownRenderer(
FlowerCrownRenderer.createModel()));
}
@SubscribeEvent
@ -70,4 +74,22 @@ public class CnFClient
LootContextParamSets.BLOCK)), lookup));
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 fr.sushi.charmsnfabrics.CharmsAndFabrics;
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.BlockElement;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.block.model.TextureSlots;
import net.minecraft.client.renderer.item.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
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.UnbakedElementsHelper;
import org.jetbrains.annotations.Nullable;
@ -29,70 +30,76 @@ import java.util.List;
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 =
() -> "FlowerCrownItemModel";
// private final FlowerCrownItemModel.Unbaked unbakedModel;
// private final ItemTransforms transforms;
private final ItemModel bakedBase;
private final BakingContext bakingContext;
private final ItemTransforms baseTransform;
private final Unbaked rawModel;
private final BakingContext context;
private ModelRenderProperties renderProperties;
public FlowerCrownItemModel(ItemModel bakedBase, BakingContext context)
public FlowerCrownItemModel(Unbaked rawModel, BakingContext context)
{
this.bakedBase = bakedBase;
this.bakingContext = context;
ResolvedModel baseItemModel = bakingContext.blockModelBaker().getModel(
ResourceLocation.withDefaultNamespace("item/generated"));
this.baseTransform = baseItemModel.getTopTransforms();
this.rawModel = rawModel;
this.context = context;
}
private ResourceLocation getFlowerLocation(int layer)
{
return ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID,
"flowers/flower" + layer);
"flower/flower" + layer);
}
/* Prevent Z fighting */
private Transformation getLayerTransform(int layer)
private static RenderTypeGroup getLayerRenderTypes(boolean unlit)
{
return new Transformation(new Vector3f(), new Quaternionf(),
new Vector3f(0.0f, 0.0f, 1.0f + (layer / 100f)),
new Quaternionf());
return new RenderTypeGroup(RenderType.translucent(), unlit ?
NeoForgeRenderTypes.ITEM_UNSORTED_UNLIT_TRANSLUCENT.get() :
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;
SpriteGetter sprites = this.bakingContext.blockModelBaker().sprites();
ModelBaker baker = this.context.blockModelBaker();
ResolvedModel resolvedModel = baker.getModel(this.rawModel.base);
TextureSlots slots = resolvedModel.getTopTextureSlots();
List<ItemModel> submodels = new ArrayList<>();
submodels.add(this.bakedBase);
List<BakedQuad> quads = resolvedModel
.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++)
{
ResourceLocation texture = this.getFlowerLocation(layer);
Material material = ClientHooks.getBlockMaterial(texture);
TextureAtlasSprite sprite = sprites.get(material, DEBUG_NAME);
private ItemModel bakeLayer(int layer)
{
SpriteGetter sprites = this.context.blockModelBaker().sprites();
Material material =
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 **** */
List<BlockElement> unbaked =
UnbakedElementsHelper.createUnbakedItemElements(0, sprite);
List<BakedQuad> quads =
UnbakedElementsHelper.bakeElements(unbaked, mat -> sprite,
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);
private CompositeModel composeModel()
{
List<ItemModel> models = new ArrayList<>();
models.add(this.bakeBase());
models.add(this.bakeLayer(0));
return new CompositeModel(models);
}
@Override
@ -101,18 +108,18 @@ public class FlowerCrownItemModel implements ItemModel
ItemDisplayContext displayContext, @Nullable ClientLevel level,
@Nullable LivingEntity entity, int seed)
{
ItemStackRenderState.LayerRenderState freshLayer =
renderState.newLayer();
this.composeModel()
.update(renderState, stack, itemModelResolver, displayContext,
level, entity, seed);
}
public record Unbaked(
ResourceLocation baseModel) implements ItemModel.Unbaked
public record Unbaked(ResourceLocation base) implements ItemModel.Unbaked
{
public static final MapCodec<FlowerCrownItemModel.Unbaked> MAP_CODEC =
RecordCodecBuilder.mapCodec(unbaked -> unbaked
.group(ResourceLocation.CODEC.fieldOf("base").forGetter(
FlowerCrownItemModel.Unbaked::baseModel))
FlowerCrownItemModel.Unbaked::base))
.apply(unbaked, FlowerCrownItemModel.Unbaked::new));
@Override
@ -124,32 +131,13 @@ public class FlowerCrownItemModel implements ItemModel
@Override
public ItemModel bake(BakingContext context)
{
ModelBaker modelbaker = context.blockModelBaker();
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);
return new FlowerCrownItemModel(this, context);
}
@Override
public void resolveDependencies(Resolver resolver)
{
resolver.markDependency(this.baseModel);
}
/* 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);
resolver.markDependency(this.base);
}
}
}

View file

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

View file

@ -2,8 +2,6 @@ package fr.sushi.charmsnfabrics.common.item;
import fr.sushi.charmsnfabrics.CharmsAndFabrics;
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.ItemStack;
import top.theillusivec4.curios.api.SlotContext;
@ -31,6 +29,28 @@ public class FlowerCrown extends Item implements ICurioItem
public ResourceLocation getModelTexture()
{
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"
}
]
}