Add water in a slightly hacky way
This commit is contained in:
parent
24ccf0ddbe
commit
d8c60fbf41
20
World.gd
Normal file
20
World.gd
Normal file
@ -0,0 +1,20 @@
|
||||
extends Spatial
|
||||
|
||||
|
||||
const ray_length = 2000
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
if Input.is_mouse_button_pressed(1):
|
||||
# If the left mouse button was pressed, cast a ray towards the mouse position
|
||||
var mouse_pos = get_viewport().get_mouse_position()
|
||||
|
||||
var camera = $LerpedFollow/LerpedCamera
|
||||
var from = camera.project_ray_origin(mouse_pos)
|
||||
var to = from + camera.project_ray_normal(mouse_pos) * ray_length
|
||||
|
||||
var result = get_world().direct_space_state.intersect_ray(from, to)
|
||||
|
||||
if not result.empty():
|
||||
# If there was a collision, set the depth of the water at that position
|
||||
$Water.set_depth_at_position(result.position, 0.0)
|
@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=23 format=2]
|
||||
[gd_scene load_steps=25 format=2]
|
||||
|
||||
[ext_resource path="res://Player.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://Planets.gd" type="Script" id=2]
|
||||
@ -11,6 +11,8 @@
|
||||
[ext_resource path="res://Resources/gas_planet_flowmap.png" type="Texture" id=9]
|
||||
[ext_resource path="res://Resources/gas_planet_base.png" type="Texture" id=10]
|
||||
[ext_resource path="res://LerpedFollow.gd" type="Script" id=11]
|
||||
[ext_resource path="res://addons/interactive_water/Water.tscn" type="PackedScene" id=12]
|
||||
[ext_resource path="res://World.gd" type="Script" id=13]
|
||||
|
||||
[sub_resource type="CubeMesh" id=1]
|
||||
size = Vector3( 10, 0.5, 10 )
|
||||
@ -101,6 +103,7 @@ glow_strength = 1.1
|
||||
glow_bloom = 0.1
|
||||
|
||||
[node name="World" type="Spatial"]
|
||||
script = ExtResource( 13 )
|
||||
|
||||
[node name="MovingPlatformPivot" type="Position3D" parent="."]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -125, 37, 60 )
|
||||
@ -204,3 +207,7 @@ target_paths = [ NodePath("../EnemyPos1"), NodePath("../EnemyPos2") ]
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource( 1 )]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 0 )
|
||||
|
||||
[node name="Water" parent="." instance=ExtResource( 12 )]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, -66, -50.5288 )
|
||||
plane_size = 200.0
|
||||
|
21
addons/interactive_water/PersistentShaderTexture.tscn
Normal file
21
addons/interactive_water/PersistentShaderTexture.tscn
Normal file
@ -0,0 +1,21 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/interactive_water/WaterHeights.gd" type="Script" id=1]
|
||||
|
||||
[node name="PersistentShaderTexture" type="Node"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Viewport" type="Viewport" parent="."]
|
||||
size = Vector2( 1, 1 )
|
||||
transparent_bg = true
|
||||
handle_input_locally = false
|
||||
hdr = false
|
||||
disable_3d = true
|
||||
usage = 0
|
||||
render_target_v_flip = true
|
||||
render_target_update_mode = 3
|
||||
|
||||
[node name="Texture" type="ColorRect" parent="Viewport"]
|
||||
margin_right = 128.0
|
||||
margin_bottom = 128.0
|
||||
rect_min_size = Vector2( 128, 128 )
|
71
addons/interactive_water/Water.gd
Normal file
71
addons/interactive_water/Water.gd
Normal file
@ -0,0 +1,71 @@
|
||||
extends Spatial
|
||||
|
||||
|
||||
# Saves every frame as a PNG in the project directory. Use for debugging (with caution)
|
||||
export var output_debug_textures := false
|
||||
export var first_output_frame := 0
|
||||
|
||||
export var plane_size := 2.0
|
||||
|
||||
var _frame_number := 0
|
||||
var _positions_to_set = []
|
||||
|
||||
|
||||
func set_depth_at_position(pos: Vector3, depth: float):
|
||||
# Transform the global position into 2D points on the water surface with values between 0 and 1
|
||||
pos -= translation
|
||||
|
||||
var position_2d = Vector2(pos.x, pos.z)
|
||||
|
||||
position_2d += Vector2(plane_size, plane_size) / 2.0
|
||||
position_2d /= plane_size
|
||||
|
||||
if position_2d.x < 1.0 and position_2d.y < 1.0 \
|
||||
and position_2d.x >= 0.0 and position_2d.y >= 0.0:
|
||||
_positions_to_set.append([position_2d, depth])
|
||||
|
||||
|
||||
func _ready():
|
||||
$WaterMesh.mesh.size = Vector2(plane_size, plane_size)
|
||||
|
||||
# Apply the scale and positioning to the collider
|
||||
$WaterMesh/StaticBody.scale.x = plane_size / 2.0
|
||||
$WaterMesh/StaticBody.scale.y = plane_size / 2.0
|
||||
$WaterMesh/StaticBody.scale.z = plane_size / 2.0
|
||||
|
||||
$WaterMesh/StaticBody.translation.y = 0.6 * 40.0
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
# Get result of previous frame
|
||||
var result = $WaterHeights.get_texture()
|
||||
|
||||
# Set it as the data of the water mesh
|
||||
$WaterMesh.material_override.set_shader_param("water_heights", result)
|
||||
|
||||
# Calculate a new frame. First, get the data of the last frame and modify it accordingly
|
||||
var image_data = result.get_data()
|
||||
|
||||
# Set outstanding pixels
|
||||
for position_and_depth in _positions_to_set:
|
||||
var pos = position_and_depth[0]
|
||||
var depth = position_and_depth[1]
|
||||
|
||||
image_data.lock()
|
||||
image_data.set_pixel(floor(pos.x * 512), floor(pos.y * 512), Color(depth, 0.0, 0.0, 0.0))
|
||||
image_data.unlock()
|
||||
|
||||
_positions_to_set.clear()
|
||||
|
||||
# Create an ImageTexture for this new frame
|
||||
var previous_frame = ImageTexture.new()
|
||||
previous_frame.create_from_image(image_data)
|
||||
|
||||
# Set the previous texture in the shader so that a new texture will be available next frame
|
||||
$WaterHeights.set_previous_texture(previous_frame)
|
||||
|
||||
# Debug output if needed
|
||||
if output_debug_textures and _frame_number > first_output_frame:
|
||||
image_data.save_png("res://debugframes/frame%s.png" % [_frame_number])
|
||||
|
||||
_frame_number += 1
|
18
addons/interactive_water/Water.shader
Normal file
18
addons/interactive_water/Water.shader
Normal file
@ -0,0 +1,18 @@
|
||||
shader_type spatial;
|
||||
|
||||
uniform sampler2D water_heights;
|
||||
|
||||
|
||||
float read_height(sampler2D tex, vec2 uv) {
|
||||
return texture(tex, uv).r + texture(tex, uv).g / 255.0;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(0.5, 0.7, 1.0) * (read_height(water_heights, UV) - 0.6) * 3.0;
|
||||
}
|
||||
|
||||
void vertex() {
|
||||
float vertex_distance = length(VERTEX) * 0.06;
|
||||
|
||||
VERTEX += NORMAL * (read_height(water_heights, UV) * 40.0 - pow(2, vertex_distance));
|
||||
}
|
38
addons/interactive_water/Water.tscn
Normal file
38
addons/interactive_water/Water.tscn
Normal file
@ -0,0 +1,38 @@
|
||||
[gd_scene load_steps=9 format=2]
|
||||
|
||||
[ext_resource path="res://addons/interactive_water/Water.shader" type="Shader" id=1]
|
||||
[ext_resource path="res://addons/interactive_water/Water.gd" type="Script" id=3]
|
||||
[ext_resource path="res://addons/interactive_water/WaterUpdateMaterial.tres" type="Material" id=4]
|
||||
[ext_resource path="res://addons/interactive_water/PersistentShaderTexture.tscn" type="PackedScene" id=5]
|
||||
|
||||
[sub_resource type="ViewportTexture" id=1]
|
||||
viewport_path = NodePath("WaterHeightViewport")
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=2]
|
||||
resource_local_to_scene = true
|
||||
shader = ExtResource( 1 )
|
||||
shader_param/water_heights = SubResource( 1 )
|
||||
|
||||
[sub_resource type="PlaneMesh" id=3]
|
||||
subdivide_width = 512
|
||||
subdivide_depth = 512
|
||||
|
||||
[sub_resource type="PlaneShape" id=4]
|
||||
|
||||
[node name="Water" type="Spatial"]
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="WaterMesh" type="MeshInstance" parent="."]
|
||||
material_override = SubResource( 2 )
|
||||
mesh = SubResource( 3 )
|
||||
material/0 = null
|
||||
|
||||
[node name="StaticBody" type="StaticBody" parent="WaterMesh"]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0 )
|
||||
|
||||
[node name="CollisionShape" type="CollisionShape" parent="WaterMesh/StaticBody"]
|
||||
shape = SubResource( 4 )
|
||||
|
||||
[node name="WaterHeights" parent="." instance=ExtResource( 5 )]
|
||||
size = Vector2( 512, 512 )
|
||||
shader_material = ExtResource( 4 )
|
30
addons/interactive_water/WaterHeights.gd
Normal file
30
addons/interactive_water/WaterHeights.gd
Normal file
@ -0,0 +1,30 @@
|
||||
extends Node
|
||||
|
||||
# A texture which is continuously (statefully) updated by a shader.
|
||||
#
|
||||
# Typical usage: Retrieve the current result with `get_texture` and, at the end of the frame,
|
||||
# re-insert that texture (updated if needed) with `set_previous_texture`.
|
||||
#
|
||||
# Note that the given shader needs to accept a `previous_frame` sampler2D. This represents the
|
||||
# result of the last frame which is used for generating the new result.
|
||||
|
||||
|
||||
export var size := Vector2(64, 64)
|
||||
export var shader_material: ShaderMaterial
|
||||
|
||||
|
||||
func _ready():
|
||||
$Viewport/Texture.rect_min_size = size
|
||||
$Viewport/Texture.rect_size = size
|
||||
$Viewport.size = size
|
||||
|
||||
assert(shader_material)
|
||||
$Viewport/Texture.material = shader_material
|
||||
|
||||
|
||||
func get_texture():
|
||||
return $Viewport.get_texture()
|
||||
|
||||
|
||||
func set_previous_texture(texture):
|
||||
$Viewport/Texture.material.set_shader_param("previous_frame", texture)
|
63
addons/interactive_water/WaterUpdate.shader
Normal file
63
addons/interactive_water/WaterUpdate.shader
Normal file
@ -0,0 +1,63 @@
|
||||
shader_type canvas_item;
|
||||
render_mode unshaded, blend_disabled;
|
||||
|
||||
uniform sampler2D previous_frame;
|
||||
|
||||
uniform float water_height = 0.6;
|
||||
|
||||
uniform float height_damping = 0.13;
|
||||
uniform float velocity_damping = 0.8;
|
||||
uniform float spread = 0.94;
|
||||
|
||||
// Height and Velocity are encoded in two components each, so RG is height and BA is velocity.
|
||||
// This is needed to get a workable accuracy.
|
||||
float read_height(sampler2D tex, vec2 uv) {
|
||||
return texture(tex, uv).r + texture(tex, uv).g / 255.0;
|
||||
}
|
||||
float read_velocity(sampler2D tex, vec2 uv) {
|
||||
return texture(tex, uv).b + texture(tex, uv).a / 255.0;
|
||||
}
|
||||
float get_encoded_remainder(float num) {
|
||||
return fract(num * 255.0);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// Read values here
|
||||
float height_here = read_height(previous_frame, UV);
|
||||
float velocity_here = read_velocity(previous_frame, UV);
|
||||
|
||||
// Apply force towards the base height
|
||||
// This follows from the damped harmonic oscillator equation F = -kx-bv
|
||||
float force = -height_damping * (height_here - water_height) - velocity_here * velocity_damping;
|
||||
float acceleration_here = force;
|
||||
|
||||
// In addition to each individual height behaving like a spring, neighbouring heights are
|
||||
// "connected by springs" as well:
|
||||
|
||||
// Read more samples
|
||||
float uv_mod = 1.0 / float(textureSize(previous_frame, 0).x);
|
||||
|
||||
float height_up = read_height(previous_frame, UV + vec2(0.0, uv_mod));
|
||||
float height_down = read_height(previous_frame, UV + vec2(0.0, -uv_mod));
|
||||
float height_left = read_height(previous_frame, UV + vec2(-uv_mod, 0.0));
|
||||
float height_right = read_height(previous_frame, UV + vec2(uv_mod, 0.0));
|
||||
|
||||
// Calculate differences
|
||||
float up_delta = spread * (height_up - height_here);
|
||||
float down_delta = spread * (height_down - height_here);
|
||||
float left_delta = spread * (height_left - height_here);
|
||||
float right_delta = spread * (height_right - height_here);
|
||||
|
||||
// Use the biggest delta to apply to this height
|
||||
float sum_delta = max(max(left_delta, right_delta), max(up_delta, down_delta));
|
||||
|
||||
// Apply velocity and height
|
||||
velocity_here += sum_delta + acceleration_here;
|
||||
height_here += velocity_here;
|
||||
|
||||
// Write to the texture
|
||||
COLOR = vec4(
|
||||
height_here, get_encoded_remainder(height_here),
|
||||
velocity_here, get_encoded_remainder(velocity_here)
|
||||
);
|
||||
}
|
10
addons/interactive_water/WaterUpdateMaterial.tres
Normal file
10
addons/interactive_water/WaterUpdateMaterial.tres
Normal file
@ -0,0 +1,10 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/interactive_water/WaterUpdate.shader" type="Shader" id=1]
|
||||
|
||||
[resource]
|
||||
shader = ExtResource( 1 )
|
||||
shader_param/water_height = 0.6
|
||||
shader_param/height_damping = 0.13
|
||||
shader_param/velocity_damping = 0.8
|
||||
shader_param/spread = 0.94
|
Loading…
x
Reference in New Issue
Block a user