dp-konzultace/scripts/my_portal.gd

88 lines
3.9 KiB
GDScript

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)
_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)
var mat: ShaderMaterial = MY_PORTAL_MATERIAL
material_override = mat
mat.set_shader_parameter("albedo", _viewport.get_texture())
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