diff --git a/godot/cake.tres b/godot/cake.tres index be5b2de..72be408 100644 --- a/godot/cake.tres +++ b/godot/cake.tres @@ -5,7 +5,6 @@ [resource] resource_name = "cake" -cull_mode = 2 vertex_color_use_as_albedo = true albedo_texture = ExtResource("1_jefwg") roughness = 0.5 diff --git a/godot/project.godot b/godot/project.godot index d35607e..a0f1b9f 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -43,3 +43,10 @@ charge={ "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) ] } + +[physics] + +3d/default_linear_damp=5.0 +3d/default_angular_damp=5.0 +3d/time_before_sleep=0.1 +3d/solver/solver_iterations=128 diff --git a/godot/smoother.gd b/godot/smoother.gd new file mode 100644 index 0000000..b1b2fd5 --- /dev/null +++ b/godot/smoother.gd @@ -0,0 +1,229 @@ +# MIT LICENSE +# +# Copyright 2022 Anatol Bogun +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +class_name Smoother extends Node + +## Smoother Node +## Version: 1.0.4 +## +## A node type that smoothes scene nodes' properties by interpolating _physics_process steps. +## +## For documentation please visit https://github.com/anatolbogun/godot-smoother-node . + +## Node properties that are interpolated. +## Defaults to ["position"], even if not displayed in the inspector. +@export var properties:Array[String] = ["position"] + +## Apply interpolation to this node's parent. +@export var smooth_parent: = true : + set (value): + if value == false: + # remove parent from _properties in case this gets toggled on and off during runtime + _properties.erase(get_parent()) + + smooth_parent = value + +## Apply interpolation to the recursive children of this node's parent. +@export var recursive: = true + +## Explicitly include node paths in addition to the nodes that are included by other Smoother +## settings. +@export var includes:Array[NodePath] = [] + +## Explicitly exclude node paths. +## This will exclude nodes that would otherwise be included by other settings. +@export var excludes:Array[NodePath] = [] + +# get an array of all currently smoothed nodes; mainly for debugging performance optimisations +var smoothed_nodes:Array[Node] : + get: + var parent: = get_parent() + return _get_physics_process_nodes(parent, !smooth_parent) if parent != null else [] as Array[Node] + +var _properties: = {} +var _physics_process_nodes:Array[Node] +var _physics_process_just_updated: = false + + +## Reset all smoothed nodes. +func reset() -> void: + _properties.clear() + + +## Reset a specific node. You may want to call this when a node gets teleported. +func reset_node(node:Node) -> void: + _properties.erase(node) + + +## Reset a specific Node by NodePath. You may want to call this when a Node gets teleported. +func reset_node_path(path:NodePath) -> void: + var node: = get_node_or_null(path) + + if node != null: + reset_node(node) + + +## Add a Node to the includes Array[NodePath]. +func add_include_node(node:Node) -> Array[NodePath]: + return add_include_path(get_path_to(node)) + + +## Add a NodePath to the includes Array[NodePath]. +func add_include_path(path:NodePath) -> Array[NodePath]: + return _add_unique_to_array(includes, path) as Array[NodePath] + + +## Remove a Node from the includes Array[NodePath]. +func remove_include_node(node:Node) -> Array[NodePath]: + return remove_include_path(get_path_to(node)) + + +## Remove a NodePath from the includes Array[NodePath]. +func remove_include_path(path:NodePath) -> Array[NodePath]: + return _remove_all_from_array(includes, path) as Array[NodePath] + + +## Add a Node to the excludes Array[NodePath]. +func add_exclude_node(node:Node) -> Array[NodePath]: + return add_exclude_path(get_path_to(node)) + + +## Add a NodePath to the excludes Array[NodePath]. +func add_exclude_path(path:NodePath) -> Array[NodePath]: + return _add_unique_to_array(excludes, path) as Array[NodePath] + + +## Remove a Node from the excludes Array[NodePath]. +func remove_exclude_node(node:Node) -> Array[NodePath]: + return remove_exclude_path(get_path_to(node)) + + +## Remove a NodePath from the excludes Array[NodePath]. +func remove_exclude_path(path:NodePath) -> Array[NodePath]: + return _remove_all_from_array(excludes, path) as Array[NodePath] + + +## Add an item to an array unless the array already contains that item. +func _add_unique_to_array(array:Array, item:Variant) -> Array: + if !array.has(item): + array.push_back(item) + + return array + + +## Remove all array items that match item. +func _remove_all_from_array(array:Array, item:Variant) -> Array: + while array.has(item): + array.erase(item) + + return array + + +## Apply interpolation to all smoothed_nodes supported properties. +func _process(_delta: float) -> void: + for node in _physics_process_nodes: + if !_properties.has(node): continue + + for property in _properties[node]: + var values = _properties[node][property] + + if values.size() == 2: + if _physics_process_just_updated: + values[1] = node[property] + + node[property] = lerp(values[0], values[1], Engine.get_physics_interpolation_fraction()) + + _physics_process_just_updated = false + + +## Store all smoothed_nodes' relevant properties of the previous (origin) and this (target) +## _physics_process frames for interpolation in the upcoming _process frames and apply the origin +## values. +func _physics_process(_delta: float) -> void: + var parent: = get_parent() + if parent == null: return + + # move this node to the top of the parent tree (typically a scene's root node) so that it is + # called before all other _physics_processes + parent.move_child(self, 0) + + if smooth_parent: + process_priority = parent.process_priority - 1 + + # update the relevant nodes once per _physics_process + _physics_process_nodes = _get_physics_process_nodes(parent, !smooth_parent) + + # clean up _properties + for key in _properties.keys(): + if !_physics_process_nodes.has(key): + _properties.erase(key) + + for node in _physics_process_nodes: + if !_properties.has(node): + # called on the first frame after a node was added to _properties + _properties[node] = {} + + # clean up _properties when a node exited the tree + node.tree_exited.connect(func (): _properties.erase(node)) + + for property in properties: + if ! property in node: continue + + if !_properties[node].has(property): + # called on the first frame after a node was added to _properties + _properties[node][property] = [node[property]] + elif _properties[node][property].size() < 2: + # called on the second frame after a node was added to _properties + _properties[node][property].push_front(_properties[node][property][0]) + _properties[node][property][1] = node[property] + else: + _properties[node][property][0] = _properties[node][property][1] + node[property] = _properties[node][property][0] + + _physics_process_just_updated = true + + +## Get the relevant nodes to be smoothed based on this node's tree position and properties. +func _get_physics_process_nodes(node: Node, ignore_node: = false, with_includes: = true) -> Array[Node]: + var nodes:Array[Node] = [] + + nodes.assign(includes.map( + get_node_or_null + ).filter( + func (_node:Node) -> bool: return _node != null && !excludes.has(get_path_to(_node)) + ) if with_includes else []) + + if ( + !ignore_node + && node != self + && !node is RigidBody2D + && !node is RigidBody3D + && !nodes.has(node) + && !excludes.has(get_path_to(node)) + && node.has_method("_physics_process") + ): + nodes.push_back(node) + + if recursive: + for child in node.get_children(): + for nested_node in _get_physics_process_nodes(child, false, false): + _add_unique_to_array(nodes, nested_node) + + return nodes