Compare commits
No commits in common. "22b6023849baaff219f3ee2db22354b54abb5866" and "0f90dd61f263ed87600f6b9771ee9d948076f087" have entirely different histories.
22b6023849
...
0f90dd61f2
@ -6,14 +6,21 @@ var target := Vector3.ZERO
|
|||||||
var acceleration := Vector3.ZERO
|
var acceleration := Vector3.ZERO
|
||||||
var velocity := Vector3.ZERO
|
var velocity := Vector3.ZERO
|
||||||
|
|
||||||
|
export var acceleration_factor = 5.0
|
||||||
|
export var accelerating := false
|
||||||
|
|
||||||
export var coefficient_of_restitution := 0.5
|
export var coefficient_of_restitution := 0.5
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
|
if accelerating:
|
||||||
|
var direction = (target - global_transform.origin).normalized()
|
||||||
|
acceleration += direction * acceleration_factor * delta
|
||||||
|
velocity += acceleration
|
||||||
|
|
||||||
var collision = move_and_collide(velocity * delta)
|
var collision = move_and_collide(velocity * delta)
|
||||||
|
|
||||||
if collision:
|
if collision:
|
||||||
# Reflect the bullet and decrease the velocity according to the coefficient of restitution
|
|
||||||
var normal = collision.normal
|
var normal = collision.normal
|
||||||
velocity = coefficient_of_restitution * (velocity - 2 * velocity.dot(normal) * normal)
|
velocity = coefficient_of_restitution * (velocity - 2 * velocity.dot(normal) * normal)
|
||||||
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
extends Object
|
|
||||||
class_name DeadzoneInput
|
|
||||||
|
|
||||||
|
|
||||||
static func get_input(type, outer_deadzone, inner_deadzone, min_length = 0.0, normalized = true):
|
|
||||||
var input = Vector2(Input.get_action_strength(type + "_up") -
|
|
||||||
Input.get_action_strength(type + "_down"),
|
|
||||||
Input.get_action_strength(type + "_right") -
|
|
||||||
Input.get_action_strength(type + "_left"))
|
|
||||||
|
|
||||||
# Remove signs to reduce the number of cases
|
|
||||||
var signs = Vector2(sign(input.x), sign(input.y))
|
|
||||||
input = Vector2(abs(input.x), abs(input.y))
|
|
||||||
|
|
||||||
if input.length() < min_length:
|
|
||||||
return Vector2.ZERO
|
|
||||||
|
|
||||||
# Deazones for each axis
|
|
||||||
if input.x > outer_deadzone:
|
|
||||||
input.x = 1.0
|
|
||||||
elif input.x < inner_deadzone:
|
|
||||||
input.x = 0.0
|
|
||||||
else:
|
|
||||||
input.x = inverse_lerp(inner_deadzone, outer_deadzone, input.x)
|
|
||||||
|
|
||||||
if input.y > outer_deadzone:
|
|
||||||
input.y = 1.0
|
|
||||||
elif input.y < inner_deadzone:
|
|
||||||
input.y = 0.0
|
|
||||||
else:
|
|
||||||
input.y = inverse_lerp(inner_deadzone, outer_deadzone, input.y)
|
|
||||||
|
|
||||||
# Re-apply signs
|
|
||||||
input *= signs
|
|
||||||
|
|
||||||
# Limit at length 1
|
|
||||||
if normalized and input.length() > 1.0:
|
|
||||||
input /= input.length()
|
|
||||||
|
|
||||||
return input
|
|
39
Orbiter.gd
39
Orbiter.gd
@ -1,39 +0,0 @@
|
|||||||
extends KinematicBody
|
|
||||||
|
|
||||||
# Orbits the parent planet by velocity.
|
|
||||||
|
|
||||||
|
|
||||||
var time_passed := 0.0
|
|
||||||
var velocity := Vector3.ZERO
|
|
||||||
|
|
||||||
export(float) var move_speed_factor = 1.0
|
|
||||||
export(NodePath) var solar_system
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
var velocity_value = get_node(solar_system).get_orbit_velocity(global_transform.origin, get_parent())
|
|
||||||
|
|
||||||
# We need a vector perpendicular to the gravity acceleration to apply the velocity along.
|
|
||||||
# So we get the cross product of that and some arbitrary other vector, in this case Vector3.RIGHT
|
|
||||||
var gravity_acceleration = get_node(solar_system).get_closest_gravity_acceleration(global_transform.origin)
|
|
||||||
var other = Vector3.RIGHT
|
|
||||||
|
|
||||||
# Apply the velocity
|
|
||||||
velocity = gravity_acceleration.cross(other).normalized() * velocity_value
|
|
||||||
|
|
||||||
|
|
||||||
func _process(delta):
|
|
||||||
# Apply gravity as acceleration to velocity, then velocity to position
|
|
||||||
var gravity_acceleration = get_node(solar_system).get_closest_gravity_acceleration(global_transform.origin)
|
|
||||||
velocity += gravity_acceleration * delta
|
|
||||||
|
|
||||||
move_and_slide(velocity)
|
|
||||||
|
|
||||||
# Rotate down vector to face center of gravity -> tidally locked
|
|
||||||
var down = gravity_acceleration
|
|
||||||
var local_down = transform.basis * Vector3.DOWN
|
|
||||||
var angle = local_down.angle_to(down)
|
|
||||||
var axis = local_down.cross(down).normalized()
|
|
||||||
|
|
||||||
if axis != Vector3.ZERO: # Happens if we're perfectly aligned already (local_down and down are equal)
|
|
||||||
rotate(axis, angle)
|
|
16
Planets.gd
16
Planets.gd
@ -21,17 +21,14 @@ func get_closest_gravity_acceleration(position: Vector3) -> Vector3:
|
|||||||
var closest_planet_distance = INF
|
var closest_planet_distance = INF
|
||||||
var closest_force = 0.0
|
var closest_force = 0.0
|
||||||
|
|
||||||
# Iterate through all planets to find the closest one
|
|
||||||
for planet in get_children():
|
for planet in get_children():
|
||||||
var pos_to_center = (planet.transform.origin - position)
|
var pos_to_center = (planet.transform.origin - position)
|
||||||
var distance = pos_to_center.length()
|
var distance = pos_to_center.length()
|
||||||
|
|
||||||
if distance < closest_planet_distance:
|
if distance < closest_planet_distance:
|
||||||
# This planet is closer than the previous ones -> calculate and save the values
|
|
||||||
var force = _gravity(planet.mass * mass_multiplier, distance * distance_multiplier)
|
var force = _gravity(planet.mass * mass_multiplier, distance * distance_multiplier)
|
||||||
force *= gravity_multiplier
|
force *= gravity_multiplier
|
||||||
|
|
||||||
# Multiply by the normalized vector towards the center to give the force a direction
|
|
||||||
closest_force = (pos_to_center / distance) * force
|
closest_force = (pos_to_center / distance) * force
|
||||||
closest_planet_distance = distance
|
closest_planet_distance = distance
|
||||||
|
|
||||||
@ -42,7 +39,6 @@ func get_closest_gravity_acceleration(position: Vector3) -> Vector3:
|
|||||||
func get_gravity_acceleration(position: Vector3) -> Vector3:
|
func get_gravity_acceleration(position: Vector3) -> Vector3:
|
||||||
var total_force = Vector3.ZERO
|
var total_force = Vector3.ZERO
|
||||||
|
|
||||||
# Add each planet's gravity force to the total force
|
|
||||||
for planet in get_children():
|
for planet in get_children():
|
||||||
var pos_to_center = (planet.transform.origin - position)
|
var pos_to_center = (planet.transform.origin - position)
|
||||||
var distance = pos_to_center.length()
|
var distance = pos_to_center.length()
|
||||||
@ -55,18 +51,6 @@ func get_gravity_acceleration(position: Vector3) -> Vector3:
|
|||||||
return total_force
|
return total_force
|
||||||
|
|
||||||
|
|
||||||
# Return the velocity needed to orbit the given planet
|
|
||||||
func get_orbit_velocity(position: Vector3, planet):
|
|
||||||
var pos_to_center = (planet.global_transform.origin - position)
|
|
||||||
var distance = pos_to_center.length()
|
|
||||||
|
|
||||||
# raw_velocity is the velocity according to the formula, but we also need to account for the gravity_multiplier.
|
|
||||||
var raw_velocity = -sqrt((G * planet.mass * mass_multiplier) / (distance * distance_multiplier))
|
|
||||||
var gravity_scale_factor = sqrt(gravity_multiplier / distance_multiplier)
|
|
||||||
|
|
||||||
return raw_velocity * gravity_scale_factor
|
|
||||||
|
|
||||||
|
|
||||||
# Formula for gravity
|
# Formula for gravity
|
||||||
static func _gravity(mass, distance):
|
static func _gravity(mass, distance):
|
||||||
return (G * mass) / (distance * distance)
|
return (G * mass) / (distance * distance)
|
||||||
|
32
Player.gd
32
Player.gd
@ -44,7 +44,8 @@ func get_center():
|
|||||||
func apply_acceleration(acceleration):
|
func apply_acceleration(acceleration):
|
||||||
# First drag, then add the new acceleration
|
# First drag, then add the new acceleration
|
||||||
# For drag: Lerp towards the target velocity
|
# For drag: Lerp towards the target velocity
|
||||||
# This is usually 0, unless we're on something that's moving, in which case it is that object's velocity
|
# This is usually 0, unless we're on something that's moving, in which case it is that object's
|
||||||
|
# velocity
|
||||||
velocity = lerp(velocity, current_target_velocity, drag)
|
velocity = lerp(velocity, current_target_velocity, drag)
|
||||||
velocity += acceleration
|
velocity += acceleration
|
||||||
|
|
||||||
@ -60,7 +61,8 @@ func get_gravity_acceleration():
|
|||||||
var distance_to_collision = ($GroundCheckRay.get_collision_point()
|
var distance_to_collision = ($GroundCheckRay.get_collision_point()
|
||||||
- $GroundCheckRay.global_transform.origin).length()
|
- $GroundCheckRay.global_transform.origin).length()
|
||||||
|
|
||||||
# This factor is 0.0 if the player is exactly on the ground, and 1.0 if they're just barely almost grounded
|
# This factor is 0.0 if the player is exactly on the ground, and 1.0 if they're just barely
|
||||||
|
# almost grounded
|
||||||
var factor = inverse_lerp(0.0, almost_on_ground_length, distance_to_collision)
|
var factor = inverse_lerp(0.0, almost_on_ground_length, distance_to_collision)
|
||||||
|
|
||||||
return lerp(planet_gravity, total_gravity, factor)
|
return lerp(planet_gravity, total_gravity, factor)
|
||||||
@ -109,23 +111,24 @@ func _physics_process(delta):
|
|||||||
var move_velocity := Vector3.ZERO
|
var move_velocity := Vector3.ZERO
|
||||||
var move_acceleration := Vector3.ZERO
|
var move_acceleration := Vector3.ZERO
|
||||||
|
|
||||||
# Apply input
|
# Movement and rotation
|
||||||
var input = DeadzoneInput.get_input("move", 0.65, 0.2, 0.0, false)
|
if Input.is_action_pressed("move_up"):
|
||||||
|
move_acceleration.z -= move_accel
|
||||||
# Either do velocity-based movement or force-based movement
|
if Input.is_action_pressed("move_down"):
|
||||||
if Input.is_action_pressed("movement_modifier"):
|
move_acceleration.z += move_accel
|
||||||
move_velocity = transform.basis * Vector3.FORWARD * 15.0 * input.x
|
if Input.is_action_pressed("move_left"):
|
||||||
else:
|
rotate(transform.basis.y, delta * rotate_speed)
|
||||||
move_acceleration += transform.basis * Vector3.FORWARD * move_accel * input.x
|
if Input.is_action_pressed("move_right"):
|
||||||
|
rotate(transform.basis.y, -delta * rotate_speed)
|
||||||
rotate(transform.basis.y, delta * rotate_speed * -input.y)
|
|
||||||
|
|
||||||
# Make movement local
|
# Make movement local
|
||||||
move_acceleration = move_acceleration
|
move_acceleration = transform.basis * move_acceleration
|
||||||
|
|
||||||
# Get acceleration caused by gravity
|
# Get acceleration caused by gravity
|
||||||
var gravity_acceleration = get_gravity_acceleration()
|
var gravity_acceleration = get_gravity_acceleration()
|
||||||
|
|
||||||
|
# FIXME: Consider setting the gravity_acceleration to 0 if on ground
|
||||||
|
|
||||||
# Apply both acceleration types
|
# Apply both acceleration types
|
||||||
apply_acceleration((move_acceleration + gravity_acceleration) * delta)
|
apply_acceleration((move_acceleration + gravity_acceleration) * delta)
|
||||||
|
|
||||||
@ -147,8 +150,7 @@ func _physics_process(delta):
|
|||||||
reset_moving_platform_velocity()
|
reset_moving_platform_velocity()
|
||||||
|
|
||||||
# Apply movement to position
|
# Apply movement to position
|
||||||
# Add and subtract the move_velocity (used for velocity-based movement) because we do want to apply it, but not remember it for next frame
|
velocity = move_and_slide(velocity)
|
||||||
velocity = move_and_slide(velocity + move_velocity) - move_velocity
|
|
||||||
|
|
||||||
# Clamp the velocity just to be save
|
# Clamp the velocity just to be save
|
||||||
velocity.x = clamp(velocity.x, -MAX_VEL, MAX_VEL)
|
velocity.x = clamp(velocity.x, -MAX_VEL, MAX_VEL)
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
extends KinematicBody
|
extends KinematicBody
|
||||||
|
|
||||||
# An enemy which shoots at the future player position and moves between two targets within a given
|
|
||||||
# amount of time.
|
|
||||||
|
|
||||||
|
|
||||||
export(NodePath) var player_node
|
export(NodePath) var player_node
|
||||||
onready var player = get_node(player_node)
|
onready var player = get_node(player_node)
|
||||||
@ -10,83 +7,46 @@ onready var player = get_node(player_node)
|
|||||||
export(NodePath) var solar_system_node
|
export(NodePath) var solar_system_node
|
||||||
onready var solar_system = get_node(solar_system_node)
|
onready var solar_system = get_node(solar_system_node)
|
||||||
|
|
||||||
export(Array, NodePath) var target_paths
|
|
||||||
|
|
||||||
export var target_fly_time := 5.0
|
|
||||||
var time_passed_since_fly_start := 0.0
|
|
||||||
var current_target_index = 0
|
|
||||||
|
|
||||||
var bullet_scene = preload("res://Bullet.tscn")
|
var bullet_scene = preload("res://Bullet.tscn")
|
||||||
var bullet_velocity = 40.0
|
var bullet_velocity = 40.0
|
||||||
|
|
||||||
var shoot_target
|
var target
|
||||||
var velocity := Vector3.ZERO
|
var velocity := Vector3(0.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# Shoot everytime the timer times out
|
|
||||||
$ShotTimer.connect("timeout", self, "shoot_bullet")
|
$ShotTimer.connect("timeout", self, "shoot_bullet")
|
||||||
|
|
||||||
# Set up pathing: Start at the first target
|
|
||||||
global_transform.origin = get_node(target_paths[0]).global_transform.origin
|
|
||||||
_set_velocity_to_fly_towards_next_target()
|
|
||||||
|
|
||||||
|
|
||||||
# Picks the next target position from the `target_paths` list and sets the `velocity` appropriately
|
|
||||||
# so that the target will be reached after `target_fly_time`.
|
|
||||||
func _set_velocity_to_fly_towards_next_target():
|
|
||||||
current_target_index = (current_target_index + 1) % target_paths.size()
|
|
||||||
|
|
||||||
# Vector from here to the new target
|
|
||||||
var vector_to_next = (get_node(target_paths[current_target_index]).global_transform.origin
|
|
||||||
- global_transform.origin)
|
|
||||||
|
|
||||||
# s = v * t --> v = s / t
|
|
||||||
velocity = vector_to_next / target_fly_time
|
|
||||||
time_passed_since_fly_start = 0.0
|
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
# Project the player's position into the future
|
# Project the player's position into the future
|
||||||
shoot_target = _get_future_position(
|
target = _get_future_position(
|
||||||
player.get_center(),
|
player.get_center(),
|
||||||
player.velocity - velocity
|
player.velocity - velocity
|
||||||
)
|
)
|
||||||
|
|
||||||
# Look at the target that will be shot at, with the gravity as the down vector
|
if target:
|
||||||
if shoot_target:
|
|
||||||
var gravity = solar_system.get_gravity_acceleration(global_transform.origin)
|
var gravity = solar_system.get_gravity_acceleration(global_transform.origin)
|
||||||
look_at(shoot_target, gravity)
|
look_at(target, gravity)
|
||||||
|
|
||||||
# Have we rearched the target?
|
|
||||||
time_passed_since_fly_start += delta
|
|
||||||
if time_passed_since_fly_start >= target_fly_time:
|
|
||||||
# If so, fly towards the next target
|
|
||||||
_set_velocity_to_fly_towards_next_target()
|
|
||||||
|
|
||||||
# Apply velocity
|
|
||||||
global_transform.origin += velocity * delta
|
global_transform.origin += velocity * delta
|
||||||
|
|
||||||
|
|
||||||
func shoot_bullet():
|
func shoot_bullet():
|
||||||
if not shoot_target:
|
if not target:
|
||||||
# Player can't be hit right now, abort
|
# Player can't be hit right now, abort
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add a bullet
|
|
||||||
var instance = bullet_scene.instance()
|
var instance = bullet_scene.instance()
|
||||||
get_tree().get_root().add_child(instance)
|
get_tree().get_root().add_child(instance)
|
||||||
|
|
||||||
# Make the bullet start at the current position of this object and fly towards the target.
|
|
||||||
# The own velocity is added because of galilean relativity.
|
|
||||||
instance.global_transform.origin = global_transform.origin - global_transform.basis.z
|
instance.global_transform.origin = global_transform.origin - global_transform.basis.z
|
||||||
instance.velocity = velocity + (shoot_target - global_transform.origin).normalized() * bullet_velocity
|
instance.velocity = velocity + (target - global_transform.origin).normalized() * bullet_velocity
|
||||||
|
|
||||||
|
|
||||||
# Return the position at which a bullet fired forwards and with the velocity `bullet_velocity` would
|
|
||||||
# intersect with the object that is at `position` and moving with the given `velocity`
|
|
||||||
func _get_future_position(position, velocity):
|
func _get_future_position(position, velocity):
|
||||||
# Solution to the quadratic formula gives us the time at which the player would be hit
|
# Solution to the quadratic formula gives us the time at which the player would be hit
|
||||||
|
# TODO: Take acceleration into account as well!
|
||||||
var a = pow(velocity.x, 2) + pow(velocity.y, 2) + pow(velocity.z, 2) - pow(bullet_velocity, 2)
|
var a = pow(velocity.x, 2) + pow(velocity.y, 2) + pow(velocity.z, 2) - pow(bullet_velocity, 2)
|
||||||
var b = 2 * (velocity.x * (position.x - global_transform.origin.x)
|
var b = 2 * (velocity.x * (position.x - global_transform.origin.x)
|
||||||
+ velocity.y * (position.y - global_transform.origin.y)
|
+ velocity.y * (position.y - global_transform.origin.y)
|
||||||
@ -107,5 +67,4 @@ func _get_future_position(position, velocity):
|
|||||||
var t = min(t1, t2)
|
var t = min(t1, t2)
|
||||||
if t < 0: t = max(t1, t2)
|
if t < 0: t = max(t1, t2)
|
||||||
|
|
||||||
# Return the position given by the time we calculated
|
|
||||||
return position + t * velocity
|
return position + t * velocity
|
||||||
|
26
World.gd
26
World.gd
@ -1,26 +0,0 @@
|
|||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
# Create some initial waves
|
|
||||||
for i in range(0, 11):
|
|
||||||
$Water.set_depth_at_position(Vector3(-50 + i * 10, -66, -50), 0.0)
|
|
61
World.tscn
61
World.tscn
File diff suppressed because one or more lines are too long
@ -1,21 +0,0 @@
|
|||||||
[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 )
|
|
@ -1,72 +0,0 @@
|
|||||||
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 (after some frames, since the texture isn't initialized until then)
|
|
||||||
if _frame_number >= 60:
|
|
||||||
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
|
|
@ -1,18 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
[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 )
|
|
@ -1,30 +0,0 @@
|
|||||||
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)
|
|
@ -1,63 +0,0 @@
|
|||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
[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
|
|
@ -9,11 +9,6 @@
|
|||||||
config_version=4
|
config_version=4
|
||||||
|
|
||||||
_global_script_classes=[ {
|
_global_script_classes=[ {
|
||||||
"base": "Object",
|
|
||||||
"class": "DeadzoneInput",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://DeadzoneInput.gd"
|
|
||||||
}, {
|
|
||||||
"base": "KinematicBody",
|
"base": "KinematicBody",
|
||||||
"class": "MovingPlatform",
|
"class": "MovingPlatform",
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
@ -30,7 +25,6 @@ _global_script_classes=[ {
|
|||||||
"path": "res://Planets.gd"
|
"path": "res://Planets.gd"
|
||||||
} ]
|
} ]
|
||||||
_global_script_class_icons={
|
_global_script_class_icons={
|
||||||
"DeadzoneInput": "",
|
|
||||||
"MovingPlatform": "",
|
"MovingPlatform": "",
|
||||||
"Planet": "",
|
"Planet": "",
|
||||||
"SolarSystem": ""
|
"SolarSystem": ""
|
||||||
@ -69,38 +63,28 @@ ui_down={
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
move_up={
|
move_up={
|
||||||
"deadzone": 0.0,
|
"deadzone": 0.5,
|
||||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null)
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null)
|
||||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
move_down={
|
move_down={
|
||||||
"deadzone": 0.0,
|
"deadzone": 0.5,
|
||||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null)
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null)
|
||||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
move_left={
|
move_left={
|
||||||
"deadzone": 0.0,
|
"deadzone": 0.5,
|
||||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null)
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null)
|
||||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
move_right={
|
move_right={
|
||||||
"deadzone": 0.0,
|
"deadzone": 0.5,
|
||||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null)
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null)
|
||||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
jump={
|
jump={
|
||||||
"deadzone": 0.5,
|
"deadzone": 0.5,
|
||||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"unicode":0,"echo":false,"script":null)
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"unicode":0,"echo":false,"script":null)
|
||||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
movement_modifier={
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [ Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":6,"pressure":0.0,"pressed":false,"script":null)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user