Portals work omg

This commit is contained in:
Vojtěch Struhár 2025-03-04 17:57:47 +01:00
commit 18f44db3fc
26 changed files with 624 additions and 0 deletions

4
.editorconfig Normal file
View File

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Godot 4+ specific ignores
.godot/
/android/

View File

@ -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 <TAB> 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)

View File

@ -0,0 +1 @@
uid://hhinqn2b35ry

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

View File

@ -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

View File

@ -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"

View File

@ -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")

View File

@ -0,0 +1 @@
uid://bbm3kiepb07f

1
icon.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 994 B

37
icon.svg.import Normal file
View File

@ -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

10
material_01.tres Normal file
View File

@ -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

12
node_label.gd Normal file
View File

@ -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"

1
node_label.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://fsxesq30glaw

6
node_label.tscn Normal file
View File

@ -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")

57
portal_camera.gd Normal file
View File

@ -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!")

1
portal_camera.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://dndoebk0ajyga

16
portal_camera.tscn Normal file
View File

@ -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")

33
portal_shader.gdshader Normal file
View File

@ -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;
}

View File

@ -0,0 +1 @@
uid://c13etsjuimkrs

View File

@ -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")

24
project.godot Normal file
View File

@ -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")

BIN
texture_01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

35
texture_01.png.import Normal file
View File

@ -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

165
world.tscn Normal file
View File

@ -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")