User Tools

Site Tools


tutorial:fluids

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:fluids [2021/10/19 22:54] – Added Advanced customization section. salvopeluxtutorial:fluids [2023/05/04 11:31] (current) – [Rendering setup] solidblock
Line 17: Line 17:
  
  /**  /**
- * @return whether the fluid infinite like water+ * @return whether the fluid is infinite (which means can be infinitely created like water). In vanilla, it depends on the game rule.
  */  */
  @Override  @Override
Line 25: Line 25:
  
  /**  /**
- * Perform actions when fluid flows into a replaceable block. Water drops+ * Perform actions when the fluid flows into a replaceable block. Water drops
  * the block's loot table. Lava plays the "block.lava.extinguish" sound.  * the block's loot table. Lava plays the "block.lava.extinguish" sound.
  */  */
Line 35: Line 35:
  
  /**  /**
- * Lava returns true if its FluidState is above a certain height and the+ * Lava returns true if it'FluidState is above a certain height and the
  * Fluid is Water.  * Fluid is Water.
  
Line 146: Line 146:
 @Override @Override
 public void onInitialize() { public void onInitialize() {
- STILL_ACID = class_2378.method_10230(class_2378.field_11154, new class_2960(MOD_ID, "acid"), new AcidFluid.Still()); + STILL_ACID = class_2378.method_10230(class_7923.field_41173, new class_2960("tutorial", "acid"), new AcidFluid.Still()); 
- FLOWING_ACID = class_2378.method_10230(class_2378.field_11154, new class_2960(MOD_ID, "flowing_acid"), new AcidFluid.Flowing()); + FLOWING_ACID = class_2378.method_10230(class_7923.field_41173, new class_2960("tutorial", "flowing_acid"), new AcidFluid.Flowing()); 
- ACID_BUCKET = class_2378.method_10230(class_2378.field_11142, new class_2960(MOD_ID, "acid_bucket"), + ACID_BUCKET = class_2378.method_10230(class_7923.field_41178, new class_2960("tutorial", "acid_bucket"), 
         new class_1755(STILL_ACID, new class_1792.class_1793().method_7896(class_1802.field_8550).method_7889(1)));         new class_1755(STILL_ACID, new class_1792.class_1793().method_7896(class_1802.field_8550).method_7889(1)));
    
Line 163: Line 163:
  "values":  "values":
  [  [
- "your_mod_id:acid", + "tutorial:acid", 
- "your_mod_id:flowing_acid"+ "tutorial:flowing_acid"
  ]  ]
 } }
Line 177: Line 177:
 @Override @Override
 public void onInitialize() { public void onInitialize() {
- ACID = class_2378.method_10230(class_2378.field_11146, new class_2960(MOD_ID, "acid"), new class_2404(STILL_ACID, FabricBlockSettings.method_9630(class_2246.field_10382)){});+ ACID = class_2378.method_10230(class_7923.field_41175, new class_2960(MOD_ID, "acid"), new class_2404(STILL_ACID, FabricBlockSettings.method_9630(class_2246.field_10382)){});
   
  // ...  // ...
Line 241: Line 241:
  
 ===== Rendering setup ===== ===== Rendering setup =====
-For your fluids to have textures or be tinted with a color, you will need to register a ''FluidRenderHandler'' for them. Here, we will reuse water's textures and just change the tint color applied to them. To make sure the textures are rendered as translucent, you can use Fabric's ''BlockRenderLayerMap''.+For your fluids to have textures or be tinted with a color, you will need to register a ''FluidRenderHandler'' for them. Here, we will reuse water's textures and just change the tint color applied to them. To make sure the textures are rendered as translucent, you can use Fabric's ''BlockRenderLayerMap'' (see [[blockappearance]]).
  
 <yarncode java [enable_line_numbers="true"]> <yarncode java [enable_line_numbers="true"]>
 +@Environment(EnvType.CLIENT)
 public class TutorialModClient implements ClientModInitializer { public class TutorialModClient implements ClientModInitializer {
  
  @Override  @Override
  public void onInitializeClient() {  public void onInitializeClient() {
- setupFluidRendering(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new class_2960("minecraft", "water"), 0x4CC248);+ FluidRenderHandlerRegistry.INSTANCE.register(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new SimpleFluidRenderHandler( 
 + new class_2960("minecraft:block/water_still"), 
 + new class_2960("minecraft:block/water_flow"), 
 + 0x4CC248 
 + )); 
  BlockRenderLayerMap.INSTANCE.putFluids(class_1921.method_23583(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID);  BlockRenderLayerMap.INSTANCE.putFluids(class_1921.method_23583(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID);
 +
 + //if you want to use custom textures they needs to be registered.
 + //In this example this is unnecessary because the vanilla water textures are already registered.
 + //To register your custom textures use this method.
 + //ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).register((atlasTexture, registry) -> {
 + //    registry.register(new Identifier("tutorial:block/custom_fluid_still"));
 + //    registry.register(new Identifier("tutorial:block/custom_fluid_flowing"));
 + //});
  
  // ...  // ...
- } 
- 
- public static void setupFluidRendering(final class_3611 still, final class_3611 flowing, final class_2960 textureFluidId, final int color) { 
- final class_2960 stillSpriteId = new class_2960(textureFluidId.method_12836(), "block/" + textureFluidId.method_12832() + "_still"); 
- final class_2960 flowingSpriteId = new class_2960(textureFluidId.method_12836(), "block/" + textureFluidId.method_12832() + "_flow"); 
- 
- // If they're not already present, add the sprites to the block atlas 
- ClientSpriteRegistryCallback.event(class_1059.field_5275).register((atlasTexture, registry) -> { 
- registry.register(stillSpriteId); 
- registry.register(flowingSpriteId); 
- }); 
- 
- final class_2960 fluidId = class_2378.field_11154.method_10221(still); 
- final class_2960 listenerId = new class_2960(fluidId.method_12836(), fluidId.method_12832() + "_reload_listener"); 
- 
- final class_1058[] fluidSprites = { null, null }; 
- 
- ResourceManagerHelper.get(class_3264.field_14188).registerReloadListener(new SimpleSynchronousResourceReloadListener() { 
- @Override 
- public class_2960 getFabricId() { 
- return listenerId; 
- } 
- 
- /** 
- * Get the sprites from the block atlas when resources are reloaded 
- */ 
- @Override 
- public void method_14491(class_3300 resourceManager) { 
- final Function<class_2960, class_1058> atlas = class_310.method_1551().method_1549(class_1059.field_5275); 
- fluidSprites[0] = atlas.apply(stillSpriteId); 
- fluidSprites[1] = atlas.apply(flowingSpriteId); 
- } 
- }); 
- 
- // The FluidRenderer gets the sprites and color from a FluidRenderHandler during rendering 
- final FluidRenderHandler renderHandler = new FluidRenderHandler() 
- { 
- @Override 
- public class_1058[] getFluidSprites(class_1920 view, class_2338 pos, class_3610 state) { 
- return fluidSprites; 
- } 
- 
- @Override 
- public int getFluidColor(class_1920 view, class_2338 pos, class_3610 state) { 
- return color; 
- } 
- }; 
- 
- FluidRenderHandlerRegistry.INSTANCE.register(still, renderHandler); 
- FluidRenderHandlerRegistry.INSTANCE.register(flowing, renderHandler); 
  }  }
 } }
