diff --git a/3d/physics_interpolation/.editorconfig b/3d/physics_interpolation/.editorconfig new file mode 100644 index 00000000..f28239ba --- /dev/null +++ b/3d/physics_interpolation/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/3d/physics_interpolation/README.md b/3d/physics_interpolation/README.md new file mode 100644 index 00000000..7cc78adc --- /dev/null +++ b/3d/physics_interpolation/README.md @@ -0,0 +1,25 @@ +# Physics Interpolation + +This demo showcases [physics interpolation](https://docs.godotengine.org/en/stable/tutorials/physics/interpolation/index.html) +in 3D with varying camera modes (first person, third person, fixed perspective). +This is also known as *fixed timestep interpolation*. + +Physics interpolation makes motion appear smooth regardless of the rendered +framerate and physics ticks per second configured in the Project Settings. +There are some caveats related to its usage though, such as increased latency +and potential issues with teleporting objects. It's recommended to go +through the +[documentation](https://docs.godotengine.org/en/stable/tutorials/physics/interpolation/physics_interpolation_introduction.html) +when enabling physics interpolation in a project. + +Physics interpolation is enabled by default in this project. Press T +to toggle it while the demo is running. This allows you to see the impact on +smoothness of physics interpolation. + +Language: GDScript + +Renderer: Compatibility + +## Screenshots + +![Screenshot](screenshots/physics_interpolation.webp) diff --git a/3d/physics_interpolation/box.tscn b/3d/physics_interpolation/box.tscn new file mode 100644 index 00000000..1e6c6a4b --- /dev/null +++ b/3d/physics_interpolation/box.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=4 format=3 uid="uid://cvgxw8nq67xxm"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_pq8q7"] + +[sub_resource type="BoxMesh" id="BoxMesh_pyidc"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_oq5cr"] +albedo_color = Color(0.5544661, 0.39379695, 0.15444939, 1) + +[node name="Box" type="RigidBody3D"] +mass = 10.0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("BoxShape3D_pq8q7") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("BoxMesh_pyidc") +surface_material_override/0 = SubResource("StandardMaterial3D_oq5cr") diff --git a/3d/physics_interpolation/bullet.gd b/3d/physics_interpolation/bullet.gd new file mode 100644 index 00000000..d6f718fd --- /dev/null +++ b/3d/physics_interpolation/bullet.gd @@ -0,0 +1,27 @@ +extends RigidBody3D + +@export var scale_curve: Curve + +var _enabled: bool = false + + +func _ready() -> void: + $CollisionShape3D.disabled = true + + +func _physics_process(_delta: float) -> void: + # Start with physics collision disabled until the first tick. + # This prevents bullets from colliding with the player when first shot, while still + # allowing them to be moved by the player when they're laying on the ground. + if !_enabled: + $CollisionShape3D.disabled = false + _enabled = true + + # Apply the appearance scaling according to the scale curve. + var time_left_ratio: float = 1.0 - $Timer.time_left / $Timer.wait_time + var scale_sampled := scale_curve.sample_baked(time_left_ratio) + $Scaler.scale = Vector3.ONE * scale_sampled + + +func _on_timer_timeout() -> void: + queue_free() diff --git a/3d/physics_interpolation/bullet.gd.uid b/3d/physics_interpolation/bullet.gd.uid new file mode 100644 index 00000000..b222102c --- /dev/null +++ b/3d/physics_interpolation/bullet.gd.uid @@ -0,0 +1 @@ +uid://dohdjr8ilotkr diff --git a/3d/physics_interpolation/bullet.tscn b/3d/physics_interpolation/bullet.tscn new file mode 100644 index 00000000..3bd4c014 --- /dev/null +++ b/3d/physics_interpolation/bullet.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=15 format=3 uid="uid://2r5wc1n0ybju"] + +[ext_resource type="Script" uid="uid://dohdjr8ilotkr" path="res://bullet.gd" id="1_v7oki"] +[ext_resource type="Texture2D" uid="uid://b70s43gn8c3kx" path="res://spark_particle.png" id="2_v8qja"] + +[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_v8qja"] +bounce = 0.61 + +[sub_resource type="Curve" id="Curve_t4vbm"] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.02, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 3 + +[sub_resource type="SphereShape3D" id="SphereShape3D_rtl8c"] +radius = 0.2 + +[sub_resource type="Gradient" id="Gradient_v8qja"] +colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_t4vbm"] +gradient = SubResource("Gradient_v8qja") + +[sub_resource type="Curve" id="Curve_v8qja"] +_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="CurveTexture" id="CurveTexture_t4vbm"] +curve = SubResource("Curve_v8qja") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_v7oki"] +direction = Vector3(0, 1, 0) +initial_velocity_max = 1.0 +gravity = Vector3(0, 1, 0) +scale_min = 0.2 +scale_curve = SubResource("CurveTexture_t4vbm") +color_ramp = SubResource("GradientTexture1D_t4vbm") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_t4vbm"] +transparency = 1 +blend_mode = 1 +shading_mode = 0 +vertex_color_use_as_albedo = true +albedo_color = Color(2.33575, 3.29442, 3.29442, 1) +albedo_texture = ExtResource("2_v8qja") +texture_filter = 5 + +[sub_resource type="QuadMesh" id="QuadMesh_rtl8c"] +material = SubResource("StandardMaterial3D_t4vbm") +size = Vector2(0.2, 0.2) + +[sub_resource type="SphereMesh" id="SphereMesh_v7oki"] +radial_segments = 24 +rings = 12 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_v7oki"] +albedo_color = Color(0.431649, 0.57319, 0.501392, 1) +metallic = 1.0 + +[node name="Bullet" type="RigidBody3D"] +collision_layer = 2 +physics_material_override = SubResource("PhysicsMaterial_v8qja") +script = ExtResource("1_v7oki") +scale_curve = SubResource("Curve_t4vbm") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_rtl8c") + +[node name="GPUParticles3D" type="GPUParticles3D" parent="."] +amount = 64 +transform_align = 1 +process_material = SubResource("ParticleProcessMaterial_v7oki") +draw_pass_1 = SubResource("QuadMesh_rtl8c") + +[node name="Scaler" type="Node3D" parent="."] +transform = Transform3D(0.001, 0, 0, 0, 0.001, 0, 0, 0, 0.001, 0, 0, 0) + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Scaler"] +transform = Transform3D(0.4, 0, 0, 0, 0.4, 0, 0, 0, 0.4, 0, 0, 0) +mesh = SubResource("SphereMesh_v7oki") +skeleton = NodePath("../..") +surface_material_override/0 = SubResource("StandardMaterial3D_v7oki") + +[node name="Timer" type="Timer" parent="."] +wait_time = 5.0 +autostart = true + +[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] diff --git a/3d/physics_interpolation/icon.png.import b/3d/physics_interpolation/icon.png.import new file mode 100644 index 00000000..51c1edf0 --- /dev/null +++ b/3d/physics_interpolation/icon.png.import @@ -0,0 +1,41 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5avn6uhdr88" +path.s3tc="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://icon.png" +dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/3d/physics_interpolation/icon.svg b/3d/physics_interpolation/icon.svg new file mode 100644 index 00000000..da56e13e --- /dev/null +++ b/3d/physics_interpolation/icon.svg @@ -0,0 +1 @@ + diff --git a/3d/physics_interpolation/icon.svg.import b/3d/physics_interpolation/icon.svg.import new file mode 100644 index 00000000..245a2f42 --- /dev/null +++ b/3d/physics_interpolation/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cb05v27t8vx5t" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/3d/physics_interpolation/player.gd b/3d/physics_interpolation/player.gd new file mode 100644 index 00000000..51816e71 --- /dev/null +++ b/3d/physics_interpolation/player.gd @@ -0,0 +1,202 @@ +extends CharacterBody3D + +const MOUSE_SENSITIVITY = 2.5 +const CAMERA_SMOOTH_SPEED = 10.0 +const MOVE_SPEED = 3.0 +const FRICTION = 10.0 +const JUMP_VELOCITY = 8.0 +const BULLET_SPEED = 9.0 + +const Bullet = preload("res://bullet.tscn") + +# Define our FPS and TPS player views using Euler angles. +var _yaw: float = 0.0 +var _pitch: float = 0.0 + +# XZ direction the player is looking at. +var _dir := Vector3(sin(_yaw), 0, cos(_yaw)) + +# TPS camera. +var _tps_camera_proximity: float = 3.0 +var _tps_camera_look_from := Vector3() + +enum CameraType { + CAM_FIXED, ## Fixed camera perspective. + CAM_FPS, ## First-person perspective. + CAM_TPS, ## Third-person perspective. +} + +# Current camera type. +# (Note that we toggle this in `_ready()`, so it actually starts with FPS camera.) +var _cam_type := CameraType.CAM_FIXED + +var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") + + +func _ready() -> void: + # Capture the mouse (stops the mouse cursor from showing and ensures it stays within the window). + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + + # We define the TPS camera in global space by setting it + # as `top_level` so it ignores the parent transform. + $Rig/Camera_TPS.top_level = true + + # Perform the logic to create FPS view to start with. + cycle_camera_type() + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseMotion: + _yaw -= event.screen_relative.x * MOUSE_SENSITIVITY * 0.001 + _pitch += event.screen_relative.y * MOUSE_SENSITIVITY * 0.002 + _pitch = clamp(_pitch, -PI, PI) + $Rig.rotation = Vector3(0, _yaw, 0) + + +func _update_camera(delta: float) -> void: + # Keep the player direction up-to-date based on the yaw. + _dir.x = sin(_yaw) + _dir.z = cos(_yaw) + + # Rotate the head (and FPS camera and firing origin) with the + # pitch from the mouse. + $Rig/Head.rotation = Vector3(_pitch * -0.5, 0, 0) + + match _cam_type: + CameraType.CAM_TPS: + # We will focus the TPS camera on the head of the player. + var target: Vector3 = $Rig/Head.get_global_transform_interpolated().origin + + # Calculate a position to look at the player from. + var pos := target + + # The camera should be behind the player, so offset the camera relative to direction. + pos.x += _dir.x * _tps_camera_proximity + pos.z += _dir.z * _tps_camera_proximity + + # Move the TPS camera up and down depending on the pitch. + # There's no special formula here, just something that looks okay. + pos.y += 2.0 + _pitch * _tps_camera_proximity * 0.2 + + # Offset from the old `_tps_camera_look_from` to the new position + # we want the TPS camera to move to. + var offset: Vector3 = pos - _tps_camera_look_from + var l: float = offset.length() + + # We cap how far we allow the TPS camera to move on each update, + # so we get a smooth movement rather than snapping. + var tps_cam_speed: float = CAMERA_SMOOTH_SPEED * delta + + # If we are trying to move further than the maximum allowed, + # we resize the offset to `tps_cam_speed`. + if l > tps_cam_speed: + offset *= tps_cam_speed / l + + # Move the TPS camera. + _tps_camera_look_from += offset + + # `look_at_from_position()` does all the magic for us. + $Rig/Camera_TPS.look_at_from_position(_tps_camera_look_from, target, Vector3(0, 1, 0)) + + # For a real TPS camera, some other things to try: + # - Ray cast from the player towards the camera to prevent it looking through walls. + # The SpringArm3D node can be useful here. + # - Try smoothing the camera by yaw/pitch from the player rather than offset. + + +func cycle_camera_type() -> void: + match _cam_type: + CameraType.CAM_FIXED: + _cam_type = CameraType.CAM_FPS + $Rig/Head/Camera_FPS.make_current() + CameraType.CAM_FPS: + _cam_type = CameraType.CAM_TPS + $Rig/Camera_TPS.make_current() + CameraType.CAM_TPS: + _cam_type = CameraType.CAM_FIXED + get_node("../Camera_Fixed").make_current() + + # Hide body in FPS view (but keep shadow casting to improve spatial awareness). + if _cam_type == CameraType.CAM_FPS: + $Rig/Mesh_Body.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_SHADOWS_ONLY + else: + $Rig/Mesh_Body.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON + + +func _process(delta: float) -> void: + if Input.is_action_just_pressed(&"cycle_camera_type"): + cycle_camera_type() + + if Input.is_action_just_pressed(&"toggle_physics_interpolation"): + get_tree().physics_interpolation = not get_tree().physics_interpolation + + if Input.is_action_just_pressed(&"fire"): + var bullet: RigidBody3D = Bullet.instantiate() + + # Figure out where we want the bullet to spawn. + # We use a dummy Node offset from the head, but you may want to use e.g. + # a BoneAttachment3D, or dummy node on a weapon. + var transform_3d: Transform3D = $Rig/Head/Fire_Origin.get_global_transform_interpolated() + bullet.position = transform_3d.origin + + # We can calculate the direction the bullet should travel from the basis (rotation) + # of the dummy Node. + var bul_dir: Vector3 = transform_3d.basis[2].normalized() + + # Give our physics bullet some starting velocity. + bullet.linear_velocity = bul_dir * -BULLET_SPEED + get_parent().add_child(bullet) + + # A moving start for a bullet using physics interpolation can be done + # by resetting, *then* offsetting the position in the direction of travel. + # This means that on the first tick the bullet will be moving rather than + # standing still, as standing still on the first tick can look unnatural. + bullet.reset_physics_interpolation() + bullet.position -= bul_dir * (1.0 - Engine.get_physics_interpolation_fraction()) + + + # If we pressed reset, or went too far from the origin, move back to the origin. + if Input.is_action_just_pressed(&"reset_position") or position.length() > 10.0: + position = Vector3(0, 1, 0) + velocity = Vector3() + reset_physics_interpolation() + _yaw = 0.0 + _pitch = 0.0 + $Rig.rotation = Vector3(0, _yaw, 0) + + if Input.is_action_just_pressed(&"jump") and is_on_floor(): + velocity.y += JUMP_VELOCITY + + # We update our camera every frame. + # Our camera is not physics interpolated, as we want fast response from the mouse. + # However in the case of first-person and third-person views, the position is indirectly + # inherited from physics-interpolated player, so we get nice smooth motion while still + # having quick mouse response. + _update_camera(delta) + + +# When physics interpolation is active on the node, +# you should move it on the physics tick (physics_process) +# rather than on the frame (process). +func _physics_process(delta: float) -> void: + var move := Vector3() + + # Calculate movement relative to the player's coordinate system. + var input: Vector2 = Input.get_vector(&"move_left", &"move_right", &"move_forward", &"move_backward") * MOVE_SPEED + move.x = input.x + move.z = input.y + + # Apply gravity. + move.y -= gravity * delta + + # Apply mouse rotation to the move, so that it is now in global space. + move = move.rotated(Vector3(0, 1, 0), _yaw) + + # Apply the global space move to the physics. + velocity += move + + move_and_slide() + + # Apply friction to horizontal motion in a tick rate-independent manner. + var friction_delta := exp(-FRICTION * delta) + velocity = Vector3(velocity.x * friction_delta, velocity.y, velocity.z * friction_delta) diff --git a/3d/physics_interpolation/player.gd.uid b/3d/physics_interpolation/player.gd.uid new file mode 100644 index 00000000..1f19f7aa --- /dev/null +++ b/3d/physics_interpolation/player.gd.uid @@ -0,0 +1 @@ +uid://cusp3a7wm67ss diff --git a/3d/physics_interpolation/project.godot b/3d/physics_interpolation/project.godot new file mode 100644 index 00000000..0f7a4a22 --- /dev/null +++ b/3d/physics_interpolation/project.godot @@ -0,0 +1,133 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Physics Interpolation" +config/description="This demo showcases physics interpolation in 3D with varying camera modes +(first person, third person, fixed perspective). This is also known as fixed timestep interpolation." +run/main_scene="uid://bj5w87j8wwnsl" +config/features=PackedStringArray("4.5", "GL Compatibility") +config/icon="res://icon.svg" + +[debug] + +gdscript/warnings/untyped_declaration=1 + +[display] + +window/stretch/mode="canvas_items" +window/stretch/aspect="expand" + +[input] + +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +ui_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +ui_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +ui_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null) +, null, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +ui_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +jump={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, 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":2,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} +fire={ +"deadzone": 0.2, +"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":1,"position":Vector2(56, 19),"global_position":Vector2(65, 67),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} +move_forward={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":122,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_backward={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_left={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +reset_position={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":82,"physical_keycode":0,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +] +} +cycle_camera_type={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +toggle_physics_interpolation={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":84,"physical_keycode":0,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null) +] +} + +[physics] + +common/physics_ticks_per_second=20 +3d/physics_engine="Jolt Physics" +3d/default_gravity=20.0 +common/physics_interpolation=true + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" +lights_and_shadows/directional_shadow/soft_shadow_filter_quality=4 +textures/default_filters/anisotropic_filtering_level=4 +anti_aliasing/quality/msaa_3d=2 diff --git a/3d/physics_interpolation/root.tscn b/3d/physics_interpolation/root.tscn new file mode 100644 index 00000000..a3dff6e8 --- /dev/null +++ b/3d/physics_interpolation/root.tscn @@ -0,0 +1,141 @@ +[gd_scene load_steps=15 format=3 uid="uid://bj5w87j8wwnsl"] + +[ext_resource type="Script" uid="uid://cusp3a7wm67ss" path="res://player.gd" id="1_pq8q7"] +[ext_resource type="PackedScene" uid="uid://cvgxw8nq67xxm" path="res://box.tscn" id="2_pyidc"] +[ext_resource type="PackedScene" uid="uid://dtuq8cdwg46ti" path="res://wall.tscn" id="3_vvh5c"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_vvh5c"] + +[sub_resource type="Sky" id="Sky_28aoi"] +sky_material = SubResource("ProceduralSkyMaterial_vvh5c") + +[sub_resource type="Environment" id="Environment_vvh5c"] +background_mode = 2 +sky = SubResource("Sky_28aoi") +ambient_light_source = 2 +ambient_light_color = Color(1, 1, 1, 1) +ambient_light_energy = 0.3 +tonemap_mode = 4 +glow_enabled = true +glow_hdr_threshold = 0.7 + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_vvh5c"] + +[sub_resource type="CylinderMesh" id="CylinderMesh_vvh5c"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pq8q7"] +albedo_color = Color(1, 0, 0, 1) + +[sub_resource type="PrismMesh" id="PrismMesh_pq8q7"] +size = Vector3(1, 1, 0.01) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pyidc"] +transparency = 1 +no_depth_test = true +albedo_color = Color(1, 1, 0, 0.501961) + +[sub_resource type="BoxMesh" id="BoxMesh_vho56"] +size = Vector3(10, 0.2, 10) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_vvh5c"] +albedo_color = Color(0.178458, 0.199519, 0.338574, 1) + +[sub_resource type="BoxShape3D" id="BoxShape3D_vvh5c"] +size = Vector3(10, 1, 10) + +[node name="Root" type="Node3D"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_vvh5c") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.258819, 0.616341, -0.743732, 0, 0.769968, 0.638083, 0.965926, -0.165148, 0.199282, 0, 6.24965, 0) +shadow_enabled = true +shadow_normal_bias = 2.7 +directional_shadow_mode = 0 +directional_shadow_fade_start = 1.0 +directional_shadow_max_distance = 15.0 + +[node name="Player" type="CharacterBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.24778, 0) +script = ExtResource("1_pq8q7") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"] +shape = SubResource("CylinderShape3D_vvh5c") + +[node name="Rig" type="Node3D" parent="Player"] +physics_interpolation_mode = 2 + +[node name="Mesh_Body" type="MeshInstance3D" parent="Player/Rig"] +mesh = SubResource("CylinderMesh_vvh5c") +skeleton = NodePath("../..") +surface_material_override/0 = SubResource("StandardMaterial3D_pq8q7") + +[node name="Camera_TPS" type="Camera3D" parent="Player/Rig"] +transform = Transform3D(1, 0, 0, 0, 0.819152, 0.573576, 0, -0.573576, 0.819152, 0, 1.72429, 2.14884) + +[node name="Head" type="Node3D" parent="Player/Rig"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.12412, 0) + +[node name="Camera_FPS" type="Camera3D" parent="Player/Rig/Head"] +current = true + +[node name="Mesh_Head" type="MeshInstance3D" parent="Player/Rig/Head"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -0.5) +mesh = SubResource("PrismMesh_pq8q7") +skeleton = NodePath("../..") +surface_material_override/0 = SubResource("StandardMaterial3D_pyidc") + +[node name="Fire_Origin" type="Node3D" parent="Player/Rig/Head"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1.60726) + +[node name="Camera_Fixed" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.788161, 0.615469, 0, -0.615469, 0.788161, 0, 4.94837, 5.75259) + +[node name="Background" type="Node3D" parent="."] + +[node name="Floor" type="MeshInstance3D" parent="Background"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0) +mesh = SubResource("BoxMesh_vho56") +skeleton = NodePath("../..") +surface_material_override/0 = SubResource("StandardMaterial3D_vvh5c") + +[node name="StaticBody3D" type="StaticBody3D" parent="Background/Floor"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Background/Floor/StaticBody3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.4, 0) +shape = SubResource("BoxShape3D_vvh5c") + +[node name="Box" parent="Background" instance=ExtResource("2_pyidc")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.596531, 2.19581) +can_sleep = false + +[node name="Wall" parent="Background" instance=ExtResource("3_vvh5c")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 5) + +[node name="Wall2" parent="Background" instance=ExtResource("3_vvh5c")] +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, -5) + +[node name="Wall3" parent="Background" instance=ExtResource("3_vvh5c")] +transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 5, 0, 0) + +[node name="Wall4" parent="Background" instance=ExtResource("3_vvh5c")] +transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, 0, 0) + +[node name="Label" type="Label" parent="."] +self_modulate = Color(1, 1, 1, 0.627451) +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 24.0 +offset_top = -151.0 +offset_right = 181.0 +offset_bottom = -24.0 +grow_vertical = 0 +theme_override_constants/outline_size = 5 +text = "WASD - Move +Space - Jump +Left Mouse - Fire +Tab - Toggle Camera +R - Reset Player +T - Toggle Physics Interpolation" diff --git a/3d/physics_interpolation/screenshots/.gdignore b/3d/physics_interpolation/screenshots/.gdignore new file mode 100644 index 00000000..e69de29b diff --git a/3d/physics_interpolation/screenshots/physics_interpolation.webp b/3d/physics_interpolation/screenshots/physics_interpolation.webp new file mode 100644 index 00000000..88c31730 Binary files /dev/null and b/3d/physics_interpolation/screenshots/physics_interpolation.webp differ diff --git a/3d/physics_interpolation/spark_particle.png b/3d/physics_interpolation/spark_particle.png new file mode 100644 index 00000000..b11cb691 Binary files /dev/null and b/3d/physics_interpolation/spark_particle.png differ diff --git a/3d/physics_interpolation/spark_particle.png.import b/3d/physics_interpolation/spark_particle.png.import new file mode 100644 index 00000000..1936444a --- /dev/null +++ b/3d/physics_interpolation/spark_particle.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b70s43gn8c3kx" +path="res://.godot/imported/spark_particle.png-c360d6960fdef6ea0e055d81cca0d9a7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://spark_particle.png" +dest_files=["res://.godot/imported/spark_particle.png-c360d6960fdef6ea0e055d81cca0d9a7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/3d/physics_interpolation/wall.tscn b/3d/physics_interpolation/wall.tscn new file mode 100644 index 00000000..dbaef11f --- /dev/null +++ b/3d/physics_interpolation/wall.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=4 format=3 uid="uid://dtuq8cdwg46ti"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_pq8q7"] +size = Vector3(10.2, 1, 0.2) + +[sub_resource type="BoxMesh" id="BoxMesh_pyidc"] +size = Vector3(10.2, 1, 0.2) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_oh6kn"] +albedo_color = Color(0.3204, 0.72, 0.072, 1) + +[node name="Wall" type="StaticBody3D"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) +shape = SubResource("BoxShape3D_pq8q7") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) +mesh = SubResource("BoxMesh_pyidc") +surface_material_override/0 = SubResource("StandardMaterial3D_oh6kn")