Skip to content

Commit 5ed7ec7

Browse files
authored
Add charged animation overlay to energetic wool items (#53)
1 parent 7fd5ebf commit 5ed7ec7

29 files changed

Lines changed: 597 additions & 49 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.cyclops.energeticsheep.client;
2+
3+
import net.minecraft.client.renderer.RenderPipelines;
4+
import net.minecraft.client.renderer.rendertype.RenderSetup;
5+
import net.minecraft.client.renderer.rendertype.RenderType;
6+
import net.minecraft.client.renderer.rendertype.TextureTransform;
7+
import net.minecraft.resources.Identifier;
8+
9+
/**
10+
* Custom render types for EnergeticSheep.
11+
*/
12+
public class EnergeticSheepRenderTypes {
13+
14+
/**
15+
* Creates an energy-swirl {@link RenderType} that additively blends the swirl colours
16+
* onto the destination but leaves the destination alpha channel untouched.
17+
*
18+
* @param texture the swirl texture
19+
* @param uOffset horizontal texture scroll offset
20+
* @param vOffset vertical texture scroll offset
21+
* @return the render type
22+
*/
23+
public static RenderType energySwirlPreserveAlpha(Identifier texture, float uOffset, float vOffset) {
24+
return RenderType.create(
25+
"energeticsheep_energy_swirl",
26+
RenderSetup.builder(RenderPipelines.ENERGY_SWIRL)
27+
.withTexture("Sampler0", texture)
28+
.setTextureTransform(new TextureTransform.OffsetTextureTransform(uOffset, vOffset))
29+
.useLightmap()
30+
.useOverlay()
31+
.sortOnUpload()
32+
.createRenderSetup()
33+
);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package org.cyclops.energeticsheep.client.render.blockentity;
2+
3+
import com.mojang.blaze3d.vertex.PoseStack;
4+
import com.mojang.serialization.MapCodec;
5+
import net.minecraft.client.Minecraft;
6+
import net.minecraft.client.model.Model;
7+
import net.minecraft.client.model.geom.ModelLayerLocation;
8+
import net.minecraft.client.model.geom.ModelPart;
9+
import net.minecraft.client.model.geom.PartPose;
10+
import net.minecraft.client.model.geom.builders.CubeDeformation;
11+
import net.minecraft.client.model.geom.builders.CubeListBuilder;
12+
import net.minecraft.client.model.geom.builders.LayerDefinition;
13+
import net.minecraft.client.model.geom.builders.MeshDefinition;
14+
import net.minecraft.client.model.geom.builders.PartDefinition;
15+
import net.minecraft.client.renderer.SubmitNodeCollector;
16+
import net.minecraft.client.renderer.rendertype.RenderType;
17+
import net.minecraft.client.renderer.rendertype.RenderTypes;
18+
import net.minecraft.client.renderer.special.NoDataSpecialModelRenderer;
19+
import net.minecraft.client.renderer.special.SpecialModelRenderer;
20+
import net.minecraft.client.renderer.texture.OverlayTexture;
21+
import net.minecraft.resources.Identifier;
22+
import net.minecraft.util.ARGB;
23+
import net.minecraft.world.level.Level;
24+
import org.cyclops.energeticsheep.Reference;
25+
import org.cyclops.energeticsheep.client.EnergeticSheepRenderTypes;
26+
27+
import java.util.function.Consumer;
28+
29+
/**
30+
* Special item model renderer that adds the charged energy-swirl overlay to energetic wool items.
31+
* @author rubensworks
32+
*/
33+
public class ItemEnergeticWoolChargeSpecialRenderer implements NoDataSpecialModelRenderer {
34+
35+
public static final ModelLayerLocation MODEL_LAYER = new ModelLayerLocation(
36+
Identifier.fromNamespaceAndPath(Reference.MOD_ID, "energetic_wool_charge"), "main");
37+
38+
private static final Identifier CHARGE_TEXTURE =
39+
Identifier.withDefaultNamespace("textures/entity/creeper/creeper_armor.png");
40+
41+
private final WoolChargeModel model;
42+
43+
public ItemEnergeticWoolChargeSpecialRenderer(ModelPart root) {
44+
this.model = new WoolChargeModel(root);
45+
}
46+
47+
public static LayerDefinition createLayer() {
48+
MeshDefinition mesh = new MeshDefinition();
49+
PartDefinition root = mesh.getRoot();
50+
root.addOrReplaceChild("cube",
51+
CubeListBuilder.create()
52+
.texOffs(0, 0)
53+
.addBox(0.0F, 0.0F, 0.0F, 16.0F, 16.0F, 16.0F, new CubeDeformation(0.5F)),
54+
PartPose.ZERO);
55+
return LayerDefinition.create(mesh, 64, 32);
56+
}
57+
58+
@Override
59+
public void getExtents(Consumer<org.joml.Vector3fc> consumer) {}
60+
61+
@Override
62+
public void submit(PoseStack poseStack, SubmitNodeCollector collector, int lightCoords, int overlay, boolean isGui, int seed) {
63+
Minecraft minecraft = Minecraft.getInstance();
64+
Level level = minecraft.level;
65+
float t = level != null
66+
? (float) level.getGameTime() + minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(true)
67+
: 0f;
68+
float xOffset = t * 0.01F;
69+
float yOffset = t * 0.01F;
70+
RenderType renderType = EnergeticSheepRenderTypes.energySwirlPreserveAlpha(CHARGE_TEXTURE, xOffset, yOffset);
71+
WoolChargeState state = new WoolChargeState();
72+
model.setupAnim(state);
73+
// Use submitCustomGeometry so the swirl is submitted to the translucent pass (hasBlending=true),
74+
// which executes AFTER the solid wool geometry. This ensures the swirl renders on top of the wool
75+
// rather than being overwritten by it.
76+
collector.submitCustomGeometry(poseStack, renderType, (capturedPose, buffer) -> {
77+
PoseStack ps = new PoseStack();
78+
ps.last().set(capturedPose);
79+
model.renderToBuffer(ps, buffer, lightCoords, OverlayTexture.NO_OVERLAY, ARGB.color(100, 255, 255, 255));
80+
});
81+
}
82+
83+
/**
84+
* Unbaked form used for codec-based registration and JSON item model wiring.
85+
*/
86+
public static class Unbaked implements NoDataSpecialModelRenderer.Unbaked {
87+
88+
public static final MapCodec<Unbaked> CODEC = MapCodec.unit(new Unbaked());
89+
90+
@Override
91+
public MapCodec<? extends NoDataSpecialModelRenderer.Unbaked> type() {
92+
return CODEC;
93+
}
94+
95+
@Override
96+
public NoDataSpecialModelRenderer bake(SpecialModelRenderer.BakingContext context) {
97+
return new ItemEnergeticWoolChargeSpecialRenderer(
98+
context.entityModelSet().bakeLayer(MODEL_LAYER));
99+
}
100+
101+
}
102+
103+
private static class WoolChargeState {}
104+
105+
private static class WoolChargeModel extends Model<WoolChargeState> {
106+
107+
public WoolChargeModel(ModelPart root) {
108+
super(root, RenderTypes::entitySolid);
109+
}
110+
111+
}
112+
113+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package org.cyclops.energeticsheep.client.render.item;
2+
3+
import com.mojang.serialization.MapCodec;
4+
import com.mojang.serialization.codecs.RecordCodecBuilder;
5+
import net.minecraft.client.multiplayer.ClientLevel;
6+
import net.minecraft.client.renderer.item.ItemModel;
7+
import net.minecraft.client.renderer.item.ItemModelResolver;
8+
import net.minecraft.client.renderer.item.ItemStackRenderState;
9+
import net.minecraft.client.renderer.item.ModelRenderProperties;
10+
import net.minecraft.client.renderer.special.NoDataSpecialModelRenderer;
11+
import net.minecraft.client.resources.model.ResolvableModel;
12+
import net.minecraft.resources.Identifier;
13+
import net.minecraft.world.entity.ItemOwner;
14+
import net.minecraft.world.item.ItemDisplayContext;
15+
import net.minecraft.world.item.ItemStack;
16+
import org.cyclops.energeticsheep.client.render.blockentity.ItemEnergeticWoolChargeSpecialRenderer;
17+
import org.joml.Matrix4fc;
18+
19+
/**
20+
* A custom {@link ItemModel} for the energetic wool charge overlay.
21+
* Unlike the vanilla {@code minecraft:special} type (which only marks the render state as animated for
22+
* enchanted items), this model always calls {@link ItemStackRenderState#setAnimated()} so that the
23+
* GUI re-submits the swirl geometry every frame and the animation never freezes.
24+
*
25+
* @author rubensworks
26+
*/
27+
public class AnimatedEnergeticWoolChargeItemModel implements ItemModel {
28+
29+
private final NoDataSpecialModelRenderer specialRenderer;
30+
private final ModelRenderProperties properties;
31+
private final Matrix4fc transformation;
32+
33+
public AnimatedEnergeticWoolChargeItemModel(
34+
NoDataSpecialModelRenderer specialRenderer,
35+
ModelRenderProperties properties,
36+
Matrix4fc transformation) {
37+
this.specialRenderer = specialRenderer;
38+
this.properties = properties;
39+
this.transformation = transformation;
40+
}
41+
42+
@Override
43+
public void update(
44+
ItemStackRenderState renderState,
45+
ItemStack stack,
46+
ItemModelResolver resolver,
47+
ItemDisplayContext context,
48+
ClientLevel level,
49+
ItemOwner owner,
50+
int seed) {
51+
renderState.setAnimated();
52+
renderState.appendModelIdentityElement(this);
53+
ItemStackRenderState.LayerRenderState layer = renderState.newLayer();
54+
layer.setExtents(ItemStackRenderState.LayerRenderState.NO_EXTENTS_SUPPLIER);
55+
layer.setLocalTransform(transformation);
56+
layer.setupSpecialModel(specialRenderer, null);
57+
properties.applyToLayer(layer, context);
58+
}
59+
60+
/**
61+
* Unbaked form used for codec-based registration and JSON item model wiring.
62+
*/
63+
public static class Unbaked implements ItemModel.Unbaked {
64+
65+
public static final MapCodec<Unbaked> CODEC = RecordCodecBuilder.mapCodec(
66+
instance -> instance.group(
67+
Identifier.CODEC.fieldOf("base").forGetter(u -> u.base))
68+
.apply(instance, Unbaked::new));
69+
70+
private final Identifier base;
71+
72+
public Unbaked(Identifier base) {
73+
this.base = base;
74+
}
75+
76+
@Override
77+
public MapCodec<? extends ItemModel.Unbaked> type() {
78+
return CODEC;
79+
}
80+
81+
@Override
82+
public void resolveDependencies(ResolvableModel.Resolver resolver) {
83+
resolver.markDependency(base);
84+
}
85+
86+
@Override
87+
public ItemModel bake(ItemModel.BakingContext context, Matrix4fc matrix) {
88+
NoDataSpecialModelRenderer renderer =
89+
new ItemEnergeticWoolChargeSpecialRenderer.Unbaked().bake(context);
90+
if (renderer == null) {
91+
return context.missingItemModel(matrix);
92+
}
93+
var baker = context.blockModelBaker();
94+
var resolvedModel = baker.getModel(base);
95+
ModelRenderProperties props = ModelRenderProperties.fromResolvedModel(
96+
baker, resolvedModel, resolvedModel.getTopTextureSlots());
97+
return new AnimatedEnergeticWoolChargeItemModel(renderer, props, matrix);
98+
}
99+
100+
}
101+
102+
}

loader-common/src/main/resources/META-INF/accesstransformer.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
public net.minecraft.world.entity.animal.sheep.Sheep eatBlockGoal
33

44
public-f net.minecraft.world.entity.Mob getLootTable()Ljava/util/Optional;
5+
6+
# Custom render pipeline (ENERGY_SWIRL_PRESERVE_ALPHA)
7+
public net.minecraft.client.renderer.RenderPipelines MATRICES_FOG_SNIPPET
8+
public net.minecraft.client.renderer.rendertype.RenderType create(Ljava/lang/String;Lnet/minecraft/client/renderer/rendertype/RenderSetup;)Lnet/minecraft/client/renderer/rendertype/RenderType;
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"model": {
3-
"type": "minecraft:model",
4-
"model": "energeticsheep:item/black_energetic_wool"
3+
"type": "minecraft:composite",
4+
"models": [
5+
{
6+
"type": "minecraft:model",
7+
"model": "energeticsheep:item/black_energetic_wool"
8+
},
9+
{
10+
"type": "energeticsheep:energetic_wool_charge_item",
11+
"base": "energeticsheep:item/black_energetic_wool"
12+
}
13+
]
514
}
6-
}
15+
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"model": {
3-
"type": "minecraft:model",
4-
"model": "energeticsheep:item/blue_energetic_wool"
3+
"type": "minecraft:composite",
4+
"models": [
5+
{
6+
"type": "minecraft:model",
7+
"model": "energeticsheep:item/blue_energetic_wool"
8+
},
9+
{
10+
"type": "energeticsheep:energetic_wool_charge_item",
11+
"base": "energeticsheep:item/blue_energetic_wool"
12+
}
13+
]
514
}
6-
}
15+
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"model": {
3-
"type": "minecraft:model",
4-
"model": "energeticsheep:item/brown_energetic_wool"
3+
"type": "minecraft:composite",
4+
"models": [
5+
{
6+
"type": "minecraft:model",
7+
"model": "energeticsheep:item/brown_energetic_wool"
8+
},
9+
{
10+
"type": "energeticsheep:energetic_wool_charge_item",
11+
"base": "energeticsheep:item/brown_energetic_wool"
12+
}
13+
]
514
}
6-
}
15+
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"model": {
3-
"type": "minecraft:model",
4-
"model": "energeticsheep:item/cyan_energetic_wool"
3+
"type": "minecraft:composite",
4+
"models": [
5+
{
6+
"type": "minecraft:model",
7+
"model": "energeticsheep:item/cyan_energetic_wool"
8+
},
9+
{
10+
"type": "energeticsheep:energetic_wool_charge_item",
11+
"base": "energeticsheep:item/cyan_energetic_wool"
12+
}
13+
]
514
}
6-
}
15+
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"model": {
3-
"type": "minecraft:model",
4-
"model": "energeticsheep:item/gray_energetic_wool"
3+
"type": "minecraft:composite",
4+
"models": [
5+
{
6+
"type": "minecraft:model",
7+
"model": "energeticsheep:item/gray_energetic_wool"
8+
},
9+
{
10+
"type": "energeticsheep:energetic_wool_charge_item",
11+
"base": "energeticsheep:item/gray_energetic_wool"
12+
}
13+
]
514
}
6-
}
15+
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"model": {
3-
"type": "minecraft:model",
4-
"model": "energeticsheep:item/green_energetic_wool"
3+
"type": "minecraft:composite",
4+
"models": [
5+
{
6+
"type": "minecraft:model",
7+
"model": "energeticsheep:item/green_energetic_wool"
8+
},
9+
{
10+
"type": "energeticsheep:energetic_wool_charge_item",
11+
"base": "energeticsheep:item/green_energetic_wool"
12+
}
13+
]
514
}
6-
}
15+
}

0 commit comments

Comments
 (0)