Line 309: Line 273:
  
 ===== Generation in the world ===== ===== Generation in the world =====
-To make lakes of acid generate in the world, you can create a ''<yarn net.minecraft.class_3085>'' in your ''ModInitializer'' and then add it to the biomes you want it to generate in: +TODO Update to 1.19.4
- +
-<code java [enable_line_numbers="true"]> +
-public static LakeFeature ACID_LAKE; +
- +
-@Override +
-public void onInitialize() { +
- ACID_LAKE = Registry.register(Registry.FEATURE, new Identifier(MOD_ID, "acid_lake"), new LakeFeature(SingleStateFeatureConfig::deserialize)); +
-  +
- // generate in swamps, similar to water lakes, but with a chance of 40 (the higher the number, the lower the generation chance) +
- Biomes.SWAMP.addFeature( +
- GenerationStep.Feature.LOCAL_MODIFICATIONS, +
- ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState())) +
- .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40))) +
- ); +
-}+
 </code> </code>
- 
-===== Advanced customization ===== 
-You may be noticed that your custom fluid is too much similar to water. 
-In this section we'll cover some customizations, like adding custom fog and fog color, "strenght" (pushing back the player), and adding custom sounds and particles. 
- 
-Unfortunately, for now, there isn't a way to do this normally, and even with fabric api, so we need to use mixins to change the behaviour of Entity class and and BackgroundRenderer class. 
- 
-To start, remove the fluid from the water tag we added previously. 
- 
-==== Adding fog customizations ==== 
-To make things simpler and more customizable, let's add an interface to get the fog values: 
- 
-<code java [enable_line_numbers="true"]> 
-public interface FabricFlowableFluid { 
-    /** 
-     * Get the fog color. 
-     */ 
-    int getFogColor(Entity focusedEntity); 
-     
-    /** 
-     * Get the fog start value. 
-     */ 
-    float getFogStart(Entity focusedEntity); 
-     
-    /** 
-     * Get the fog end value. 
-     */ 
-    float getFogEnd(Entity focusedEntity); 
-} 
-</code> 
- 
-Now you must implement this interface to the previous example fluid class: 
- 
-<code java [enable_line_numbers="true"]> 
-public abstract class AcidFluid extends TutorialFluid implements FabricFlowableFluid { 
- 
-    [...] 
-     
-    /** 
-     * Get the fog color. 
-     */ 
-    @Override 
-    public int getFogColor(Entity focusedEntity) { 
-        //Set the fog color to #99ff33 for a light green acid. 
-        return 0x99ff33; 
-    } 
- 
-    /** 
-     * Get the fog start value. (Lava uses 0.25f, and 0.0f if the player has fire resistance) 
-     */ 
-    @Override 
-    public float getFogStart(Entity focusedEntity) { 
-        //You can use focusedEntity to get the effects, just comment for now. 
-        //if (entity instanceof LivingEntity && ((LivingEntity)entity).hasStatusEffect(StatusEffects.FIRE_RESISTANCE)) return 0.0f; 
-        return 0.25f; 
-    } 
- 
-    /** 
-     * Get the fog end value. (Lava uses 1.0f, and 3.0f if the player has fire resistance) 
-     */ 
-    @Override 
-    public float getFogEnd(Entity focusedEntity) { 
-        //You can use focusedEntity to get the effects, just comment for now. 
-        //if (entity instanceof LivingEntity && ((LivingEntity)entity).hasStatusEffect(StatusEffects.FIRE_RESISTANCE)) return 3.0f 
-        return 1.0f; 
-    } 
-     
-    [...] 
-} 
-</code> 
- 
-To make the game use these values we must use an inject mixin to two methods inside the BackgroundRenderer class: 
- 
-* render   : This will render the fog color 
-* applyFog : This will aplly the fog with the start and end parameters. 
- 
-<code java [enable_line_numbers="true"]> 
-@Mixin(BackgroundRenderer.class) 
-public class BackgroundRendererMixin { 
-    @Shadow private static float red; 
-    @Shadow private static float green; 
-    @Shadow private static float blue; 
-    @Shadow private static long lastWaterFogColorUpdateTime = -1L; 
- 
-    @Inject(method = "render(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/world/ClientWorld;IF)V", 
-            at = @At("HEAD"), 
-            cancellable = true) 
-    private static void render(Camera camera, float tickDelta, ClientWorld world, int i, float f, CallbackInfo ci) { 
-     
-        //Get the fluid that submerged the camera 
-        FluidState fluidState = ((FabricCamera)camera).getSubmergedFluidState(); 
- 
-        //If this is an instance of FabricFlowableFluid interface... 
-        if (fluidState.getFluid() instanceof FabricFlowableFluid fluid) { 
-         
-            //Get the color of the fog... 
-            int fogColor = fluid.getFogColor(camera.getFocusedEntity()); 
-             
-            //This is an hexadecimal color, so we need to get the three "red", "green", and "blue" values. 
-            red = (fogColor >> 16 & 255) / 255f; 
-            green = (fogColor >> 8 & 255) / 255f; 
-            blue = (fogColor & 255) / 255f; 
-             
-            //This is for compatibility, just add! 
-            lastWaterFogColorUpdateTime = -1L; 
-             
-            //Apply the color, then return. 
-            RenderSystem.clearColor(red, green, blue, 0.0f); 
-             
-            ci.cancel(); 
-        } 
-    } 
- 
-    @Inject(method = "applyFog(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/BackgroundRenderer$FogType;FZ)V", 
-            at = @At("HEAD"), 
-            cancellable = true) 
-    private static void applyFog(Camera camera, BackgroundRenderer.FogType fogType, float viewDistance, boolean thickFog, CallbackInfo ci) { 
-     
-        //Get the fluid that submerged the camera 
-        FluidState fluidState = ((FabricCamera)camera).getSubmergedFluidState(); 
- 
-        //If this is an instance of FabricFlowableFluid interface... 
-        if (fluidState.getFluid() instanceof FabricFlowableFluid fluid) { 
-         
-            //Get the start and end parameters and apply them, then return. 
-            RenderSystem.setShaderFogStart(fluid.getFogStart(camera.getFocusedEntity())); 
-            RenderSystem.setShaderFogEnd(fluid.getFogEnd(camera.getFocusedEntity())); 
-             
-            ci.cancel(); 
-        } 
-    } 
-} 
-</code> 
- 
-From 1.17 camera has not the getSubmergedFluidState() method so, as you can see, we must "re-add" it. 
-This is pretty simple: 
- 
-Add a "FabricCamera" interface: 
- 
-<code java [enable_line_numbers="true"]> 
-public interface FabricCamera { 
-    /** 
-     * Returns the fluid in which the camera is submerged. 
-     */ 
-    FluidState getSubmergedFluidState(); 
-} 
-</code> 
- 
-Add a mixin to get the value (Accessor) from the camera: 
- 
-<code java [enable_line_numbers="true"]> 
-@Mixin(Camera.class) 
-public class CameraMixin implements FabricCamera { 
-    @Shadow private BlockView area; 
-    @Shadow @Final private BlockPos.Mutable blockPos; 
- 
-    /** 
-     * Returns the fluid in which the camera is submerged. 
-     */ 
-    @Override 
-    public FluidState getSubmergedFluidState() { 
-        return this.area.getFluidState(this.blockPos); 
-    } 
-} 
-</code> 
- 
-==== Adding push back, custom sounds and custom particles ==== 
-(WIP, in the very nearly, future...) 
tutorial/fluids.1634684071.txt.gz · Last modified: 2021/10/19 22:54 by salvopelux