Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<PhysicsBlockPropertyType<Double>> FLOATING_SCALE = register(Sable.sablePath("floating_scale"), Codec.DOUBLE, 1.0);
/**
* The buoyancy of a fluid
*/
public static final RegistryObject<PhysicsBlockPropertyType<Double>> BUOYANCY = register(Sable.sablePath("buoyancy"), Codec.DOUBLE, 0.0);
/**
* The viscosity of a fluid
*/
public static final RegistryObject<PhysicsBlockPropertyType<Double>> VISCOSITY = register(Sable.sablePath("viscosity"), Codec.DOUBLE, 0.0);

public static void register() {
// no-op
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"selector": "#sable:high_buoyancy_fluid",

"properties": {
"sable:buoyancy": 2.0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"selector": "#sable:low_buoyancy_fluid",

"properties": {
"sable:buoyancy": 1.0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"selector": "#sable:runny_fluid",

"properties": {
"sable:viscosity": 1.0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"selector": "#sable:viscous_fluid",

"properties": {
"sable:viscosity": 4.0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"minecraft:lava",
{ "id": "create:honey", "required": false }
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"minecraft:water",
{ "id": "create:chocolate", "required": false }
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"minecraft:water"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"replace": false,
"values": [
"minecraft:lava",
{ "id": "create:chocolate", "required": false },
{ "id": "create:honey", "required": false }
]
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 3 additions & 2 deletions common/src/main/rust/marten/src/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
65 changes: 60 additions & 5 deletions common/src/main/rust/rapier/src/buoyancy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,33 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) {
let vels: RigidBodyVelocity<Real> = *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::<f64>::new(
dynamic_pos.x as f64 + 0.5,
dynamic_pos.y as f64 + 0.5,
Expand All @@ -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);

Expand Down Expand Up @@ -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::<f64>::new(
dynamic_pos.x as f64 + 0.5,
dynamic_pos.y as f64 + 0.5,
Expand Down Expand Up @@ -129,6 +180,7 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) {
&local_pos,
0.25,
voxel_collider_data.volume,
fluid_data.buoyancy
);
}
} else {
Expand All @@ -138,6 +190,7 @@ pub fn compute_buoyancy(scene: &mut PhysicsScene) {
&local_pos,
0.5,
voxel_collider_data.volume,
fluid_data.buoyancy
);
}
}
Expand All @@ -151,6 +204,7 @@ fn do_drag(
point: &Vector,
size: Real,
strength: Real,
viscosity: Real
) {
let point = body.position().transform_point(*point);

Expand All @@ -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(
Expand All @@ -185,6 +239,7 @@ fn do_float(
point: &Vector,
size: Real,
strength: Real,
buoyancy: Real
) {
let point = body.position().transform_point(*point);

Expand All @@ -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,
);
Expand Down
Loading