588 lines
20 KiB
GDScript
588 lines
20 KiB
GDScript
@tool
|
|
class_name DynamicPage
|
|
extends Polygon2D
|
|
|
|
# ==============================================================================
|
|
# ENUMS & CONSTANTS
|
|
# ==============================================================================
|
|
|
|
## Defines the material physical properties for the page simulation.
|
|
enum PagePreset {
|
|
CUSTOM,
|
|
DEFAULT,
|
|
STANDARD_PAPER,
|
|
HEAVY_GRIMOIRE,
|
|
LIGHT_MAGAZINE,
|
|
OLD_SCROLL,
|
|
RIGID_BOARD,
|
|
WET_CLOTH,
|
|
PLASTIC_SHEET,
|
|
METAL_PLATE
|
|
}
|
|
|
|
## Determines the direction of the page turn curve.
|
|
enum CurlMode {
|
|
STRAIGHT,
|
|
TOP_CORNER_FIRST,
|
|
BOTTOM_CORNER_FIRST
|
|
}
|
|
|
|
const SKELETON_NAME = "AutoSkeleton"
|
|
const SHADOW_NAME = "AutoShadow"
|
|
|
|
|
|
# ==============================================================================
|
|
# SIGNALS
|
|
# ==============================================================================
|
|
|
|
signal change_page_requested
|
|
signal end_animation
|
|
|
|
|
|
# ==============================================================================
|
|
# INTERNAL CACHE
|
|
# ==============================================================================
|
|
|
|
var _custom_cache: Dictionary = {}
|
|
|
|
|
|
# ==============================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================
|
|
|
|
@export_category("Rig Generator")
|
|
|
|
## Horizontal mesh subdivision. Higher values create smoother bends.
|
|
@export_range(1, 20) var subdivision_x: int = 8
|
|
## Vertical mesh subdivision. Higher values allow for better corner curling.
|
|
@export_range(1, 10) var subdivision_y: int = 5
|
|
## Generates the mesh, skeleton, weights and animations.
|
|
@export var rebuild_all: bool = false : set = _on_generate_pressed
|
|
|
|
|
|
@export_category("Animation Generator")
|
|
|
|
@export_group("Quick Presets")
|
|
## Select a material style to auto-configure physics.
|
|
@export var animation_preset: PagePreset = PagePreset.CUSTOM : set = _on_preset_changed
|
|
|
|
|
|
@export_group("Manual Configuration")
|
|
## The AnimationPlayer node to store the generated animations.
|
|
@export var anim_player: AnimationPlayer
|
|
## Duration of the page turn animation in seconds.
|
|
@export var anim_duration: float = 0.75
|
|
|
|
|
|
@export_subgroup("Paper Physics")
|
|
## Stiffness of the material. 0.5 = Rubber, 5.0 = Wood.
|
|
@export_range(0.5, 5.0, 0.1) var paper_stiffness: float = 2.0
|
|
## Bend angle during the lift phase (Negative = Tip drags down).
|
|
@export_range(-180.0, 180.0) var lift_bend: float = -10.0
|
|
## Bend angle during the landing phase (Negative = Tip floats up).
|
|
@export_range(-180.0, 180.0) var land_bend: float = -8.0
|
|
|
|
|
|
@export_subgroup("Curl Effect")
|
|
## Which part of the page initiates the movement.
|
|
@export var curl_mode: CurlMode = CurlMode.TOP_CORNER_FIRST
|
|
## Time delay for the trailing corner (0.0 = None, 1.0 = High lag).
|
|
@export_range(0.0, 1.0, 0.05) var curl_lag: float = 0.8
|
|
|
|
|
|
@export_subgroup("Shadow FX")
|
|
## Create a dynamic shadow behind the page (Background Shadow).
|
|
@export var enable_shadow: bool = false
|
|
## The gradient texture used for the back shadow.
|
|
@export var shadow_gradient: GradientTexture2D
|
|
|
|
|
|
@export_subgroup("Fine Timing")
|
|
## Normalized time (0.0 - 1.0) for the peak lift position.
|
|
@export_range(0.05, 0.45, 0.01) var timing_peak_lift: float = 0.15
|
|
## Normalized time (0.0 - 1.0) for the landing contact.
|
|
@export_range(0.55, 0.95, 0.01) var timing_peak_land: float = 0.85
|
|
|
|
@export var generate_anims_btn: bool = false : set = _on_anim_pressed
|
|
|
|
|
|
# ==============================================================================
|
|
# RUNTIME INITIALIZATION
|
|
# ==============================================================================
|
|
|
|
func _ready():
|
|
self.z_index = 10
|
|
if animation_preset == PagePreset.CUSTOM:
|
|
_save_state_to_cache()
|
|
|
|
|
|
func rebuild(current_page_size: Vector2 = Vector2.ZERO) -> void:
|
|
if not anim_player: return
|
|
_clean_previous_rig()
|
|
_create_rig_logic(current_page_size)
|
|
_generate_animations_logic()
|
|
|
|
|
|
# ==============================================================================
|
|
# PRESET LOGIC
|
|
# ==============================================================================
|
|
|
|
func _on_preset_changed(val):
|
|
if animation_preset == PagePreset.CUSTOM:
|
|
_save_state_to_cache()
|
|
|
|
animation_preset = val
|
|
|
|
if val == PagePreset.CUSTOM:
|
|
if not _custom_cache.is_empty():
|
|
_load_state_from_cache()
|
|
print("[PageRigger] Restored Custom Settings.")
|
|
notify_property_list_changed()
|
|
return
|
|
|
|
# Defaults
|
|
curl_mode = CurlMode.BOTTOM_CORNER_FIRST
|
|
curl_lag = 0.3
|
|
enable_shadow = false
|
|
anim_duration = 0.75
|
|
|
|
match val:
|
|
PagePreset.DEFAULT:
|
|
paper_stiffness = 2.0; lift_bend = -15.0; land_bend = 5.0
|
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.5
|
|
anim_duration = 0.75; timing_peak_lift = 0.15; timing_peak_land = 0.85
|
|
|
|
PagePreset.STANDARD_PAPER:
|
|
paper_stiffness = 2.5; lift_bend = -12.0; land_bend = 3.0
|
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.4
|
|
anim_duration = 0.65; timing_peak_lift = 0.12; timing_peak_land = 0.88
|
|
|
|
PagePreset.HEAVY_GRIMOIRE:
|
|
paper_stiffness = 4.0; lift_bend = -3.0; land_bend = 1.0
|
|
curl_mode = CurlMode.STRAIGHT; curl_lag = 0.1
|
|
anim_duration = 1.1; timing_peak_lift = 0.25; timing_peak_land = 0.75
|
|
|
|
PagePreset.LIGHT_MAGAZINE:
|
|
paper_stiffness = 0.8; lift_bend = -16.0; land_bend = 0.0
|
|
curl_mode = CurlMode.BOTTOM_CORNER_FIRST; curl_lag = 0.6
|
|
anim_duration = 0.8; timing_peak_lift = 0.10; timing_peak_land = 0.92
|
|
|
|
PagePreset.OLD_SCROLL:
|
|
paper_stiffness = 1.8; lift_bend = -20.0; land_bend = 8.0
|
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.9
|
|
anim_duration = 0.9; timing_peak_lift = 0.18; timing_peak_land = 0.82
|
|
|
|
PagePreset.RIGID_BOARD:
|
|
paper_stiffness = 5.0; lift_bend = 0.0; land_bend = 0.0
|
|
curl_mode = CurlMode.STRAIGHT; curl_lag = 0.0
|
|
anim_duration = 0.8; timing_peak_lift = 0.2; timing_peak_land = 0.8
|
|
|
|
PagePreset.WET_CLOTH:
|
|
paper_stiffness = 0.3; lift_bend = -28.0; land_bend = -0.0
|
|
curl_mode = CurlMode.BOTTOM_CORNER_FIRST; curl_lag = 0.6
|
|
anim_duration = 1.3; timing_peak_lift = 0.2; timing_peak_land = 0.95
|
|
|
|
PagePreset.PLASTIC_SHEET:
|
|
paper_stiffness = 3.5; lift_bend = -12.0; land_bend = 12.0
|
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.2
|
|
anim_duration = 0.5; timing_peak_lift = 0.08; timing_peak_land = 0.92
|
|
|
|
PagePreset.METAL_PLATE:
|
|
paper_stiffness = 5.0; lift_bend = 0.0; land_bend = 0.0
|
|
curl_mode = CurlMode.STRAIGHT; curl_lag = 0.0
|
|
anim_duration = 1.4; timing_peak_lift = 0.15; timing_peak_land = 0.98
|
|
|
|
notify_property_list_changed()
|
|
if anim_player and skeleton != NodePath(""):
|
|
_generate_animations_logic()
|
|
print("[PageRigger] Preset Applied: ", PagePreset.keys()[val])
|
|
|
|
|
|
func _save_state_to_cache():
|
|
_custom_cache = {
|
|
"stiffness": paper_stiffness, "lift": lift_bend, "land": land_bend,
|
|
"curl_m": curl_mode, "curl_l": curl_lag,
|
|
"t_lift": timing_peak_lift, "t_land": timing_peak_land,
|
|
"dur": anim_duration, "shadow": enable_shadow
|
|
}
|
|
|
|
|
|
func _load_state_from_cache():
|
|
paper_stiffness = _custom_cache.get("stiffness", 1.5)
|
|
lift_bend = _custom_cache.get("lift", -30.0)
|
|
land_bend = _custom_cache.get("land", -15.0)
|
|
curl_mode = _custom_cache.get("curl_m", CurlMode.BOTTOM_CORNER_FIRST)
|
|
curl_lag = _custom_cache.get("curl_l", 0.3)
|
|
timing_peak_lift = _custom_cache.get("t_lift", 0.15)
|
|
timing_peak_land = _custom_cache.get("t_land", 0.85)
|
|
anim_duration = _custom_cache.get("dur", 1.0)
|
|
enable_shadow = false # Force disable shadow
|
|
|
|
|
|
# ==============================================================================
|
|
# RIGGING LOGIC
|
|
# ==============================================================================
|
|
|
|
func _calculate_polygon_rect() -> Rect2:
|
|
if polygon.size() == 0:
|
|
return Rect2(0,0,0,0)
|
|
|
|
var min_v = polygon[0]
|
|
var max_v = polygon[0]
|
|
|
|
for v in polygon:
|
|
min_v.x = min(min_v.x, v.x)
|
|
min_v.y = min(min_v.y, v.y)
|
|
max_v.x = max(max_v.x, v.x)
|
|
max_v.y = max(max_v.y, v.y)
|
|
|
|
return Rect2(min_v, max_v - min_v)
|
|
|
|
|
|
func _create_rig_logic(current_page_size: Vector2 = Vector2.ZERO):
|
|
self.z_index = 10
|
|
|
|
var original_size = Vector2(512, 820)
|
|
var tex_size: Vector2 = Vector2.ZERO
|
|
if current_page_size != Vector2.ZERO:
|
|
tex_size = current_page_size
|
|
elif texture:
|
|
tex_size = texture.get_size()
|
|
if tex_size == Vector2.ZERO:
|
|
tex_size = original_size
|
|
|
|
var step_x = tex_size.x / subdivision_x
|
|
var step_y = tex_size.y / subdivision_y
|
|
|
|
# Prepare Gradient Texture for Background Shadow
|
|
var applied_shadow_tex: Texture2D = null
|
|
if not shadow_gradient:
|
|
shadow_gradient = preload("uid://rm7bporlv3cf")
|
|
var dup = shadow_gradient.duplicate()
|
|
if dup is GradientTexture2D:
|
|
dup.width = int(tex_size.x)
|
|
applied_shadow_tex = dup
|
|
|
|
# --- 0. BACKGROUND SHADOW ---
|
|
if enable_shadow:
|
|
var shadow = Polygon2D.new()
|
|
shadow.name = SHADOW_NAME
|
|
shadow.z_index = -1
|
|
|
|
if applied_shadow_tex:
|
|
shadow.texture = applied_shadow_tex
|
|
shadow.color = Color.WHITE
|
|
else:
|
|
shadow.color = Color(0, 0, 0, 0.5)
|
|
|
|
var margin = 4.0
|
|
shadow.polygon = PackedVector2Array([
|
|
Vector2(margin, margin), Vector2(tex_size.x - margin, margin),
|
|
Vector2(tex_size.x - margin, tex_size.y - margin), Vector2(margin, tex_size.y - margin)
|
|
])
|
|
shadow.uv = shadow.polygon
|
|
add_child(shadow)
|
|
|
|
# --- 1. MESH & UV ---
|
|
var new_uvs = PackedVector2Array()
|
|
for y in range(subdivision_y + 1):
|
|
for x in range(subdivision_x + 1):
|
|
new_uvs.append(Vector2(x * step_x, y * step_y))
|
|
self.uv = new_uvs
|
|
self.polygon = new_uvs
|
|
|
|
var new_polygons = []
|
|
var rc = subdivision_x + 1
|
|
for y in range(subdivision_y):
|
|
for x in range(subdivision_x):
|
|
var i = y * rc + x
|
|
new_polygons.append(PackedInt32Array([i, i + 1, i + rc + 1, i + rc]))
|
|
self.polygons = new_polygons
|
|
|
|
# --- 2. SKELETON ---
|
|
var sk = Skeleton2D.new()
|
|
sk.name = SKELETON_NAME
|
|
add_child(sk)
|
|
self.skeleton = NodePath(SKELETON_NAME)
|
|
|
|
# --- 3. BONES (Structured Hierarchy) ---
|
|
for y in range(subdivision_y + 1):
|
|
var parent_bone: Bone2D = null
|
|
|
|
for x in range(subdivision_x + 1):
|
|
var b = Bone2D.new()
|
|
b.name = "Bone_%d_%d" % [x, y]
|
|
b.set_autocalculate_length_and_angle(false)
|
|
b.set_length(step_x)
|
|
|
|
if x == 0:
|
|
# Root bone (Spine)
|
|
sk.add_child(b)
|
|
b.position = Vector2(0, y * step_y)
|
|
else:
|
|
# Child bone
|
|
if parent_bone:
|
|
parent_bone.add_child(b)
|
|
b.position = Vector2(step_x, 0)
|
|
|
|
b.set_rest(b.transform)
|
|
parent_bone = b
|
|
|
|
# --- 4. WEIGHTS (Gradient Skinning) ---
|
|
_apply_weights_to_polygon(self, sk)
|
|
|
|
queue_redraw()
|
|
notify_property_list_changed()
|
|
|
|
|
|
func _apply_weights_to_polygon(poly: Polygon2D, sk: Skeleton2D):
|
|
poly.clear_bones()
|
|
|
|
var bone_map = {}
|
|
for y in range(subdivision_y + 1):
|
|
for x in range(subdivision_x + 1):
|
|
var bname = "Bone_%d_%d" % [x, y]
|
|
var bone_node = sk.find_child(bname, true, false)
|
|
if bone_node:
|
|
bone_map[bname] = bone_node.get_path()
|
|
|
|
var vertex_count = poly.polygon.size()
|
|
|
|
var weights_per_bone = {}
|
|
|
|
for bpath in bone_map.values():
|
|
var w_arr = PackedFloat32Array()
|
|
w_arr.resize(vertex_count)
|
|
w_arr.fill(0.0)
|
|
weights_per_bone[bpath] = w_arr
|
|
|
|
var vert_idx = 0
|
|
for y in range(subdivision_y + 1):
|
|
for x in range(subdivision_x + 1):
|
|
var primary_name = "Bone_%d_%d" % [x, y]
|
|
var prev_name = "Bone_%d_%d" % [x - 1, y]
|
|
var next_name = "Bone_%d_%d" % [x + 1, y]
|
|
|
|
var primary_path = bone_map.get(primary_name)
|
|
var prev_path = bone_map.get(prev_name)
|
|
var next_path = bone_map.get(next_name)
|
|
|
|
var w_center = 0.7
|
|
var w_neighbor = 0.15
|
|
|
|
if primary_path: weights_per_bone[primary_path][vert_idx] += w_center
|
|
if prev_path: weights_per_bone[prev_path][vert_idx] += w_neighbor
|
|
else: weights_per_bone[primary_path][vert_idx] += w_neighbor
|
|
if next_path: weights_per_bone[next_path][vert_idx] += w_neighbor
|
|
else: weights_per_bone[primary_path][vert_idx] += w_neighbor
|
|
|
|
vert_idx += 1
|
|
|
|
for bpath in weights_per_bone:
|
|
poly.add_bone(bpath, weights_per_bone[bpath])
|
|
|
|
|
|
# ==============================================================================
|
|
# ANIMATION LOGIC
|
|
# ==============================================================================
|
|
|
|
func _generate_animations_logic():
|
|
var library: AnimationLibrary
|
|
if anim_player.has_animation_library(""): library = anim_player.get_animation_library("")
|
|
else: library = AnimationLibrary.new(); anim_player.add_animation_library("", library)
|
|
|
|
_create_single_anim(library, "turn_flexible_page", false, false)
|
|
_create_single_anim(library, "turn_rigid_page", true, false)
|
|
_create_single_anim(library, "turn_flexible_page_mirror", false, true)
|
|
_create_single_anim(library, "turn_rigid_page_mirror", true, true)
|
|
notify_property_list_changed()
|
|
|
|
|
|
func _create_single_anim(library: AnimationLibrary, anim_name: String, is_rigid: bool, is_mirror: bool):
|
|
var anim = Animation.new()
|
|
anim.length = anim_duration
|
|
anim.step = 0.01
|
|
|
|
if library.has_animation(anim_name): library.remove_animation(anim_name)
|
|
library.add_animation(anim_name, anim)
|
|
|
|
var sk_node = find_child(SKELETON_NAME, true, false)
|
|
if not sk_node: return
|
|
|
|
var my_path = anim_player.get_node(anim_player.root_node).get_path_to(self)
|
|
|
|
var visible_col = Color(1, 1, 1, 1)
|
|
var invisible_col = Color(1, 1, 1, 0)
|
|
var faded_col = Color(1, 1, 1, 0.6)
|
|
|
|
var t_mid = anim_duration * 0.5
|
|
var t_end_snap = anim_duration * 0.95
|
|
|
|
# ==========================================================================
|
|
# 1. VISIBILITY & Z-INDEX
|
|
# ==========================================================================
|
|
var z_track = anim.add_track(Animation.TYPE_VALUE)
|
|
anim.track_set_path(z_track, str(my_path) + ":z_index")
|
|
anim.track_set_interpolation_type(z_track, Animation.INTERPOLATION_NEAREST)
|
|
anim.track_insert_key(z_track, 0.0, 10)
|
|
anim.track_insert_key(z_track, anim_duration * 0.15, 25)
|
|
anim.track_insert_key(z_track, anim_duration * 0.65, 10)
|
|
|
|
# ==========================================================================
|
|
# 2. BACKGROUND SHADOW
|
|
# ==========================================================================
|
|
var shadow_node = find_child(SHADOW_NAME, true, false)
|
|
if shadow_node and enable_shadow:
|
|
var shadow_path = anim_player.get_node(anim_player.root_node).get_path_to(shadow_node)
|
|
var t_scale = anim.add_track(Animation.TYPE_VALUE)
|
|
anim.track_set_path(t_scale, str(shadow_path) + ":scale")
|
|
anim.track_set_interpolation_type(t_scale, Animation.INTERPOLATION_CUBIC)
|
|
var t_mod = anim.add_track(Animation.TYPE_VALUE)
|
|
anim.track_set_path(t_mod, str(shadow_path) + ":modulate")
|
|
|
|
var s_start = Vector2(-1.0, 1.0) if is_mirror else Vector2(1.0, 1.0)
|
|
var s_end = Vector2(1.0, 1.0) if is_mirror else Vector2(-1.0, 1.0)
|
|
|
|
anim.track_insert_key(t_scale, 0.0, s_start)
|
|
anim.track_insert_key(t_mod, 0.0, invisible_col)
|
|
anim.track_insert_key(t_mod, anim_duration * 0.1, visible_col)
|
|
|
|
anim.track_insert_key(t_scale, t_mid, Vector2(0.01, 1.0))
|
|
anim.track_insert_key(t_mod, t_mid, faded_col)
|
|
|
|
anim.track_insert_key(t_scale, t_end_snap, s_end)
|
|
anim.track_insert_key(t_scale, anim_duration, s_end)
|
|
anim.track_insert_key(t_mod, t_end_snap, invisible_col)
|
|
anim.track_insert_key(t_mod, anim_duration, invisible_col)
|
|
|
|
# ==========================================================================
|
|
# 3. BONE ANIMATION
|
|
# ==========================================================================
|
|
for y in range(subdivision_y + 1):
|
|
var row_factor = 1.0
|
|
if not is_rigid and curl_mode != CurlMode.STRAIGHT:
|
|
var y_ratio = float(y) / float(max(1, subdivision_y))
|
|
if curl_mode == CurlMode.TOP_CORNER_FIRST:
|
|
row_factor = lerp(1.0, 1.0 - curl_lag, y_ratio)
|
|
elif curl_mode == CurlMode.BOTTOM_CORNER_FIRST:
|
|
row_factor = lerp(1.0 - curl_lag, 1.0, y_ratio)
|
|
|
|
var time_offset = (1.0 - row_factor) * (anim_duration * 0.1)
|
|
|
|
for x in range(subdivision_x + 1):
|
|
var bone_name = "Bone_%d_%d" % [x, y]
|
|
var bone = sk_node.find_child(bone_name, true, false)
|
|
if not bone: continue
|
|
|
|
var t_idx = anim.add_track(Animation.TYPE_VALUE)
|
|
var path = anim_player.get_node(anim_player.root_node).get_path_to(bone)
|
|
anim.track_set_path(t_idx, str(path) + ":rotation_degrees")
|
|
anim.track_set_interpolation_type(t_idx, Animation.INTERPOLATION_CUBIC)
|
|
|
|
var deg_flat_right = 0.0; var deg_flat_left = -179.9; var deg_mid = -90.0
|
|
|
|
if is_rigid:
|
|
if x == 0:
|
|
var s = deg_flat_left if is_mirror else deg_flat_right
|
|
var e = deg_flat_right if is_mirror else deg_flat_left
|
|
anim.track_insert_key(t_idx, 0.0, s)
|
|
anim.track_insert_key(t_idx, anim_duration * 0.5, deg_mid)
|
|
anim.track_insert_key(t_idx, anim_duration, e)
|
|
else:
|
|
anim.track_insert_key(t_idx, 0.0, 0.0)
|
|
anim.track_insert_key(t_idx, anim_duration, 0.0)
|
|
else:
|
|
var x_ratio = float(x) / float(subdivision_x)
|
|
var influence = pow(x_ratio, paper_stiffness)
|
|
var t_lift = clamp((anim_duration * timing_peak_lift) + time_offset, 0.0, (anim_duration * 0.5) - 0.05)
|
|
var t_mid_bone = anim_duration * 0.5
|
|
var t_land = clamp((anim_duration * timing_peak_land) - time_offset, t_mid_bone + 0.05, anim_duration)
|
|
|
|
if x == 0:
|
|
# ROOT BONE
|
|
var s = deg_flat_left if is_mirror else deg_flat_right
|
|
var e = deg_flat_right if is_mirror else deg_flat_left
|
|
var sl = -15.0 * row_factor
|
|
if is_mirror: sl = deg_flat_left - sl
|
|
var slt = lerp(-90.0, -180.0, timing_peak_land)
|
|
if is_mirror: slt = lerp(-90.0, 0.0, timing_peak_land)
|
|
|
|
anim.track_insert_key(t_idx, 0.0, s)
|
|
anim.track_insert_key(t_idx, t_lift, sl)
|
|
anim.track_insert_key(t_idx, t_mid_bone, deg_mid)
|
|
anim.track_insert_key(t_idx, t_land, slt)
|
|
|
|
var t_settle = lerp(t_land, anim_duration, 0.7)
|
|
var s_settle = lerp(slt, e, 0.85)
|
|
anim.track_insert_key(t_idx, t_settle, s_settle)
|
|
|
|
anim.track_insert_key(t_idx, anim_duration, e)
|
|
else:
|
|
# SURFACE BONES (CURVED)
|
|
var bm = -1.0 if is_mirror else 1.0
|
|
|
|
anim.track_insert_key(t_idx, 0.0, 0.0)
|
|
anim.track_insert_key(t_idx, t_lift, lift_bend * row_factor * bm * influence)
|
|
# REMOVED FORCED 0.0 AT MIDPOINT FOR ORGANIC CURVE
|
|
|
|
var val_land = land_bend * row_factor * bm * influence
|
|
anim.track_insert_key(t_idx, t_land, val_land)
|
|
|
|
var t_settle = lerp(t_land, anim_duration, 0.75)
|
|
anim.track_insert_key(t_idx, t_settle, val_land * 0.15)
|
|
|
|
anim.track_insert_key(t_idx, anim_duration, 0.0)
|
|
|
|
# ==========================================================================
|
|
# 4. MAIN POLYGON SCALE ("SQUASH & STRETCH")
|
|
# ==========================================================================
|
|
if not is_rigid:
|
|
var t_main_scale = anim.add_track(Animation.TYPE_VALUE)
|
|
anim.track_set_path(t_main_scale, str(my_path) + ":scale")
|
|
anim.track_set_interpolation_type(t_main_scale, Animation.INTERPOLATION_LINEAR)
|
|
var squash_x = 0.88
|
|
|
|
anim.track_insert_key(t_main_scale, 0.0, Vector2(1, 1))
|
|
anim.track_insert_key(t_main_scale, t_mid, Vector2(1, 1))
|
|
anim.track_insert_key(t_main_scale, t_mid + (anim_duration * 0.1), Vector2(squash_x, 1))
|
|
anim.track_insert_key(t_main_scale, anim_duration * 0.85, Vector2(1, 1))
|
|
anim.track_insert_key(t_main_scale, anim_duration, Vector2(1, 1))
|
|
|
|
var method_track = anim.add_track(Animation.TYPE_METHOD)
|
|
anim.track_set_path(method_track, str(my_path))
|
|
anim.track_insert_key(method_track, anim_duration * 0.5, {"method": "_trigger_midpoint", "args": []})
|
|
anim.track_insert_key(method_track, anim_duration, {"method": "_trigger_end", "args": []})
|
|
|
|
|
|
# ==============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ==============================================================================
|
|
|
|
func _trigger_midpoint(): emit_signal("change_page_requested")
|
|
func _trigger_end(): emit_signal("end_animation")
|
|
|
|
|
|
func _on_generate_pressed(val):
|
|
if val:
|
|
rebuild_all=false
|
|
_clean_previous_rig()
|
|
_create_rig_logic()
|
|
_generate_animations_logic()
|
|
|
|
|
|
func _on_anim_pressed(val):
|
|
if val:
|
|
generate_anims_btn=false
|
|
if anim_player and skeleton!=NodePath(""):
|
|
_generate_animations_logic()
|
|
|
|
|
|
func _clean_previous_rig():
|
|
for c in get_children():
|
|
remove_child(c)
|
|
c.free()
|
|
var old_shadow = find_child(SHADOW_NAME, true, false)
|
|
if old_shadow: old_shadow.free()
|
|
clear_bones()
|
|
skeleton = NodePath("")
|