extends MeshInstance3D class_name MyPortal ## My own portal implementation @export var exit_portal: MyPortal #preload("res://scripts/my_portal_material.tres") const MY_PORTAL_MATERIAL = preload("uid://cwobk5ik5k5aj") var _viewport: SubViewport var _camera: Camera3D = Camera3D.new() var _main_camera: Camera3D = null var exit_scale: int = 1 # This is not really needed I think func _ready() -> void: assert(exit_portal != null) # Update portals last process_priority = 1000 _main_camera = get_viewport().get_camera_3d() # NOTE: In case the exit portal is in a different world, better put the viewport and camera # onto IT, rather than me (?) _viewport = SubViewport.new() _viewport.name = name + "_Viewport" _viewport.size = get_viewport().size add_child(_viewport) _camera.name = name + "_ExitCamera" _viewport.add_child(_camera) # FIXME: Remove this - debug only var debug = MeshInstance3D.new() debug.rotate_z(PI / 2) debug.material_override = StandardMaterial3D.new() debug.material_override.albedo_color = Color.MAGENTA debug.mesh = PrismMesh.new() _camera.add_child(debug) var mat: ShaderMaterial = MY_PORTAL_MATERIAL material_override = mat mat.set_shader_parameter("albedo", _viewport.get_texture()) print(name, ": _ready") func _process(delta: float) -> void: _camera.global_transform = real_to_exit_transform(_main_camera.global_transform) # TODO: Adjust near clip plane to the portal _camera.near = _main_camera.near _camera.far = _main_camera.far _camera.fov = _main_camera.fov _camera.keep_aspect = _main_camera.keep_aspect ## Return a new Transform3D relative to the exit portal based on the real Transform3D relative to this portal. func real_to_exit_transform(real:Transform3D) -> Transform3D: # Convert from global space to local space at the entrance (this) portal var local:Transform3D = global_transform.affine_inverse() * real # Compensate for any scale the entrance portal may have var unscaled:Transform3D = local.scaled(global_transform.basis.get_scale()) # Flip it (the portal always flips the view 180 degrees) var flipped:Transform3D = unscaled.rotated(Vector3.UP, PI) # Apply any scale the exit portal may have (and apply custom exit scale) var exit_scale_vector:Vector3 = exit_portal.global_transform.basis.get_scale() var scaled_at_exit:Transform3D = flipped.scaled(Vector3.ONE / exit_scale_vector * exit_scale) # Convert from local space at the exit portal to global space var local_at_exit:Transform3D = exit_portal.global_transform * scaled_at_exit return local_at_exit ## Return a new position relative to the exit portal based on the real position relative to this portal. func real_to_exit_position(real:Vector3) -> Vector3: # Convert from global space to local space at the entrance (this) portal var local:Vector3 = global_transform.affine_inverse() * real # Compensate for any scale the entrance portal may have var unscaled:Vector3 = local * global_transform.basis.get_scale() # Apply any scale the exit portal may have (and apply custom exit scale) var exit_scale_vector:Vector3 = Vector3(-1, 1, 1) * exit_portal.global_transform.basis.get_scale() var scaled_at_exit:Vector3 = unscaled / exit_scale_vector * exit_scale # Convert from local space at the exit portal to global space var local_at_exit:Vector3 = exit_portal.global_transform * scaled_at_exit return local_at_exit ## Return a new direction relative to the exit portal based on the real direction relative to this portal. func real_to_exit_direction(real:Vector3) -> Vector3: # Convert from global to local space at the entrance (this) portal var local:Vector3 = global_transform.basis.inverse() * real # Compensate for any scale the entrance portal may have var unscaled:Vector3 = local * global_transform.basis.get_scale() # Flip it (the portal always flips the view 180 degrees) var flipped:Vector3 = unscaled.rotated(Vector3.UP, PI) # Apply any scale the exit portal may have (and apply custom exit scale) var exit_scale_vector:Vector3 = exit_portal.global_transform.basis.get_scale() var scaled_at_exit:Vector3 = flipped / exit_scale_vector * exit_scale # Convert from local space at the exit portal to global space var local_at_exit:Vector3 = exit_portal.global_transform.basis * scaled_at_exit return local_at_exit