Depend on Shine
Shine's client-side V2 API is provided in com.bloom.api.v2. It supports feature rules, content ownership, renderer cooperation, external lighting, weather state, and diagnostics without exposing Shine's framebuffers or shaders.
Use a compatibility resource for fixed rules. Use the Java API when a rule depends on live state or when another renderer contributes data to Shine.
Development Dependency
Compile against the Shine jar without bundling its classes:
dependencies {
modCompileOnly files("libs/shine-3.0.jar")
}Stable Maven coordinates are not published. Shine's Fabric mod ID is shine.
Use "depends": { "shine": ">=3.0" } when Shine is required. Optional integrations can use "suggests": { "shine": ">=3.0" } and the dedicated entrypoint below.
Compatibility Entrypoint
Register the entrypoint in fabric.mod.json:
{
"entrypoints": {
"shine:compat": [
"com.example.client.ExampleShineCompat"
]
},
"suggests": {
"shine": ">=3.0"
}
}Only Shine requests shine:compat, so an optional integration remains inactive when Shine is absent.
package com.example.client;
import com.bloom.api.v2.ShineCompatEntrypoint;
import com.bloom.api.v2.ShineCompatRegistrar;
import com.bloom.api.v2.ShineFeatures;
import com.bloom.api.v2.ShineSelector;
import java.util.List;
public final class ExampleShineCompat implements ShineCompatEntrypoint {
@Override
public void register(ShineCompatRegistrar registrar) {
ShineSelector selector = ShineSelector.builder()
.blocks(List.of("#example_mod:custom_grass"))
.build();
registrar.claim(
"custom_grass_renderer",
ShineFeatures.GRASS_BLADES,
selector,
ExampleState::customGrassActive,
"Example Mod renders this grass"
);
}
}Dynamic conditions are sampled at most once per client tick. Keep them fast, side-effect free, and safe for client-thread calls. A callback that throws is disabled for the session and recorded in Compatibility Diagnostics.
ShineInteropApi.registrar(ownerId) provides the same registration surface when a Fabric entrypoint is not practical.
Features and Rules
ShineFeatures contains the canonical feature constants. ShineFeatureKey represents a namespaced feature ID, and ShineInteropApi.features() exposes the complete descriptor catalog.
Rules inherit through the feature tree. Targeting ShineFeatures.PARTICLES, for example, also affects its particle children. The suppress, claim, include, and exclude helpers use the same modes and selectors documented under Compatibility. Use registerRule(...) when the full form is needed.
Render Scopes
A render scope keeps synchronous external drawing out of selected Shine capture channels:
import com.bloom.api.v2.ShineInteropApi;
import com.bloom.api.v2.ShineRenderChannel;
import com.bloom.api.v2.ShineRenderScope;
import java.util.List;
try (ShineRenderScope ignored = ShineInteropApi.openRenderScope(
"example_mod",
List.of(ShineRenderChannel.BLOOM_SOURCE, ShineRenderChannel.RIM_MASK))) {
ExternalEntityBatchRenderer.render();
}Scopes are nestable and must use try-with-resources. They affect only capture work reached synchronously inside the scope. Use a particleTypes content rule for deferred particles and a terrain visibility contribution for recorded Sodium terrain meshes.
Contributions
| Registration | Purpose |
|---|---|
registerColoredLight(...) | Define a static colored light for selected blocks or block tags. |
registerDynamicLights(...) | Supply moving world-space lights once per client tick. |
registerBloomSource(...) | Give matching geometry in Shine's normal capture path a bloom strength. |
registerRimMaskSource(...) | Include matching block or fluid terrain in the rim-light mask. |
registerWeatherProvider(...) | Supply normalized rain, thunder, wind, and a weather type. |
registerTerrainVisibility(...) | Compose a constrained visibility value into Sodium terrain shaders. |
registerPostPass(...) | Run an opaque callback at a semantic post-processing phase. |
Static colored lights require a block or block-tag selector. Dynamic light and weather providers are sampled once per tick.
When multiple definitions match the same content:
| Contribution | Result |
|---|---|
| Colored light | Colors are averaged by intensity, the largest radius is used, and intensities are added. Shine's configured block light participates in the same merge, and the result is clamped to Shine's limits. |
| Bloom source | The strongest matching strength wins against the configured source strength, then the result is clamped to Shine's maximum. |
| Rim-mask source | The content is included when either Shine's configuration or any matching contribution includes it. |
registrar.registerColoredLight(
"blue_lantern",
ShineSelector.builder().blocks(List.of("example_mod:blue_lantern")).build(),
new ShineColoredLight(0x66AAFF, 12.0F, 1.0F)
);
registrar.registerDynamicLights("wisps", () -> currentWisps().stream()
.map(wisp -> new ShineDynamicLight(
wisp.getX(), wisp.getY(), wisp.getZ(),
new ShineColoredLight(0x8EEBFF, 9.0F, 0.8F)))
.toList()
);
registrar.registerWeatherProvider("acid_rain", 100, () ->
AcidRainClient.active()
? new ShineWeatherState(1.0F, 0.2F, 0.7F, "example_mod:acid_rain")
: null
);Weather values are clamped to 0..1. The highest-priority provider returning a non-null state supplies Shine's effective weather values.
Bloom and rim-mask definitions classify geometry Shine already captures:
registrar.registerBloomSource(
"charged_crystal",
ShineSelector.builder().blocks(List.of("#example_mod:charged_crystals")).build(),
2.0F
);
registrar.registerRimMaskSource(
"outlined_crystal",
ShineSelector.builder().blocks(List.of("example_mod:charged_crystal")).build()
);Bloom definitions can match blocks, fluids, entity textures, and particle types. Rim-mask definitions apply to block and fluid terrain. These registrations do not add draw calls or bypass the player's Bloom and Rim Light settings.
Terrain Visibility
Terrain integrations provide a GLSL identifier with an optional swizzle, not shader source. The visibility channels are SCENE_VISIBILITY, BLOOM_SOURCE_VISIBILITY, and RIM_MASK_VISIBILITY.
The external Sodium shader patch must declare the symbol and contain the required marker. Shine validates and composes contributions in owner and contribution-ID order.
Post Passes
Post passes run in BEFORE_SCENE_POST, BEFORE_SHINE, AFTER_SHINE, or FINAL.
registrar.registerPostPass(
"soft_overlay",
ShinePostPhase.AFTER_SHINE,
50,
List.of(ShineRenderChannel.POST_PROCESSING),
context -> ExampleOverlay.render(
context.width(), context.height(), context.partialTick())
);Within each phase, lower priority values run first; ties are ordered by owner and pass ID. Passes run only while Shine is enabled, post-processing is configured through Bloom, Rim Light, or Shine Effects, and compatibility rules allow post-processing. ShinePostContext exposes only width, height, and partial tick. The callback is responsible for restoring its own rendering state.
State and Diagnostics
| API | Result |
|---|---|
features(), feature(...), resolveFeature(...) | Feature catalog and ID resolution. |
isFeatureAllowed(...), canSpawn(...) | Fast compatibility gates, with contextual overloads. |
contentDecision(...) | DEFAULT, INCLUDE, EXCLUDE, or CLAIMED for one context. |
featureState(...), featureStates() | Configured, suppressed, claimed, and effective feature state. |
activeRules(...), rules() | Active registrations and their owner, source, selector, and reason. |
diagnostics() | Invalid registrations and isolated callback failures. |
isFeatureAllowed(...) checks compatibility only; the feature's user setting must still be checked. State and diagnostic methods describe configuration and registration state, not whether a renderer produced pixels in a particular frame.
The API also exposes immutable snapshots of registered terrain, lighting, render-source, weather, dynamic-light, and post-pass contributions for diagnostics and tooling.
V1 API
com.bloom.api.ShineCompatibilityApi and ShineSystem provide group-level blockers and coarse read-only state. V1 registrations are translated into V2 feature rules. Use V2 for individual feature targets, contextual selectors, content decisions, and contributions.