diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/chunk/VoxelNeighborhoodState.java b/common/src/main/java/dev/ryanhcode/sable/physics/chunk/VoxelNeighborhoodState.java index 9df2f9d..859cb3b 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/chunk/VoxelNeighborhoodState.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/chunk/VoxelNeighborhoodState.java @@ -1,14 +1,14 @@ package dev.ryanhcode.sable.physics.chunk; import dev.ryanhcode.sable.api.block.BlockWithSubLevelCollisionCallback; +import dev.ryanhcode.sable.physics.config.block_properties.PhysicsBlockPropertyHelper; import dev.ryanhcode.sable.util.LevelAccelerator; import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.block.KelpBlock; -import net.minecraft.world.level.block.KelpPlantBlock; +import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.piston.MovingPistonBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; @@ -70,15 +70,11 @@ public static boolean isFullBlock(final BlockGetter blockGetter, final BlockPos return IS_FULL_BLOCK.apply(blockGetter, state); } - public static boolean isLiquid(final BlockState state) { - return state.liquid() || state.getBlock() instanceof KelpPlantBlock || state.getBlock() instanceof KelpBlock; - } - public static VoxelNeighborhoodState getState(final LevelAccelerator level, final BlockPos pos, @Nullable final LevelChunk chunk) { final ChunkPos initialPos = new ChunkPos(pos); final BlockState state = chunk != null ? level.getBlockState(chunk, pos) : level.getBlockState(pos); - if (isLiquid(state) || BlockWithSubLevelCollisionCallback.hasCallback(state)) + if (PhysicsBlockPropertyHelper.getFluidViscosity(state) > 0 || BlockWithSubLevelCollisionCallback.hasCallback(state)) return CORNER; if (!isSolid(level, pos, state)) { diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyHelper.java b/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyHelper.java index 40fb3c3..b1c308a 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyHelper.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyHelper.java @@ -7,6 +7,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.BubbleColumnBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; @@ -102,4 +103,36 @@ public static FloatingBlockMaterial getFloatingMaterial(final BlockState state) return FloatingBlockMaterialDataHandler.allMaterials.get(location); } + /** + * Gets the buoyancy of a fluid block + * + * @param state The state of the block + * @return The buoyancy of the fluid + */ + public static double getFluidBuoyancy(final BlockState state) { + if (state.getBlock() instanceof BubbleColumnBlock) { + if (state.getValue(BubbleColumnBlock.DRAG_DOWN)) { + return -1.0; + } else { + return 3.0; + } + } + + return ((BlockStateExtension) state).sable$getProperty(PhysicsBlockPropertyTypes.BUOYANCY.get()); + } + + /** + * Gets the viscosity of a fluid block + * + * @param state The state of the block + * @return The viscosity of the fluid + */ + public static double getFluidViscosity(final BlockState state) { + if (state.getBlock() instanceof BubbleColumnBlock) { + return 1.0; + } + + return ((BlockStateExtension) state).sable$getProperty(PhysicsBlockPropertyTypes.VISCOSITY.get()); + } + } diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyTypes.java b/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyTypes.java index f1bb82d..832f073 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyTypes.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/config/block_properties/PhysicsBlockPropertyTypes.java @@ -54,6 +54,14 @@ public class PhysicsBlockPropertyTypes { * The scale / multiplier of the effects caused by the floating material for this block */ public static final RegistryObject> FLOATING_SCALE = register(Sable.sablePath("floating_scale"), Codec.DOUBLE, 1.0); + /** + * The buoyancy of a fluid + */ + public static final RegistryObject> BUOYANCY = register(Sable.sablePath("buoyancy"), Codec.DOUBLE, 0.0); + /** + * The viscosity of a fluid + */ + public static final RegistryObject> VISCOSITY = register(Sable.sablePath("viscosity"), Codec.DOUBLE, 0.0); public static void register() { // no-op diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java index 15f6f44..0d90607 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java @@ -218,17 +218,18 @@ public static synchronized int getID(final ServerLevel level) { * Adds a new voxel collider data entry. * * @param frictionMultiplier the friction multiplier - * @param isFluid if the block should be treated as a fluid + * @param buoyancy if the block is a fluid, its buoyancy + * @param viscosity if the block is a fluid, its viscosity * @param contactEvents if the block has special contact event behavior * @return the ID of the new block collider data entry */ @ApiStatus.Internal - protected static native int newVoxelCollider(double frictionMultiplier, double volume, double restitution, boolean isFluid, BlockSubLevelCollisionCallback contactEvents); + protected static native int newVoxelCollider(double frictionMultiplier, double volume, double restitution, double buoyancy, double viscosity, BlockSubLevelCollisionCallback contactEvents); /** * Adds a new box to a voxel collider data entry. * - * @param index the ID of the block physics data entry from {@link Rapier3D#newVoxelCollider(double, double, double, boolean, BlockSubLevelCollisionCallback)}} + * @param index the ID of the block physics data entry from {@link Rapier3D#newVoxelCollider(double, double, double, double, double, BlockSubLevelCollisionCallback)}} * @param bounds a 6-long double array, formatted [minX, minY, minZ, maxX, maxY, maxZ] */ @ApiStatus.Internal @@ -237,7 +238,7 @@ public static synchronized int getID(final ServerLevel level) { /** * Clears all boxes from a voxel collider data entry. * - * @param index the ID of the block physics data entry from {@link Rapier3D#newVoxelCollider(double, double, double, boolean, BlockSubLevelCollisionCallback)}} + * @param index the ID of the block physics data entry from {@link Rapier3D#newVoxelCollider(double, double, double, double, double, BlockSubLevelCollisionCallback)}} */ @ApiStatus.Internal public static native void clearVoxelColliderBoxes(int index); @@ -254,13 +255,14 @@ public static synchronized int getID(final ServerLevel level) { * Allocates a new block physics data entry * * @param frictionMultiplier the friction multiplier - * @param isFluid if the block should be treated as a fluid + * @param buoyancy if the block is a fluid, its buoyancy + * @param viscosity if the block is a fluid, its viscosity * @param contactEvents if the block has special contact event behavior * @return the handle of the new block physics data entry */ @ApiStatus.Internal - public static RapierVoxelColliderData createVoxelColliderEntry(final double frictionMultiplier, final double volume, final double restitution, final boolean isFluid, final BlockSubLevelCollisionCallback contactEvents) { - return new RapierVoxelColliderData(Rapier3D.newVoxelCollider(frictionMultiplier, volume, restitution, isFluid, contactEvents)); + public static RapierVoxelColliderData createVoxelColliderEntry(final double frictionMultiplier, final double volume, final double restitution, final double buoyancy, final double viscosity, final BlockSubLevelCollisionCallback contactEvents) { + return new RapierVoxelColliderData(Rapier3D.newVoxelCollider(frictionMultiplier, volume, restitution, buoyancy, viscosity, contactEvents)); } /** diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/collider/RapierVoxelColliderBakery.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/collider/RapierVoxelColliderBakery.java index 3b0f505..14cffc5 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/collider/RapierVoxelColliderBakery.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/collider/RapierVoxelColliderBakery.java @@ -5,7 +5,6 @@ import dev.ryanhcode.sable.api.physics.callback.BlockSubLevelCollisionCallback; import dev.ryanhcode.sable.api.physics.collider.SableCollisionContext; import dev.ryanhcode.sable.companion.math.JOMLConversion; -import dev.ryanhcode.sable.physics.chunk.VoxelNeighborhoodState; import dev.ryanhcode.sable.physics.config.block_properties.PhysicsBlockPropertyHelper; import dev.ryanhcode.sable.physics.impl.rapier.Rapier3D; import net.minecraft.Util; @@ -51,15 +50,17 @@ public RapierVoxelColliderBakery(@NotNull final BlockGetter blockGetter) { * @return the physics data ID for the block at the given position, or null for empty */ private @NotNull RapierVoxelColliderData buildPhysicsDataForBlock(final BlockState childState) { - final boolean liquid = VoxelNeighborhoodState.isLiquid(childState); final double friction = PhysicsBlockPropertyHelper.getFriction(childState); final double volume = PhysicsBlockPropertyHelper.getVolume(childState); final double restitution = PhysicsBlockPropertyHelper.getRestitution(childState); + final double buoyancy = PhysicsBlockPropertyHelper.getFluidBuoyancy(childState); + final double viscosity = PhysicsBlockPropertyHelper.getFluidViscosity(childState); final BlockSubLevelCollisionCallback callback = BlockWithSubLevelCollisionCallback.sable$getCallback(childState); - final RapierVoxelColliderData entry = Rapier3D.createVoxelColliderEntry(friction, volume, restitution, liquid, callback); + final RapierVoxelColliderData entry = Rapier3D.createVoxelColliderEntry(friction, volume, restitution, buoyancy, viscosity, callback); - if (liquid) { + + if (viscosity > 0) { entry.addBox(JOMLConversion.ZERO, new Vector3d(1.0, 1.0, 1.0)); return entry; } diff --git a/common/src/main/resources/data/sable/physics_block_properties/high_buoyancy_fluid.json b/common/src/main/resources/data/sable/physics_block_properties/high_buoyancy_fluid.json new file mode 100644 index 0000000..fc75b60 --- /dev/null +++ b/common/src/main/resources/data/sable/physics_block_properties/high_buoyancy_fluid.json @@ -0,0 +1,7 @@ +{ + "selector": "#sable:high_buoyancy_fluid", + + "properties": { + "sable:buoyancy": 2.0 + } +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/physics_block_properties/low_buoyancy_fluid.json b/common/src/main/resources/data/sable/physics_block_properties/low_buoyancy_fluid.json new file mode 100644 index 0000000..1cb9d8b --- /dev/null +++ b/common/src/main/resources/data/sable/physics_block_properties/low_buoyancy_fluid.json @@ -0,0 +1,7 @@ +{ + "selector": "#sable:low_buoyancy_fluid", + + "properties": { + "sable:buoyancy": 1.0 + } +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/physics_block_properties/runny_fluid.json b/common/src/main/resources/data/sable/physics_block_properties/runny_fluid.json new file mode 100644 index 0000000..b2d4fd8 --- /dev/null +++ b/common/src/main/resources/data/sable/physics_block_properties/runny_fluid.json @@ -0,0 +1,7 @@ +{ + "selector": "#sable:runny_fluid", + + "properties": { + "sable:viscosity": 1.0 + } +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/physics_block_properties/viscous_fluid.json b/common/src/main/resources/data/sable/physics_block_properties/viscous_fluid.json new file mode 100644 index 0000000..da332e7 --- /dev/null +++ b/common/src/main/resources/data/sable/physics_block_properties/viscous_fluid.json @@ -0,0 +1,7 @@ +{ + "selector": "#sable:viscous_fluid", + + "properties": { + "sable:viscosity": 4.0 + } +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/tags/block/high_buoyancy_fluid.json b/common/src/main/resources/data/sable/tags/block/high_buoyancy_fluid.json new file mode 100644 index 0000000..0dbb93a --- /dev/null +++ b/common/src/main/resources/data/sable/tags/block/high_buoyancy_fluid.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:lava", + { "id": "create:honey", "required": false } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/tags/block/low_buoyancy_fluid.json b/common/src/main/resources/data/sable/tags/block/low_buoyancy_fluid.json new file mode 100644 index 0000000..40655a0 --- /dev/null +++ b/common/src/main/resources/data/sable/tags/block/low_buoyancy_fluid.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:water", + { "id": "create:chocolate", "required": false } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/tags/block/runny_fluid.json b/common/src/main/resources/data/sable/tags/block/runny_fluid.json new file mode 100644 index 0000000..3d6f6d5 --- /dev/null +++ b/common/src/main/resources/data/sable/tags/block/runny_fluid.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:water" + ] +} \ No newline at end of file diff --git a/common/src/main/resources/data/sable/tags/block/viscous_fluid.json b/common/src/main/resources/data/sable/tags/block/viscous_fluid.json new file mode 100644 index 0000000..4cc8004 --- /dev/null +++ b/common/src/main/resources/data/sable/tags/block/viscous_fluid.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:lava", + { "id": "create:chocolate", "required": false }, + { "id": "create:honey", "required": false } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/natives/sable_rapier/sable_rapier_aarch64_linux.so b/common/src/main/resources/natives/sable_rapier/sable_rapier_aarch64_linux.so deleted file mode 100755 index 7fc4b4f..0000000 Binary files a/common/src/main/resources/natives/sable_rapier/sable_rapier_aarch64_linux.so and /dev/null differ diff --git a/common/src/main/resources/natives/sable_rapier/sable_rapier_aarch64_macos.dylib b/common/src/main/resources/natives/sable_rapier/sable_rapier_aarch64_macos.dylib deleted file mode 100755 index e5ca6e8..0000000 Binary files a/common/src/main/resources/natives/sable_rapier/sable_rapier_aarch64_macos.dylib and /dev/null differ diff --git a/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_linux.so b/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_linux.so deleted file mode 100755 index 84e0905..0000000 Binary files a/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_linux.so and /dev/null differ diff --git a/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_macos.dylib b/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_macos.dylib deleted file mode 100755 index 45975c5..0000000 Binary files a/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_macos.dylib and /dev/null differ diff --git a/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_windows.dll b/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_windows.dll deleted file mode 100755 index 2ff41b5..0000000 Binary files a/common/src/main/resources/natives/sable_rapier/sable_rapier_x86_64_windows.dll and /dev/null differ diff --git a/common/src/main/rust/marten/src/level.rs b/common/src/main/rust/marten/src/level.rs index 2f3b491..3222383 100644 --- a/common/src/main/rust/marten/src/level.rs +++ b/common/src/main/rust/marten/src/level.rs @@ -83,8 +83,9 @@ pub struct VoxelColliderData { /// Formatted [min_x, min_y, min_z, max_x, max_y, max_z] pub collision_boxes: Vec<(f32, f32, f32, f32, f32, f32)>, - /// If this should be treated as a fluid for buoyancy - pub is_fluid: bool, + /// Non-zero values if this is a fluid + pub buoyancy: f32, + pub viscosity: f32, /// The friction multiplier pub friction: f32, diff --git a/common/src/main/rust/rapier/src/buoyancy.rs b/common/src/main/rust/rapier/src/buoyancy.rs index a0e1d4a..04c63eb 100644 --- a/common/src/main/rust/rapier/src/buoyancy.rs +++ b/common/src/main/rust/rapier/src/buoyancy.rs @@ -47,7 +47,33 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) { let vels: RigidBodyVelocity = *body.vels(); let complex = (local_bounds_max - local_bounds_min).sum() < 10; + let scene = state.scenes.get_mut(&scene.scene_id).unwrap(); for (static_pos, dynamic_pos) in pairs.iter() { + let fluid_chunk = scene.get_chunk(static_pos.x >> 4, static_pos.y >> 4, static_pos.z >> 4); + + if fluid_chunk.is_none() { + continue; + } + + let (fluid_block_id, _) = fluid_chunk.unwrap().get_block( + static_pos.x & 15, + static_pos.y & 15, + static_pos.z & 15, + ); + + if fluid_block_id == 0 { + continue; + } + + let fluid_voxel_data = &state.voxel_collider_map.get( + (fluid_block_id - 1) as usize, + Vector3::new(static_pos.x, static_pos.y, static_pos.z), + ); + + let Some(fluid_data) = fluid_voxel_data else { + continue; + }; + let local_pos = Vector3::::new( dynamic_pos.x as f64 + 0.5, dynamic_pos.y as f64 + 0.5, @@ -68,13 +94,13 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) { local_pos.y + y as Real * 0.25, local_pos.z + z as Real * 0.25, ); - do_drag(body, &vels, static_pos, &local_pos, 0.25, 1.0); + do_drag(body, &vels, static_pos, &local_pos, 0.25, 1.0, fluid_data.viscosity); } } else { - do_drag(body, &vels, static_pos, &local_pos, 0.5, 1.0); + do_drag(body, &vels, static_pos, &local_pos, 0.5, 1.0, fluid_data.viscosity); } } - let scene = state.scenes.get_mut(&scene.scene_id).unwrap(); + for (static_pos, dynamic_pos) in pairs.iter() { let chunk = scene.get_chunk(dynamic_pos.x >> 4, dynamic_pos.y >> 4, dynamic_pos.z >> 4); @@ -102,6 +128,31 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) { continue; }; + let fluid_chunk = scene.get_chunk(static_pos.x >> 4, static_pos.y >> 4, static_pos.z >> 4); + + if fluid_chunk.is_none() { + continue; + } + + let (fluid_block_id, _) = fluid_chunk.unwrap().get_block( + static_pos.x & 15, + static_pos.y & 15, + static_pos.z & 15, + ); + + if fluid_block_id == 0 { + continue; + } + + let fluid_voxel_data = &state.voxel_collider_map.get( + (fluid_block_id - 1) as usize, + Vector3::new(static_pos.x, static_pos.y, static_pos.z), + ); + + let Some(fluid_data) = fluid_voxel_data else { + continue; + }; + let local_pos = Vector3::::new( dynamic_pos.x as f64 + 0.5, dynamic_pos.y as f64 + 0.5, @@ -129,6 +180,7 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) { &local_pos, 0.25, voxel_collider_data.volume, + fluid_data.buoyancy ); } } else { @@ -138,6 +190,7 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) { &local_pos, 0.5, voxel_collider_data.volume, + fluid_data.buoyancy ); } } @@ -151,6 +204,7 @@ fn do_drag( point: &Vector, size: Real, strength: Real, + viscosity: Real ) { let point = body.position().transform_point(*point); @@ -176,7 +230,7 @@ fn do_drag( let volume = overlap.unwrap().volume(); let velo = vels.velocity_at_point(point, body.mass_properties().world_com); - body.add_force_at_point(-velo * 1.7 * volume * strength, point, false); + body.add_force_at_point(-velo * 1.7 * volume * strength * viscosity, point, false); } fn do_float( @@ -185,6 +239,7 @@ fn do_float( point: &Vector, size: Real, strength: Real, + buoyancy: Real ) { let point = body.position().transform_point(*point); @@ -210,7 +265,7 @@ fn do_float( let volume = overlap.unwrap().volume(); body.add_force_at_point( - Vector::new(0.0, 10.5 * volume * strength, 0.0), + Vector::new(0.0, 10.5 * volume * strength * buoyancy, 0.0), point, false, ); diff --git a/common/src/main/rust/rapier/src/dispatcher.rs b/common/src/main/rust/rapier/src/dispatcher.rs index 98be179..835e8a2 100644 --- a/common/src/main/rust/rapier/src/dispatcher.rs +++ b/common/src/main/rust/rapier/src/dispatcher.rs @@ -303,7 +303,7 @@ impl SableDispatcher { unreachable!() }; - if voxel_collider_data.is_fluid { + if voxel_collider_data.viscosity > 0.0 { continue; } @@ -857,7 +857,7 @@ fn is_inside_voxel_collider( return false; }; - if voxel_data.is_fluid { + if voxel_data.viscosity > 0.0 { return false; } diff --git a/common/src/main/rust/rapier/src/lib.rs b/common/src/main/rust/rapier/src/lib.rs index 6e3f5eb..d282a49 100644 --- a/common/src/main/rust/rapier/src/lib.rs +++ b/common/src/main/rust/rapier/src/lib.rs @@ -827,7 +827,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add if state.voxel_collider_map.voxel_colliders[(block.0 - 1) as usize] .as_ref() .unwrap() - .is_fluid + .viscosity > 0.0 { insert_block_octree( &mut octree_chunk.liquid_octree, @@ -1008,7 +1008,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cha .unwrap() .as_ref() .unwrap() - .is_fluid + .viscosity > 0.0 { insert_block_octree( &mut octree_chunk.liquid_octree, diff --git a/common/src/main/rust/rapier/src/voxel_collider.rs b/common/src/main/rust/rapier/src/voxel_collider.rs index 2badf7f..e1e6ae1 100644 --- a/common/src/main/rust/rapier/src/voxel_collider.rs +++ b/common/src/main/rust/rapier/src/voxel_collider.rs @@ -50,7 +50,8 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_new friction: jdouble, volume: jdouble, restitution: jdouble, - is_fluid: jboolean, + buoyancy: jdouble, + viscosity: jdouble, contact_events: JObject, dynamic: jboolean, ) -> jint { @@ -85,7 +86,8 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_new .voxel_colliders .push(Some(VoxelColliderData { collision_boxes: Vec::new(), - is_fluid: is_fluid > 0, + buoyancy: buoyancy as Real, + viscosity: viscosity as Real, friction: friction as Real, volume: volume as Real, restitution: restitution as Real,