-
Notifications
You must be signed in to change notification settings - Fork 0
Custom Anchor Rendering
While a SpellFieldAnchorEntity is inherently invisible, EnchantmentCore provides a robust, data-driven pipeline to attach 2D animated sprites to it, as well as a developer-facing registry for attaching completely custom 3D rendering logic.
By default, passing an AnchorVisualConfig into your summon_spell_field_anchor effect payload allows you to render a 2D quad at the anchor's location. This quad is dynamically rendered at full brightness (ignoring block light) to ensure spell effects look vibrant even inside blocks or entities.
The visual config handles UV manipulation automatically based on the frame_count and ticks_per_frame properties, allowing you to easily animate your spell fields using a standard vertical sprite sheet.
| Property | Type | Description |
|---|---|---|
texture |
ResourceLocation |
The path to the texture sprite sheet (e.g., my_mod:textures/entity/spell/magic_circle.png). |
model_id |
ResourceLocation |
An optional identifier used to bypass standard 2D rendering in favor of custom 3D logic. |
scale |
Float |
The visual scaling multiplier for the quad. |
frame_count |
Integer |
The total number of animation frames in the vertical sprite sheet. |
ticks_per_frame |
Integer |
The duration each frame is displayed before advancing. |
tint |
Integer |
An RGB color tint applied across the entire quad. (Note: If writing raw JSON, this must be a decimal integer, e.g., 16711680 for red. Hexadecimal like 0xFF0000 is only valid in Java datagen).
|
If a simple 2D animated quad is insufficient for your spell (e.g., you want to render a complex rotating 3D crystal or a localized black hole shader), you can bypass the default renderer entirely using the AnchorRendererRegistry.
Because JSON files contain purely static data, Java rendering classes cannot be directly passed into an enchantment's AnchorVisualConfig. Instead, Enchantment Core uses a delegation pattern to bridge the gap between your data and your client-side rendering logic.
In Minecraft, only one renderer can be registered per entity type. Enchantment Core registers a global SpellFieldAnchorRenderer to handle every spell field anchor. Every frame, this global renderer executes the following logic:
-
Check for a Model ID: It asks the entity if a
model_idstring was provided in its JSON payload and synchronized to the client. -
Lookup the Delegate: If a
model_idexists (e.g.,my_mod:crystal_anchor), the global renderer queries theAnchorRendererRegistryfor a mapped Java class. -
Execute Custom Logic: If it finds a registered
AnchorModelRenderer, it skips the default 2D rendering and delegates the rendering matrix (PoseStack) over to the custom class. -
Fallback: If no
model_idis defined, or the registry lookup fails, it falls back to rendering the 2D sprite defined in thetextureproperty.
In your enchantment's JSON (or Datagen), supply a custom identifier to the model_id property of the AnchorVisualConfig:
"visual_config": {
"model_id": "my_mod:crystal_anchor",
"scale": 2.0
}Create a class that implements the AnchorModelRenderer functional interface. This interface exposes the raw parameters from Minecraft's EntityRenderer pipeline.
import johnsmith.enchantmentcore.api.client.render.AnchorModelRenderer;
import johnsmith.enchantmentcore.api.entity.SpellFieldAnchor;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.entity.Entity;
public class CrystalAnchorRenderer implements AnchorModelRenderer {
@Override
public void render(Entity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
// Retrieve standard parameters (scale, frames, tint) if you want to utilize them
float scale = ((SpellFieldAnchor) entity).getVisualScale();
poseStack.pushPose();
// Apply your custom transformations, rotations, and rendering logic here.
// e.g., Rendering a BlockState, a custom GeoModel, or raw Vertex buffers.
poseStack.popPose();
}
}Register your custom renderer during your client-side setup phase, binding it to the model_id you defined in your data.
import johnsmith.enchantmentcore.api.client.render.AnchorRendererRegistry;
import net.minecraft.resources.ResourceLocation;
public class MyModClient {
public static void init() {
AnchorRendererRegistry.register(
ResourceLocation.fromNamespaceAndPath("my_mod", "crystal_anchor"),
new CrystalAnchorRenderer()
);
}
}Want to learn more about the configuration engine powering Enchantment Core?
Check out Config Understood, the official wiki for Config Overhauled, for a deep dive into dynamic property generation and state synchronization!