commit 18f44db3fc1340a1c9c6c100f1199a17d2bc7f77 Author: Vojtěch Struhár Date: Tue Mar 4 17:57:47 2025 +0100 Portals work omg diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/addons/freecam_3D/freecam.gd b/addons/freecam_3D/freecam.gd new file mode 100644 index 0000000..b019470 --- /dev/null +++ b/addons/freecam_3D/freecam.gd @@ -0,0 +1,145 @@ +extends Camera3D + +## Camera with flying script attached to it. +class_name Freecam3D + +## +## Camera with toggleable freecam mode for prototyping when creating levels, shaders, lighting, etc. +## +## Usage: Run your game, press and fly around freely. Uses Minecraft-like controls. +## + +## Customize your own toggle key to avoid collisions with your current mappings. +@export var toggle_key: Key = KEY_TAB +## Speed up / down by scrolling the mouse whell down / up +@export var invert_speed_controls: bool = false + +@export var overlay_text: bool = true + +## Pivot node for camera looking around +@onready var pivot := Node3D.new() +## Main parent for camera overlay. +@onready var screen_overlay := VBoxContainer.new() +## Container for the chat-like event log. +@onready var event_log := VBoxContainer.new() + +const MAX_SPEED := 4 +const MIN_SPEED := 0.1 +const ACCELERATION := 0.1 +const MOUSE_SENSITIVITY := 0.002 + +## Whether or not the camera can move. +var movement_active := false: + set(val): + movement_active = val + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED if movement_active else Input.MOUSE_MODE_VISIBLE) + display_message("[Movement ON]" if movement_active else "[Movement OFF]") + +## The current maximum speed. Lower or higher it by scrolling the mouse wheel. +var target_speed := MIN_SPEED +## Movement velocity. +var velocity := Vector3.ZERO + + +## Sets up pivot and UI overlay elements. +func _setup_nodes() -> void: + self.add_sibling(pivot) + pivot.position = position + pivot.rotation = rotation + pivot.name = "FreecamPivot" + self.reparent(pivot) + self.position = Vector3.ZERO + self.rotation = Vector3.ZERO + # UI stuff + screen_overlay.add_theme_constant_override("Separation", 8) + self.add_child(screen_overlay) + screen_overlay.add_child(_make_label("Debug Camera")) + screen_overlay.add_spacer(false) + + screen_overlay.add_child(event_log) + screen_overlay.visible = overlay_text + + +func _ready() -> void: + _setup_nodes.call_deferred() + _add_keybindings() + + +func _process(delta: float) -> void: + + if Input.is_action_just_released("__debug_camera_toggle"): + movement_active = not movement_active + + if movement_active: + var dir = Vector3.ZERO + if Input.is_action_pressed("__debug_camera_forward"): dir.z -= 1 + if Input.is_action_pressed("__debug_camera_back"): dir.z += 1 + if Input.is_action_pressed("__debug_camera_left"): dir.x -= 1 + if Input.is_action_pressed("__debug_camera_right"): dir.x += 1 + if Input.is_action_pressed("__debug_camera_up"): dir.y += 1 + if Input.is_action_pressed("__debug_camera_down"): dir.y -= 1 + + dir = dir.normalized() + dir = dir.rotated(Vector3.UP, pivot.rotation.y) + + velocity = lerp(velocity, dir * target_speed, ACCELERATION) + pivot.position += velocity + + +func _input(event: InputEvent) -> void: + if movement_active: + # Turn around + if event is InputEventMouseMotion: + pivot.rotate_y(-event.relative.x * MOUSE_SENSITIVITY) + rotate_x(-event.relative.y * MOUSE_SENSITIVITY) + rotation.x = clamp(rotation.x, -PI/2, PI/2) + + var speed_up = func(): + target_speed = clamp(target_speed + 0.15, MIN_SPEED, MAX_SPEED) + display_message("[Speed up] " + str(target_speed)) + + var slow_down = func(): + target_speed = clamp(target_speed - 0.15, MIN_SPEED, MAX_SPEED) + display_message("[Slow down] " + str(target_speed)) + + # Speed up and down with the mouse wheel + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: + slow_down.call() if invert_speed_controls else speed_up.call() + + if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed: + speed_up.call() if invert_speed_controls else slow_down.call() + + +## Pushes new message label into "chat" and removes the old ones if necessary +func display_message(text: String) -> void: + while event_log.get_child_count() >= 3: + event_log.remove_child(event_log.get_child(0)) + + event_log.add_child(_make_label(text)) + + +func _make_label(text: String) -> Label: + var l = Label.new() + l.text = text + return l + + +func _add_keybindings() -> void: + var actions = InputMap.get_actions() + if "__debug_camera_forward" not in actions: _add_key_input_action("__debug_camera_forward", KEY_W) + if "__debug_camera_back" not in actions: _add_key_input_action("__debug_camera_back", KEY_S) + if "__debug_camera_left" not in actions: _add_key_input_action("__debug_camera_left", KEY_A) + if "__debug_camera_right" not in actions: _add_key_input_action("__debug_camera_right", KEY_D) + if "__debug_camera_up" not in actions: _add_key_input_action("__debug_camera_up", KEY_SPACE) + if "__debug_camera_down" not in actions: _add_key_input_action("__debug_camera_down", KEY_SHIFT) + if "__debug_camera_toggle" not in actions: _add_key_input_action("__debug_camera_toggle", toggle_key) + + +func _add_key_input_action(name: String, key: Key) -> void: + var ev = InputEventKey.new() + ev.physical_keycode = key + + InputMap.add_action(name) + InputMap.action_add_event(name, ev) + diff --git a/addons/freecam_3D/freecam.gd.uid b/addons/freecam_3D/freecam.gd.uid new file mode 100644 index 0000000..e5369d3 --- /dev/null +++ b/addons/freecam_3D/freecam.gd.uid @@ -0,0 +1 @@ +uid://hhinqn2b35ry diff --git a/addons/freecam_3D/mc-camera2.png b/addons/freecam_3D/mc-camera2.png new file mode 100644 index 0000000..62f31b3 Binary files /dev/null and b/addons/freecam_3D/mc-camera2.png differ diff --git a/addons/freecam_3D/mc-camera2.png.import b/addons/freecam_3D/mc-camera2.png.import new file mode 100644 index 0000000..4f5aa2c --- /dev/null +++ b/addons/freecam_3D/mc-camera2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4kitg6s5bcqu" +path="res://.godot/imported/mc-camera2.png-fe60f555c5ea92c0796267cd654ef834.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/freecam_3D/mc-camera2.png" +dest_files=["res://.godot/imported/mc-camera2.png-fe60f555c5ea92c0796267cd654ef834.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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=1 diff --git a/addons/freecam_3D/plugin.cfg b/addons/freecam_3D/plugin.cfg new file mode 100644 index 0000000..db0b562 --- /dev/null +++ b/addons/freecam_3D/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Freecam3D" +description="A flying freecam with Minecraft-like controls." +author="Vojtech Struhar" +version="1.2.0" +script="plugin.gd" diff --git a/addons/freecam_3D/plugin.gd b/addons/freecam_3D/plugin.gd new file mode 100644 index 0000000..e9419da --- /dev/null +++ b/addons/freecam_3D/plugin.gd @@ -0,0 +1,20 @@ +@tool +extends EditorPlugin + +## +## Bootstraps the Freecam3D node. +## + +func _enter_tree() -> void: + print("[Freecam3D Plugin] Loaded.") + + add_custom_type( + "Freecam3D", + "Camera3D", + preload("res://addons/freecam_3D/freecam.gd"), + preload("res://addons/freecam_3D/mc-camera2.png")) + + +func _exit_tree() -> void: + remove_custom_type("Freecam3D") + diff --git a/addons/freecam_3D/plugin.gd.uid b/addons/freecam_3D/plugin.gd.uid new file mode 100644 index 0000000..607cde6 --- /dev/null +++ b/addons/freecam_3D/plugin.gd.uid @@ -0,0 +1 @@ +uid://bbm3kiepb07f diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..4b8476f --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://xi6x2fcxxvt7" +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/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +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=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/material_01.tres b/material_01.tres new file mode 100644 index 0000000..30deead --- /dev/null +++ b/material_01.tres @@ -0,0 +1,10 @@ +[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://ghbu2w8ocp64"] + +[ext_resource type="Texture2D" uid="uid://s3y04hbbp0w2" path="res://texture_01.png" id="1_bp01u"] + +[resource] +vertex_color_is_srgb = true +albedo_texture = ExtResource("1_bp01u") +uv1_triplanar = true +uv1_world_triplanar = true +texture_filter = 5 diff --git a/node_label.gd b/node_label.gd new file mode 100644 index 0000000..4c6f9b3 --- /dev/null +++ b/node_label.gd @@ -0,0 +1,12 @@ +@tool +extends Label3D + +func _ready() -> void: + update_text() + +func update_text() -> void: + var p = get_parent() + if p: + self.text = p.name + else: + self.text = "No parent" diff --git a/node_label.gd.uid b/node_label.gd.uid new file mode 100644 index 0000000..9b468bb --- /dev/null +++ b/node_label.gd.uid @@ -0,0 +1 @@ +uid://fsxesq30glaw diff --git a/node_label.tscn b/node_label.tscn new file mode 100644 index 0000000..12b945e --- /dev/null +++ b/node_label.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://ja485k5pgefb"] + +[ext_resource type="Script" uid="uid://fsxesq30glaw" path="res://node_label.gd" id="1_4l7rt"] + +[node name="NodeLabel" type="Label3D"] +script = ExtResource("1_4l7rt") diff --git a/portal_camera.gd b/portal_camera.gd new file mode 100644 index 0000000..521f85b --- /dev/null +++ b/portal_camera.gd @@ -0,0 +1,57 @@ +@tool +extends Camera3D + +@export var home: Node3D +@export var target: Node3D +@export var portal_mesh: MeshInstance3D: + set(v): + portal_mesh = v + if v != null: + print("set: portal_mesh", v) + var mat = v.mesh.surface_get_material(0) as ShaderMaterial + mat.set_shader_parameter("albedo", get_parent().get_texture()) + +@export var player_camera: Node3D + +@export_tool_button("Grab editor camera", "Camera3D") +var _tb_grab_editor_camera: Callable = _grab_editor_camera + + +func _ready() -> void: + if home == null: + var p = get_parent() + while p is not Node3D: + p = p.get_parent() + home = p + print(name + ": home = " + home.name) + + var env: Environment = get_world_3d().environment.duplicate() + env.tonemap_mode = Environment.TONE_MAPPER_LINEAR + env.tonemap_exposure = 1 + self.environment = env + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + if not check(): + return + + var player_to_home = home.global_transform.affine_inverse() * player_camera.global_transform + var flipped = player_to_home.rotated(Vector3.UP, PI) + var relative_to_target = target.global_transform * flipped + + self.global_transform = relative_to_target + + +func check() -> bool: + return target != null and \ + player_camera != null and \ + home != null and \ + portal_mesh != null + +func _grab_editor_camera() -> void: + if Engine.is_editor_hint(): + print("Grabbing editor camera") + player_camera = EditorInterface.get_editor_viewport_3d(0).get_camera_3d() + else: + print("Not in editor - cannot grab editor camera!") diff --git a/portal_camera.gd.uid b/portal_camera.gd.uid new file mode 100644 index 0000000..457a908 --- /dev/null +++ b/portal_camera.gd.uid @@ -0,0 +1 @@ +uid://dndoebk0ajyga diff --git a/portal_camera.tscn b/portal_camera.tscn new file mode 100644 index 0000000..90af686 --- /dev/null +++ b/portal_camera.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=4 format=3 uid="uid://c3r8mu47c7pex"] + +[ext_resource type="Script" uid="uid://dndoebk0ajyga" path="res://portal_camera.gd" id="1_jsd0o"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_jsd0o"] + +[sub_resource type="PrismMesh" id="PrismMesh_llqtg"] +material = SubResource("StandardMaterial3D_jsd0o") +size = Vector3(1.5, 1, 0.68) + +[node name="PortalCamera" type="Camera3D"] +script = ExtResource("1_jsd0o") + +[node name="Cone" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, -0.5) +mesh = SubResource("PrismMesh_llqtg") diff --git a/portal_shader.gdshader b/portal_shader.gdshader new file mode 100644 index 0000000..54e463b --- /dev/null +++ b/portal_shader.gdshader @@ -0,0 +1,33 @@ +/* + Asset: Godot Simple Portal System + File: portal.gdshader + Description: Shader used by the simple portal system. + Repository: https://github.com/Donitzo/godot-simple-portal-system + License: CC0 License +*/ + +shader_type spatial; +render_mode unshaded; + +uniform sampler2D albedo:hint_default_black, source_color; + +varying float pixel_distance; + +void vertex() { + // Calculate the world-space distance between the pixel and camera. + // Pass the distance to the fragment shader using a varying attribute. + vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; + vec3 camera_position = (INV_VIEW_MATRIX * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + + //pixel_distance = distance(world_position, camera_position); +} + +void fragment() { + // The portal color is simply the screen-space color of the exit camera render target. + // This is because the exit camera views the exit portal from the perspective of the player watching + // the entrance portal, meaning the exit portal will occupy the same screen-space as the entrance portal. + vec3 portal_color = texture(albedo, SCREEN_UV).rgb; + + // Fade-out color + ALBEDO = portal_color; +} \ No newline at end of file diff --git a/portal_shader.gdshader.uid b/portal_shader.gdshader.uid new file mode 100644 index 0000000..32aa8bc --- /dev/null +++ b/portal_shader.gdshader.uid @@ -0,0 +1 @@ +uid://c13etsjuimkrs diff --git a/portal_shader_material.tres b/portal_shader_material.tres new file mode 100644 index 0000000..e94ce29 --- /dev/null +++ b/portal_shader_material.tres @@ -0,0 +1,8 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://ctb8htd1nm3an"] + +[ext_resource type="Shader" uid="uid://c13etsjuimkrs" path="res://portal_shader.gdshader" id="1_qfvuj"] + +[resource] +resource_local_to_scene = true +render_priority = 0 +shader = ExtResource("1_qfvuj") diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..5491efe --- /dev/null +++ b/project.godot @@ -0,0 +1,24 @@ +; 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="Test portal transformations" +config/features=PackedStringArray("4.4", "Forward Plus") +config/icon="res://icon.svg" + +[display] + +window/size/viewport_width=1920 +window/size/viewport_height=1080 + +[editor_plugins] + +enabled=PackedStringArray("res://addons/freecam_3D/plugin.cfg") diff --git a/texture_01.png b/texture_01.png new file mode 100644 index 0000000..14a9811 Binary files /dev/null and b/texture_01.png differ diff --git a/texture_01.png.import b/texture_01.png.import new file mode 100644 index 0000000..5889d0b --- /dev/null +++ b/texture_01.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://s3y04hbbp0w2" +path.s3tc="res://.godot/imported/texture_01.png-87772d058110b5d64dbdfb8c04d0f2f5.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://texture_01.png" +dest_files=["res://.godot/imported/texture_01.png-87772d058110b5d64dbdfb8c04d0f2f5.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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/world.tscn b/world.tscn new file mode 100644 index 0000000..3894b54 --- /dev/null +++ b/world.tscn @@ -0,0 +1,165 @@ +[gd_scene load_steps=23 format=4 uid="uid://bjp6cbtfne2qj"] + +[ext_resource type="Material" uid="uid://ghbu2w8ocp64" path="res://material_01.tres" id="1_f3sb7"] +[ext_resource type="Shader" uid="uid://c13etsjuimkrs" path="res://portal_shader.gdshader" id="2_036b0"] +[ext_resource type="PackedScene" uid="uid://ja485k5pgefb" path="res://node_label.tscn" id="2_fj7yv"] +[ext_resource type="PackedScene" uid="uid://c3r8mu47c7pex" path="res://portal_camera.tscn" id="3_tlwt5"] +[ext_resource type="Script" uid="uid://hhinqn2b35ry" path="res://addons/freecam_3D/freecam.gd" id="5_036b0"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_036b0"] +sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) +ground_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) + +[sub_resource type="Sky" id="Sky_dwbse"] +sky_material = SubResource("ProceduralSkyMaterial_036b0") + +[sub_resource type="Environment" id="Environment_pkg7o"] +background_mode = 2 +sky = SubResource("Sky_dwbse") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="BoxMesh" id="BoxMesh_fj7yv"] +size = Vector3(20, 1, 20) + +[sub_resource type="ViewportTexture" id="ViewportTexture_036b0"] +viewport_path = NodePath("PortalA/SubViewport") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_dwbse"] +resource_local_to_scene = true +render_priority = 0 +shader = ExtResource("2_036b0") +shader_parameter/albedo = SubResource("ViewportTexture_036b0") + +[sub_resource type="PlaneMesh" id="PlaneMesh_pkg7o"] +material = SubResource("ShaderMaterial_dwbse") +size = Vector2(1.5, 2) +orientation = 2 + +[sub_resource type="Environment" id="Environment_dwbse"] +background_mode = 2 +sky = SubResource("Sky_dwbse") +glow_enabled = true + +[sub_resource type="ArrayMesh" id="ArrayMesh_tlwt5"] +_surfaces = [{ +"aabb": AABB(-1, -1.25, -0.25, 2, 2.5, 0.5), +"attribute_data": PackedByteArray("AACAPwAAAAAAAAAAAAAAAAAAgD8AAAA+AAAAPs3MzD0AAAAAAAAAAAAAAD4AAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAPwAAAD4AAAAAAAAAAAAAAAAAAAA+AABgPwAAgD8AAIA/AACAPwAAYD/NzMw9AABgP2ZmZj8AAIA/AACAPwAAYD8AAAAAAABgP2ZmZj8AAAAAAACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAGA/zczMPQAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AABgP83MzD0AAIA/AAAAAAAAAD7NzMw9AACAPwAAgD8AAIA/AABgPwAAAAAAAIA/AACAPwAAAAAAAGA/AAAAAAAAgD8AAIA/AAAAPgAAAAAAAAAAAAAAAAAAAD5mZmY/AAAAAAAAgD8AAIA/AABgPwAAAAAAAGA/AACAPwAAAAAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAgD8AAAA+AACAPwAAAAAAAAAAAAAAPmZmZj8AAAAAAACAPwAAYD9mZmY/AACAPwAAAAAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAgD8AAAA+ZmZmPwAAAAAAAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAPs3MzD0AAIA/AAAAAAAAAAAAAAAAAAAAAAAAQD9mZmY/AACAPmZmZj8AAEA/ZmZmPwAAgD4AAAAAAABAPwAAAAAAAIA+AAAAAAAAQD9mZmY/AACAPmZmZj8AAEA/AACAPgAAAAAAAEA/AAAAAAAAQD8AAIA/ZmZmPwAAgD4AAAAAAABAPwAAAAAAAIA+AABAPwAAgD8AAIA+AACAPwAAgD4AAAAA"), +"format": 34359738391, +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 84, +"vertex_data": PackedByteArray("AACAvwAAoL8AAIC+AACAvwAAoL8AAIA+AABAvwAAoL8AAIC+AABAPwAAgD8AAIA+AACAPwAAoD8AAIA+AABAPwAAoL8AAIA+AACAvwAAoL8AAIA+AACAvwAAoL8AAIC+AACAvwAAoD8AAIC+AABAvwAAoL8AAIC+AACAvwAAoL8AAIA+AABAvwAAoL8AAIA+AABAvwAAoL8AAIA+AACAvwAAoL8AAIA+AABAvwAAgD8AAIA+AABAvwAAgD8AAIC+AACAvwAAoD8AAIC+AABAvwAAoL8AAIC+AABAvwAAgD8AAIC+AACAPwAAoD8AAIC+AACAvwAAoD8AAIC+AACAvwAAoD8AAIC+AACAvwAAoD8AAIA+AACAvwAAoL8AAIA+AACAvwAAoD8AAIA+AABAvwAAgD8AAIA+AACAvwAAoL8AAIA+AACAvwAAoD8AAIC+AACAPwAAoD8AAIA+AACAvwAAoD8AAIA+AABAvwAAgD8AAIA+AACAvwAAoD8AAIA+AABAPwAAgD8AAIA+AACAPwAAoL8AAIC+AABAPwAAoL8AAIC+AACAPwAAoL8AAIA+AACAvwAAoL8AAIC+AABAvwAAoL8AAIC+AACAvwAAoD8AAIC+AABAPwAAoL8AAIC+AACAPwAAoL8AAIC+AABAPwAAgD8AAIC+AACAPwAAoL8AAIA+AABAPwAAoL8AAIC+AABAPwAAoL8AAIA+AACAPwAAoL8AAIA+AACAPwAAoD8AAIA+AACAPwAAoL8AAIC+AACAPwAAoL8AAIA+AABAPwAAoL8AAIA+AACAPwAAoD8AAIA+AABAPwAAgD8AAIC+AACAPwAAoD8AAIC+AABAvwAAgD8AAIC+AACAPwAAoD8AAIC+AACAPwAAoD8AAIA+AACAvwAAoD8AAIC+AACAPwAAoD8AAIC+AABAPwAAgD8AAIC+AACAPwAAoL8AAIC+AACAPwAAoD8AAIA+AACAPwAAoD8AAIC+AACAPwAAoL8AAIC+AABAPwAAgD8AAIA+AACAvwAAoD8AAIA+AACAPwAAoD8AAIA+AABAvwAAgD8AAIA+AABAvwAAoL8AAIC+AABAvwAAoL8AAIA+AABAvwAAoL8AAIC+AABAvwAAgD8AAIA+AABAvwAAgD8AAIC+AABAPwAAgD8AAIC+AABAPwAAoL8AAIA+AABAPwAAoL8AAIC+AABAPwAAgD8AAIA+AABAPwAAgD8AAIC+AABAvwAAgD8AAIC+AABAPwAAoL8AAIA+AABAPwAAgD8AAIC+AABAPwAAgD8AAIA+AABAvwAAgD8AAIC+AABAvwAAgD8AAIA+AABAPwAAgD8AAIA+/38AAP//AAD/fwAA//8AAP9/AAD//wAA/3//fwAA/z//f/9/AAD/P/9//38AAP8/AAD/f/9//n8AAP9//3/+fwAA/3//f/5//38AAP//AAD/fwAA//8AAP9/AAD//wAA/3//fwAA/z//f/9/AAD/P/9//38AAP8//////wAA/z//////AAD/P/////8AAP8//////wAA/z//////AAD/P/////8AAP8/AAD/f/9//n8AAP9//3/+fwAA/3//f/5//3//fwAA/z//f/9/AAD/P/9//38AAP8//3//////AAD/f/////8AAP9//////wAA/3//fwAA/z//f/9/AAD/P/9//38AAP8//38AAP//AAD/fwAA//8AAP9/AAD//wAA/////wAA/z//////AAD/P/////8AAP8//////wAA/z//////AAD/P/////8AAP8//38AAP//AAD/fwAA//8AAP9/AAD//wAA////f/9//n////9//3/+f////3//f/5//3//fwAA/z//f/9/AAD/P/9//38AAP8//////wAA/z//////AAD/P/////8AAP8//3//////AAD/f/////8AAP9//////wAA/////wAA/z//////AAD/P/////8AAP8/////f/9//n////9//3/+f////3//f/5//3//fwAA/z//f/9/AAD/P/9//38AAP8/////f/9/AID///9//38AgP///3//fwCA////f/9/AID///9//38AgP///3//fwCAAAD/f/9/AIAAAP9//38AgAAA/3//fwCA/38AAP//////fwAA//////9/AAD/////AAD/f/9/AIAAAP9//38AgAAA/3//fwCA/38AAP//////fwAA//////9/AAD/////") +}] + +[sub_resource type="ViewportTexture" id="ViewportTexture_pkg7o"] +viewport_path = NodePath("PortalB/SubViewport") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_aqk2v"] +render_priority = 0 +shader = ExtResource("2_036b0") +shader_parameter/albedo = SubResource("ViewportTexture_pkg7o") + +[sub_resource type="PlaneMesh" id="PlaneMesh_036b0"] +material = SubResource("ShaderMaterial_aqk2v") +size = Vector2(1.5, 2) +orientation = 2 + +[sub_resource type="Environment" id="Environment_gbfbk"] +background_mode = 2 +sky = SubResource("Sky_dwbse") +glow_enabled = true + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_036b0"] +albedo_color = Color(0.917479, 0.431154, 0.0392214, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_dwbse"] +material = SubResource("StandardMaterial3D_036b0") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_dwbse"] +albedo_color = Color(0.655993, 0.310296, 1, 1) + +[sub_resource type="SphereMesh" id="SphereMesh_pkg7o"] +material = SubResource("StandardMaterial3D_dwbse") + +[node name="World" type="Node3D"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_pkg7o") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0) +shadow_enabled = true + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0) +material_override = ExtResource("1_f3sb7") +mesh = SubResource("BoxMesh_fj7yv") + +[node name="PortalA" type="Node3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.5, 0, 3.62398) + +[node name="MeshA" type="MeshInstance3D" parent="PortalA"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +mesh = SubResource("PlaneMesh_pkg7o") + +[node name="NodeLabel" parent="PortalA" instance=ExtResource("2_fj7yv")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.49309, 0) +text = "PortalA" + +[node name="SubViewport" type="SubViewport" parent="PortalA"] +size = Vector2i(1920, 1080) + +[node name="PortalCamera" parent="PortalA/SubViewport" node_paths=PackedStringArray("home", "target", "portal_mesh", "player_camera") instance=ExtResource("3_tlwt5")] +transform = Transform3D(0.677513, 0.085151, -0.730565, 0, 0.993276, 0.115771, 0.735511, -0.0784365, 0.672957, 1.96377, 1.7265, 7.70245) +environment = SubResource("Environment_dwbse") +home = NodePath("../..") +target = NodePath("../../../PortalB") +portal_mesh = NodePath("../../MeshA") +player_camera = NodePath("../../../Freecam3D") + +[node name="PortalFrame" type="MeshInstance3D" parent="PortalA"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +material_override = ExtResource("1_f3sb7") +mesh = SubResource("ArrayMesh_tlwt5") + +[node name="PortalB" type="Node3D" parent="."] +transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 2, 0, 0) + +[node name="MeshB" type="MeshInstance3D" parent="PortalB"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +mesh = SubResource("PlaneMesh_036b0") + +[node name="NodeLabel" parent="PortalB" instance=ExtResource("2_fj7yv")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.49309, 0) +text = "PortalB" + +[node name="SubViewport" type="SubViewport" parent="PortalB"] +size = Vector2i(1920, 1080) + +[node name="PortalCamera" parent="PortalB/SubViewport" node_paths=PackedStringArray("home", "target", "portal_mesh", "player_camera") instance=ExtResource("3_tlwt5")] +transform = Transform3D(-0.677513, -0.085151, 0.730565, 0, 0.993276, 0.115771, -0.735511, 0.0784365, -0.672957, 2.16021, 1.7265, -0.57847) +environment = SubResource("Environment_gbfbk") +home = NodePath("../..") +target = NodePath("../../../PortalA") +portal_mesh = NodePath("../../MeshB") +player_camera = NodePath("../../../Freecam3D") + +[node name="PortalFrame2" type="MeshInstance3D" parent="PortalB"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +material_override = ExtResource("1_f3sb7") +mesh = SubResource("ArrayMesh_tlwt5") +skeleton = NodePath("../../PortalA") + +[node name="Freecam3D" type="Camera3D" parent="."] +transform = Transform3D(0.735511, -0.0784364, 0.672957, 0, 0.993276, 0.115771, -0.677513, -0.085151, 0.730565, 6.20245, 1.7265, 3.66021) +script = ExtResource("5_036b0") +metadata/_custom_type_script = "uid://hhinqn2b35ry" + +[node name="MeshInstance3D2" type="MeshInstance3D" parent="."] +transform = Transform3D(0.961808, 0, 0.273724, 0, 1, 0, -0.273724, 0, 0.961808, 4.66263, 0.5, -0.915985) +mesh = SubResource("BoxMesh_dwbse") + +[node name="MeshInstance3D3" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.66637, 0.158801, 5.54613) +mesh = SubResource("SphereMesh_pkg7o")