diff options
-rw-r--r-- | Float.gd | 15 | ||||
-rw-r--r-- | Float.tscn | 18 | ||||
-rw-r--r-- | Main.tscn | 56 | ||||
-rw-r--r-- | Ship.gd | 4 | ||||
-rw-r--r-- | project.godot | 6 | ||||
-rw-r--r-- | water/BuoyantBody.gd | 120 |
6 files changed, 139 insertions, 80 deletions
diff --git a/Float.gd b/Float.gd deleted file mode 100644 index 77ffa95..0000000 --- a/Float.gd +++ /dev/null @@ -1,15 +0,0 @@ -extends Spatial - -onready var inv_gravity = -ProjectSettings.get_setting("physics/3d/default_gravity_vector") * ProjectSettings.get_setting("physics/3d/default_gravity") - -func _physics_process(_delta): - var parent = get_parent() - var water = get_node("/root/Main/Water") - var height = self.global_transform.origin.y - var wave_height = water.height(self.global_transform.origin) - if height < wave_height: - var pos = self.global_transform.origin-parent.global_transform.origin - #buoyancy force is weight of the displaced water - #we can estimate it by how deep we are in the water - var buoyancy = inv_gravity * clamp(wave_height-height,0.0,1.0) - parent.add_force(buoyancy, pos) diff --git a/Float.tscn b/Float.tscn deleted file mode 100644 index 6d1ba7e..0000000 --- a/Float.tscn +++ /dev/null @@ -1,18 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://Float.gd" type="Script" id=1] - -[sub_resource type="SpatialMaterial" id=1] -albedo_color = Color( 0.823529, 0.796078, 0.0588235, 1 ) - -[sub_resource type="SphereMesh" id=2] -material = SubResource( 1 ) -radius = 0.5 -height = 1.0 - -[node name="Float" type="Spatial"] -script = ExtResource( 1 ) - -[node name="MeshInstance" type="MeshInstance" parent="."] -visible = false -mesh = SubResource( 2 ) @@ -1,10 +1,8 @@ -[gd_scene load_steps=15 format=2] +[gd_scene load_steps=14 format=2] [ext_resource path="res://character/fps_controller/fps_controller.tscn" type="PackedScene" id=1] [ext_resource path="res://ship/ship/ship.tscn" type="PackedScene" id=2] [ext_resource path="res://water/Water.tscn" type="PackedScene" id=3] -[ext_resource path="res://Float.tscn" type="PackedScene" id=4] -[ext_resource path="res://ship/ship/crate.tscn" type="PackedScene" id=5] [ext_resource path="res://world/World.tscn" type="PackedScene" id=6] [ext_resource path="res://Ship.gd" type="Script" id=7] [ext_resource path="res://FPS.gd" type="Script" id=8] @@ -18,6 +16,9 @@ [sub_resource type="SpatialMaterial" id=21] albedo_color = Color( 0.941176, 0.658824, 0.215686, 1 ) +[sub_resource type="BoxShape" id=23] +extents = Vector3( 10, 5, 55 ) + [sub_resource type="CapsuleShape" id=22] radius = 11.4853 height = 97.7559 @@ -72,12 +73,19 @@ acceleration = 10.0 [node name="ShipRigidBody" type="RigidBody" parent="."] collision_layer = 2 collision_mask = 8 +mass = 10000.0 can_sleep = false axis_lock_linear_x = true axis_lock_linear_z = true linear_damp = 2.0 angular_damp = 2.0 script = ExtResource( 7 ) +water = NodePath("../Water") + +[node name="BuoyancyBounds" type="CollisionShape" parent="ShipRigidBody"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3, 0 ) +shape = SubResource( 23 ) +disabled = true [node name="CollisionShape" type="CollisionShape" parent="ShipRigidBody"] transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.78898, 0 ) @@ -85,48 +93,6 @@ shape = SubResource( 22 ) [node name="ship" parent="ShipRigidBody" instance=ExtResource( 2 )] -[node name="Float" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, 53.0825 ) - -[node name="Float2" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, 33.0751 ) - -[node name="Float3" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, 15.7992 ) - -[node name="Float4" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, 0 ) - -[node name="Float5" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, -13.8797 ) - -[node name="Float6" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, -28.8669 ) - -[node name="Float7" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -9.77011, -3.30323, -48.8743 ) - -[node name="Float8" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, 52.7872 ) - -[node name="Float9" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, 33.2966 ) - -[node name="Float10" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, 16.0946 ) - -[node name="Float11" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, 0 ) - -[node name="Float12" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, -14.1012 ) - -[node name="Float13" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, -29.0883 ) - -[node name="Float14" parent="ShipRigidBody" instance=ExtResource( 4 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9.77011, -3.30323, -49.022 ) - [node name="fps_controller" parent="ShipRigidBody" instance=ExtResource( 1 )] transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 17, 44 ) @@ -1,4 +1,4 @@ -extends RigidBody +extends BuoyantBody export var ang_acceleration = 240.0 @@ -29,4 +29,4 @@ func _input(event): func _integrate_forces(_state): var stick = Input.get_axis("ship_right","ship_left") if stick != 0.0: - self.add_torque(Vector3.UP*stick*ang_acceleration) + self.add_torque(Vector3.UP*stick*ang_acceleration*self.mass) diff --git a/project.godot b/project.godot index 42f0e5f..1f86ed2 100644 --- a/project.godot +++ b/project.godot @@ -9,6 +9,11 @@ config_version=4 _global_script_classes=[ { +"base": "RigidBody", +"class": "BuoyantBody", +"language": "GDScript", +"path": "res://water/BuoyantBody.gd" +}, { "base": "Monument", "class": "DockMonument", "language": "GDScript", @@ -30,6 +35,7 @@ _global_script_classes=[ { "path": "res://world/monuments/lighthouse/lighthouseMonument.gd" } ] _global_script_class_icons={ +"BuoyantBody": "", "DockMonument": "", "Monument": "", "Wave": "", diff --git a/water/BuoyantBody.gd b/water/BuoyantBody.gd new file mode 100644 index 0000000..4a9dc78 --- /dev/null +++ b/water/BuoyantBody.gd @@ -0,0 +1,120 @@ +extends RigidBody +class_name BuoyantBody + +export(NodePath) var water = NodePath("/root/Main/Water") +onready var _water = get_node(water) + +var volume_half = 0.0 +var volume = 0.0 +var bounding_box = [] +var center_of_mass = Vector3.ZERO +var volume_up = Vector3.UP + +onready var inv_gravity = -ProjectSettings.get_setting("physics/3d/default_gravity_vector") * ProjectSettings.get_setting("physics/3d/default_gravity") +const POINT_VOLUME_WEIGHT = 0.125 + +func _ready(): + for c in get_children(): + if c is CollisionShape: + if c.shape is SphereShape: + _init_buoyancy(c.shape.radius,c.shape.radius,c.shape.radius,c.transform) + elif c.shape is CapsuleShape: + _init_buoyancy(c.shape.radius,c.shape.radius,c.shape.height/2.0,c.transform) + elif c.shape is BoxShape: + _init_buoyancy(c.shape.extents.x,c.shape.extents.y,c.shape.extents.z,c.transform) + else: + assert(false,"this collision shape isn't supported for buoyancy") + break + +func _init_buoyancy(x_rad,y_rad,z_rad,xform): + self.volume_half = x_rad * y_rad * z_rad * 4.0 + self.volume = volume_half * 2.0 + var aabb = [ + Vector3(-x_rad,-y_rad,-z_rad), + Vector3(-x_rad,-y_rad, z_rad), + Vector3(-x_rad, y_rad,-z_rad), + Vector3(-x_rad, y_rad, z_rad), + Vector3( x_rad,-y_rad,-z_rad), + Vector3( x_rad,-y_rad, z_rad), + Vector3( x_rad, y_rad,-z_rad), + Vector3( x_rad, y_rad, z_rad) + ] + for p in aabb: + self.bounding_box.push_back(xform * p) + self.center_of_mass = xform * center_of_mass + self.volume_up = (xform.basis * Vector3.UP).normalized() + +func _physics_process(_delta): + #check if points are submerged + #also weight them based on how deep they are to help get a weighted average center point later + var submerged = [] + var sub_weight = [] + var sub_total = 0.0 + for p in bounding_box: + var g_p = self.global_transform * p + var wave_height = _water.height(g_p) + var diff = g_p.y - wave_height + if diff < 0.0: + submerged.push_back(p) + sub_weight.push_back(diff) + sub_total += diff + #if no points are submerged, the whole thing is likely above water + #therefore, no buoyant force is applied + if submerged.size() == 0: + return + #if all points are submerged, the whole thing is likely below water + #so the buoyant force would be the weight of the water displaced by the entire volume + elif submerged.size() == 8: + add_central_force(inv_gravity * volume) + #if only some points are submerged, we need to estimate the amount of the volume displacing water + #the weight of that water is the buoyant force + else: + #we want to apply the buoyant force to the center of mass of the submerged part of the volume + #we can estimate it with a weighted average of the submerged points + var force_pnt = Vector3.ZERO + for p in range(submerged.size()): + force_pnt += submerged[p] * sub_weight[p] + force_pnt /= sub_total + #apply_force uses global rotation but local origin... + force_pnt = self.global_transform.basis * force_pnt + #to estimate the submerged part of the volume, + #we can see how deep into the water the bottom of an axis-aligned bounding box is + #and do an easing and lerp over the volume + var lowest = (self.global_transform * bounding_box[0]).y + for p in bounding_box: + var p_h = (self.global_transform * p).y + if p_h < lowest: + lowest = p_h + var center = self.global_transform * center_of_mass + var depth = _water.height(center) - lowest + #the water isn't a flat plane + #it's possible that some points are submerged, + #but the lowest point of the axis-aligned bounding box isn't actually under water + #in that case, for simplicity, + #just fall back to the old method of applying a small, constant force + #proportional to the number of submerged points + if depth <= 0.0: + add_force(inv_gravity * volume * POINT_VOLUME_WEIGHT * submerged.size(), force_pnt) + #lerp the volume against the depth of the axis-aligned bounding box + #but that lerp is only linear when the actual bounding box is axis-aligned + #for simplicity, we can lerp an easing curve based on how aligned the bounding box is + #on one end, it's linear and on the other it uses a basic exponential easing + #we want to do an inverse exponential ease for the upper half of the volume if it is also submerged + else: + var up = self.global_transform.basis * volume_up + var aligned = up.dot(Vector3.UP) + aligned = abs(aligned) + aligned -= 0.5 + aligned = abs(aligned) + aligned *= 2.0 + var easing_curve = lerp(4.8,1.0,aligned) + var breadth = (center.y - lowest) + depth = clamp(depth,0.0,breadth*2.0) + depth /= breadth + var v = clamp(depth,0.0,1.0) + v = ease(v,easing_curve) + var v2 = clamp(depth-1.0,0.0,1.0) + v2 = 1.0 - ease(1.0 - v2,easing_curve) + v += v2 + v = lerp(0.0,volume_half,v) + add_force(inv_gravity * v, force_pnt) |