Base scene + player
4
.editorconfig
Normal file
@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
61
addons/portals/README.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Portals 3D
|
||||
|
||||
This plugin enables you to easily create seamless plugins.
|
||||
|
||||
## Documentation
|
||||
|
||||
For documentation about `Portal3D`, see the portal script itself. Everything is properly documented
|
||||
and viewable in the default documentation window. Go to the script editor, click _Search Help_ in
|
||||
the top bar and search for "Portal3D".
|
||||
|
||||
For everything else, there is this README.
|
||||
|
||||
## Guides
|
||||
|
||||
### Customize portals in the editor
|
||||
|
||||
The portal mesh has a custom shader material assigned to it at runtime (defined in
|
||||
`materials/portal_shader.gdshader`), but in editor, it uses a regular material -- find it at
|
||||
`materials/editor-preview-portal-material.tres`. You can edit this material to customize how
|
||||
portals look in the editor (in case the default gray color blends in too much).
|
||||
|
||||
### Smooth teleportation
|
||||
|
||||
The Portal3D script provides a mechanism for smooth teleportation. In order to be able to create
|
||||
smooth portal transitions, you need to put a clipping shader onto all meshes that are supposed to
|
||||
participate in the smooth teleportation.
|
||||
|
||||
**How to convert a regular mesh to a clippable one?** Like this:
|
||||
|
||||
1. On your material, click the downward arrow menu and select _Convert to ShaderMaterial_
|
||||
2. Include the shader macros and use them to inject clipping uniforms, the vertex logic
|
||||
and the fragment logic.
|
||||
|
||||
```c
|
||||
shader_type spatial;
|
||||
|
||||
// ...
|
||||
|
||||
#include "res://addons/portals/materials/portalclip_mesh.gdshaderinc"
|
||||
|
||||
PORTALCLIP_UNIFORMS
|
||||
|
||||
void vertex() {
|
||||
// ...
|
||||
PORTALCLIP_VERTEX
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// ...
|
||||
PORTALCLIP_FRAGMENT
|
||||
}
|
||||
```
|
||||
|
||||
And that's it! Now look for `DUPLICATE_MESHES_CALLBACK` in the Portal3D script, you are ready to
|
||||
get going with smooth teleportation!
|
||||
|
||||
## Gizmos
|
||||
|
||||
This plugin includes couple of custom gizmos. One gives a connected portal an outline and the
|
||||
second one visualizes portal's front direction. You can configure the color of both gizmos in
|
||||
_Project Settings / Addons / Portals_ or turn them off altogether.
|
58
addons/portals/gizmos/portal_exit_outline.gd
Normal file
@ -0,0 +1,58 @@
|
||||
extends EditorNode3DGizmoPlugin
|
||||
|
||||
func _init() -> void:
|
||||
var exit_outline_color = PortalSettings.get_setting("gizmo_exit_outline_color")
|
||||
create_material("outline", exit_outline_color, false, true, false)
|
||||
|
||||
func _get_gizmo_name() -> String:
|
||||
return "PortalExitOutlineGizmo"
|
||||
|
||||
func _has_gizmo(for_node_3d: Node3D) -> bool:
|
||||
return for_node_3d is Portal3D
|
||||
|
||||
func _redraw(gizmo: EditorNode3DGizmo) -> void:
|
||||
var portal = gizmo.get_node_3d() as Portal3D
|
||||
assert(portal != null, "This gizmo works only for Portal3D")
|
||||
gizmo.clear()
|
||||
|
||||
if portal not in EditorInterface.get_selection().get_selected_nodes():
|
||||
return
|
||||
|
||||
var ep: Portal3D = portal.exit_portal
|
||||
if ep == null:
|
||||
return
|
||||
|
||||
|
||||
var extents = Vector3(ep.portal_size.x, ep.portal_size.y, ep._portal_thickness) / 2
|
||||
|
||||
var lines: Array[Vector3] = [
|
||||
# Front rect
|
||||
extents, extents * Vector3(1, -1, 1),
|
||||
extents, extents * Vector3(-1, 1, 1),
|
||||
extents * Vector3(1, -1, 1), extents * Vector3(-1, -1, 1),
|
||||
extents * Vector3(-1, 1, 1), extents * Vector3(-1, -1, 1),
|
||||
|
||||
# Back rect
|
||||
- extents, -extents * Vector3(1, -1, 1),
|
||||
- extents, -extents * Vector3(-1, 1, 1),
|
||||
- extents * Vector3(1, -1, 1), -extents * Vector3(-1, -1, 1),
|
||||
- extents * Vector3(-1, 1, 1), -extents * Vector3(-1, -1, 1),
|
||||
|
||||
# Short Z connections
|
||||
extents * Vector3(1, 1, 1), extents * Vector3(1, 1, -1),
|
||||
extents * Vector3(1, -1, 1), extents * Vector3(1, -1, -1),
|
||||
extents * Vector3(-1, 1, 1), extents * Vector3(-1, 1, -1),
|
||||
extents * Vector3(-1, -1, 1), extents * Vector3(-1, -1, -1),
|
||||
]
|
||||
|
||||
# Double each line for visual thickness
|
||||
#for i in range(lines.size()):
|
||||
#lines.append(lines[i] + (lines[i].normalized() * 0.005))
|
||||
|
||||
for i in range(lines.size()):
|
||||
lines[i] = portal.to_local(ep.to_global(lines[i]))
|
||||
|
||||
gizmo.add_lines(
|
||||
PackedVector3Array(lines),
|
||||
get_material("outline", gizmo)
|
||||
)
|
1
addons/portals/gizmos/portal_exit_outline.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://pk5ua52g54m1
|
39
addons/portals/gizmos/portal_forward_direction.gd
Normal file
@ -0,0 +1,39 @@
|
||||
extends EditorNode3DGizmoPlugin
|
||||
|
||||
func _init() -> void:
|
||||
var forward_color = PortalSettings.get_setting("gizmo_forward_color")
|
||||
create_material("forward", forward_color, false, false, false)
|
||||
|
||||
|
||||
func _get_gizmo_name() -> String:
|
||||
return "PortalForwardDirectionGizmo"
|
||||
|
||||
func _has_gizmo(for_node_3d: Node3D) -> bool:
|
||||
return for_node_3d is Portal3D
|
||||
|
||||
func _redraw(gizmo: EditorNode3DGizmo) -> void:
|
||||
var portal = gizmo.get_node_3d() as Portal3D
|
||||
assert(portal != null, "This gizmo works only for Portal3D")
|
||||
var active: bool = portal in EditorInterface.get_selection().get_selected_nodes()
|
||||
|
||||
gizmo.clear()
|
||||
|
||||
var lines: Array[Vector3] = [
|
||||
Vector3.ZERO, Vector3(0, 0, 1)
|
||||
]
|
||||
if active:
|
||||
var arrow_spread = 0.05
|
||||
lines.append_array([
|
||||
Vector3(0, 0, 1), Vector3(arrow_spread, -arrow_spread, 0.9),
|
||||
Vector3(0, 0, 1), Vector3(-arrow_spread, arrow_spread, 0.9),
|
||||
])
|
||||
|
||||
var offset = 0.005
|
||||
for i in range(lines.size()):
|
||||
var p = lines[i]
|
||||
lines.append(Vector3(p.x + offset, p.y + offset, p.z))
|
||||
|
||||
gizmo.add_lines(
|
||||
PackedVector3Array(lines),
|
||||
get_material("forward", gizmo)
|
||||
)
|
1
addons/portals/gizmos/portal_forward_direction.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cacoywhcpn4ja
|
@ -0,0 +1,5 @@
|
||||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://dcfkcyddxkglf"]
|
||||
|
||||
[resource]
|
||||
cull_mode = 2
|
||||
albedo_color = Color(0.849621, 0.849621, 0.849621, 1)
|
1
addons/portals/materials/portal3d-icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fc7f7f" stroke-width="8.33"><path d="m4.167 33.333h37.5l-16.667-16.666m16.667 16.666-16.667 16.667"/><path d="m41.667 58.333h16.666v-50h-16.666" stroke-linecap="round"/></g></svg>
|
After Width: | Height: | Size: 327 B |
37
addons/portals/materials/portal3d-icon.svg.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ct62bsuel5hyc"
|
||||
path="res://.godot/imported/portal3d-icon.svg-a34538e0e6bdf86bd50ffe0190100a72.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/portals/materials/portal3d-icon.svg"
|
||||
dest_files=["res://.godot/imported/portal3d-icon.svg-a34538e0e6bdf86bd50ffe0190100a72.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
|
16
addons/portals/materials/portal_shader.gdshader
Normal file
@ -0,0 +1,16 @@
|
||||
shader_type spatial;
|
||||
render_mode unshaded;
|
||||
|
||||
uniform sampler2D albedo: hint_default_black, source_color;
|
||||
|
||||
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;
|
||||
|
||||
ALBEDO = portal_color;
|
||||
|
||||
// FOR DEBUG - make portals slightly red
|
||||
// ALBEDO = mix(portal_color, vec3(1, 0, 0), 0.1);
|
||||
}
|
1
addons/portals/materials/portal_shader.gdshader.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bhdb2skdxehes
|
11
addons/portals/materials/portalclip_mesh.gdshaderinc
Normal file
@ -0,0 +1,11 @@
|
||||
#define PORTALCLIP_UNIFORMS \
|
||||
instance uniform bool portal_clip_active = false;\
|
||||
instance uniform vec3 portal_clip_point = vec3(0, 0, 0);\
|
||||
instance uniform vec3 portal_clip_normal = vec3(0, 1, 0);\
|
||||
varying vec3 portal_clip_vertex_position;\
|
||||
|
||||
#define PORTALCLIP_VERTEX \
|
||||
portal_clip_vertex_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;\
|
||||
|
||||
#define PORTALCLIP_FRAGMENT \
|
||||
if (portal_clip_active && dot(portal_clip_vertex_position - portal_clip_point, portal_clip_normal) < 0.0) ALPHA = 0.0;
|
1
addons/portals/materials/portalclip_mesh.gdshaderinc.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cpxsita6rqndc
|
7
addons/portals/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="Portals 3D"
|
||||
description="Seamless portals plugin in 3D"
|
||||
author="Vojtech Struhar"
|
||||
version="1.0"
|
||||
script="plugin.gd"
|
39
addons/portals/plugin.gd
Normal file
@ -0,0 +1,39 @@
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const ExitOutlinesGizmo = preload("uid://pk5ua52g54m1") # gizmos/portal_exit_outline.gd
|
||||
var exit_outline_gizmo
|
||||
|
||||
const ForwardDirGizmo = preload("uid://cacoywhcpn4ja") # gizmos/portal_forward_direction.gd
|
||||
var forward_dir_gizmo
|
||||
|
||||
func _enter_tree() -> void:
|
||||
|
||||
PortalSettings.init_setting("gizmo_exit_outline_active", true, true)
|
||||
PortalSettings.add_info(AtExport.bool_("gizmo_exit_outline_active"))
|
||||
|
||||
PortalSettings.init_setting("gizmo_exit_outline_color", Color.DEEP_SKY_BLUE, true)
|
||||
PortalSettings.add_info(AtExport.color_no_alpha("gizmo_exit_outline_color"))
|
||||
|
||||
PortalSettings.init_setting("gizmo_forward_active", true, true)
|
||||
PortalSettings.add_info(AtExport.bool_("gizmo_forward_active"))
|
||||
|
||||
PortalSettings.init_setting("gizmo_forward_color", Color.HOT_PINK, true)
|
||||
PortalSettings.add_info(AtExport.color_no_alpha("gizmo_forward_color"))
|
||||
|
||||
PortalSettings.init_setting("portals_group_name", "portals")
|
||||
PortalSettings.add_info(AtExport.string("portals_group_name"))
|
||||
|
||||
if PortalSettings.get_setting("gizmo_exit_outline_active"):
|
||||
exit_outline_gizmo = ExitOutlinesGizmo.new()
|
||||
add_node_3d_gizmo_plugin(exit_outline_gizmo)
|
||||
|
||||
if PortalSettings.get_setting("gizmo_forward_active"):
|
||||
forward_dir_gizmo = ForwardDirGizmo.new()
|
||||
add_node_3d_gizmo_plugin(forward_dir_gizmo)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if exit_outline_gizmo:
|
||||
remove_node_3d_gizmo_plugin(exit_outline_gizmo)
|
||||
if forward_dir_gizmo:
|
||||
remove_node_3d_gizmo_plugin(forward_dir_gizmo)
|
1
addons/portals/plugin.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://ev8ej7qedyih
|
171
addons/portals/scripts/at_export.gd
Normal file
@ -0,0 +1,171 @@
|
||||
class_name AtExport extends Object
|
||||
|
||||
## Helper class for defining custom export inspector.
|
||||
##
|
||||
## Intended usage is when using [method Object._get_property_list] to define a custom editor
|
||||
## inspector. The list not exhaustive, as I didn't need every single export annotation. [br]
|
||||
## [codeblock]
|
||||
## @export var foo: int = 0
|
||||
## [/codeblock]
|
||||
## becomes
|
||||
## [codeblock]
|
||||
## var foo: int = 0
|
||||
##
|
||||
## func _get_property_list() -> void:
|
||||
## return [
|
||||
## AtExport.int_("health")
|
||||
## ]
|
||||
## [/codeblock]
|
||||
## Coincidentally, the dictionaries used to register [ProjectSettings] are very similar,
|
||||
## too.
|
||||
|
||||
|
||||
static func _base(propname: String, type: int) -> Dictionary:
|
||||
return {
|
||||
"name": propname,
|
||||
"type": type,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
}
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_tool_button]
|
||||
static func button(propname: String, button_text: String, button_icon: String = "Callable") -> Dictionary:
|
||||
var result := _base(propname, TYPE_CALLABLE)
|
||||
|
||||
assert(not button_text.contains(","), "Button text cannot contain a comma")
|
||||
|
||||
result["hint"] = PROPERTY_HINT_TOOL_BUTTON
|
||||
result["hint_string"] = button_text + "," + button_icon
|
||||
|
||||
return result
|
||||
|
||||
## [annotation @GDScript.@export] bool variables
|
||||
static func bool_(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_BOOL)
|
||||
|
||||
## [annotation @GDScript.@export] [Color] variables
|
||||
static func color(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_COLOR)
|
||||
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_color_no_alpha]
|
||||
static func color_no_alpha(propname: String) -> Dictionary:
|
||||
var result := _base(propname, TYPE_COLOR)
|
||||
result["hint"] = PROPERTY_HINT_COLOR_NO_ALPHA
|
||||
return result
|
||||
|
||||
## Exporting an enum variable.[br]Example:
|
||||
## [codeblock]
|
||||
## var view_direction: ViewDirection
|
||||
## # ...
|
||||
## AtExport.enum_("view_direction", &"Portal3D.ViewDirection", ViewDirection)
|
||||
## [/codeblock]
|
||||
static func enum_(propname: String, parent_and_enum: StringName, enum_class: Variant) -> Dictionary:
|
||||
var result := int_(propname)
|
||||
|
||||
result["class_name"] = parent_and_enum
|
||||
result["hint"] = PROPERTY_HINT_ENUM
|
||||
result["hint_string"] = ",".join(enum_class.keys())
|
||||
result["usage"] |= PROPERTY_USAGE_CLASS_IS_ENUM
|
||||
|
||||
return result
|
||||
|
||||
## [annotation @GDScript.@export] float variables
|
||||
static func float_(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_FLOAT)
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_range] with float variables.
|
||||
## Also see [method int_range]
|
||||
static func float_range(propname: String, min: float, max: float, step: float = 0.01, extra_hints: Array[String] = []) -> Dictionary:
|
||||
var result := float_(propname)
|
||||
var hint_string = "%f,%f,%f" % [min, max, step]
|
||||
|
||||
if extra_hints.size() > 0:
|
||||
for h in extra_hints:
|
||||
hint_string += ("," + h)
|
||||
|
||||
result["hint"] = PROPERTY_HINT_RANGE
|
||||
result["hint_string"] = hint_string
|
||||
|
||||
return result
|
||||
|
||||
## [annotation @GDScript.@export] integer variables
|
||||
static func int_(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_INT)
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_flags]
|
||||
static func int_flags(propname: String, options: Array) -> Dictionary:
|
||||
var result := int_(propname)
|
||||
result["hint"] = PROPERTY_HINT_FLAGS
|
||||
result["hint_string"] = ",".join(options)
|
||||
return result
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_flags_3d_physics]
|
||||
static func int_physics_3d(propname: String) -> Dictionary:
|
||||
var result := int_(propname)
|
||||
result["hint"] = PROPERTY_HINT_LAYERS_3D_PHYSICS
|
||||
return result
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_range] with integer variables.
|
||||
## Also see [method float_range]
|
||||
static func int_range(propname: String, min: int, max: int, step: int = 1, extra_hints: Array[String] = []) -> Dictionary:
|
||||
var result := float_range(propname, min, max, step, extra_hints)
|
||||
result["type"] = TYPE_INT
|
||||
return result
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_flags_3d_render]
|
||||
static func int_render_3d(propname: String) -> Dictionary:
|
||||
var result := int_(propname)
|
||||
result["hint"] = PROPERTY_HINT_LAYERS_3D_RENDER
|
||||
return result
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_group].
|
||||
static func group(group_name: String, prefix: String = "") -> Dictionary:
|
||||
var result := _base(group_name, TYPE_NIL)
|
||||
# Overwrite the usage!
|
||||
result["usage"] = PROPERTY_USAGE_GROUP
|
||||
result["hint_string"] = prefix
|
||||
return result
|
||||
|
||||
## Close the group that began with [method group]. If you've supplied a prefix to [method group],
|
||||
## it should close itself.
|
||||
static func group_end() -> Dictionary:
|
||||
return group("")
|
||||
|
||||
## [annotation @GDScript.@export] NodePath variables. Variables of [i]node type[/i] also only store
|
||||
## [NodePath]s.
|
||||
## [codeblock]
|
||||
## var mesh: MeshInstance3D
|
||||
## # inside _get_property_list
|
||||
## AtExport.node("mesh", "MeshInstance3D")
|
||||
## [/codeblock]
|
||||
static func node(propname: String, node_class: StringName) -> Dictionary:
|
||||
var result = _base(propname, TYPE_OBJECT)
|
||||
result["hint"] = PROPERTY_HINT_NODE_TYPE
|
||||
result["class_name"] = node_class
|
||||
result["hint_string"] = node_class
|
||||
return result
|
||||
|
||||
## [annotation @GDScript.@export] for [String] variables
|
||||
static func string(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_STRING)
|
||||
|
||||
## Replacement for [annotation @GDScript.@export_subgroup]. Only works when nested inside
|
||||
## [method group].
|
||||
static func subgroup(subgroup_name: String, prefix: String = "") -> Dictionary:
|
||||
var result := _base(subgroup_name, TYPE_NIL)
|
||||
# Overwrite the usage!
|
||||
result["usage"] = PROPERTY_USAGE_SUBGROUP
|
||||
result["hint_string"] = prefix
|
||||
return result
|
||||
|
||||
## Closes a subgroup created with [method subgroup]. Also see [method group_end]
|
||||
static func subgroup_end() -> Dictionary:
|
||||
return subgroup("")
|
||||
|
||||
## [annotation @GDScript.@export] for [Vector2] variables
|
||||
static func vector2(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_VECTOR2)
|
||||
|
||||
## [annotation @GDScript.@export] for [Vector3] variables
|
||||
static func vector3(propname: String) -> Dictionary:
|
||||
return _base(propname, TYPE_VECTOR3)
|
1
addons/portals/scripts/at_export.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://d2ufiv5n1dcdr
|
967
addons/portals/scripts/portal_3d.gd
Normal file
@ -0,0 +1,967 @@
|
||||
@tool
|
||||
@icon("uid://ct62bsuel5hyc")
|
||||
class_name Portal3D extends Node3D
|
||||
|
||||
## Seamless 3D portal
|
||||
##
|
||||
## To get started, create two Portal3D instances and set their [member exit_portal] to each other.
|
||||
## This creates a linked portal pair that you can look through. Make your player to collide with
|
||||
## [member teleport_collision_mask] and you will be able to walk back and forth through the portal.
|
||||
## [br][br]
|
||||
## To integrate portals into your game, you can make use of the [signal on_teleport] and
|
||||
## [signal on_teleport_receive] signals. You can link a portal a different one by chaning its
|
||||
## [member exit_portal] during gameplay. The next level is to make use of the portal's callbacks,
|
||||
## mainly the [member ON_TELEPORT_CALLBACK]. If you need to raycast through a portal, then the
|
||||
## [method forward_raycast] method might come in handy! When it comes to optimization, you can use
|
||||
## the [method activate] and [method deactivate] methods to control which portals are consuming
|
||||
## resources.
|
||||
## [br][br]
|
||||
## [b]TIP:[/b] If you change the default value of some property, it will not get synchronized into existing
|
||||
## portal instances due to how Godot handles custom inspectors. For easier defaults management,
|
||||
## I recommend creating a scene with Portal3D as a root and re-using that.
|
||||
|
||||
|
||||
#region Public API
|
||||
|
||||
## Emitted when this portal teleports something. Also see [signal on_teleport_receive]
|
||||
signal on_teleport(node: Node3D)
|
||||
|
||||
## Emitted when this portal [i]receives[/i] a teleported node. Whoever had [b]this[/b] portal as
|
||||
## its [member exit_portal] triggered a teleport!
|
||||
signal on_teleport_receive(node: Node3D)
|
||||
|
||||
## Activates the portal, making it visible and teleporting again. THe assumption is that it was
|
||||
## previously deactivated by [method deactivate] or [member start_deactivated]. Recreates internal
|
||||
## viewports if needed.
|
||||
func activate() -> void:
|
||||
process_mode = Node.PROCESS_MODE_INHERIT
|
||||
|
||||
if portal_viewport == null:
|
||||
# Viewports have been destroyed
|
||||
_setup_cameras()
|
||||
|
||||
show()
|
||||
|
||||
|
||||
## Disables all processing (this includes teleportation) and hides the portal. Optionally destroys
|
||||
## the viewports, freeing up memory. [br][br]
|
||||
## Setting [member start_deactivated] to [code]true[/code] avoid viewport allocation at the start of
|
||||
## the game. [br][br]
|
||||
## Deactivated portal has to be explicitly activated by calling [method activate].
|
||||
func deactivate(destroy_viewports: bool = false) -> void:
|
||||
hide()
|
||||
_watchlist_teleportables.clear()
|
||||
|
||||
if destroy_viewports:
|
||||
if portal_viewport:
|
||||
portal_viewport.queue_free()
|
||||
portal_viewport = null
|
||||
portal_camera = null
|
||||
|
||||
process_mode = Node.PROCESS_MODE_DISABLED
|
||||
|
||||
## Helper method for checking for raycast collisions through portals. If your [RayCast3D] node hits
|
||||
## a portal collider, pass the [RayCast3D] node to this function to find out what's on the other
|
||||
## side of the portal! [br][br]
|
||||
## Uses [method PhysicsDirectSpaceState3D.intersect_ray] under the hood.[br][br]
|
||||
## Also see [method forward_raycast_query].
|
||||
func forward_raycast(raycast: RayCast3D) -> Dictionary:
|
||||
var start := to_exit_position(raycast.get_collision_point())
|
||||
var goal := to_exit_position(raycast.to_global(raycast.target_position))
|
||||
|
||||
var query = PhysicsRayQueryParameters3D.create(
|
||||
start,
|
||||
goal,
|
||||
raycast.collision_mask,
|
||||
[self.teleport_area, exit_portal.teleport_area]
|
||||
)
|
||||
query.collide_with_areas = raycast.collide_with_areas
|
||||
query.collide_with_bodies = raycast.collide_with_bodies
|
||||
query.hit_back_faces = raycast.hit_back_faces
|
||||
query.hit_from_inside = raycast.hit_from_inside
|
||||
|
||||
return get_world_3d().direct_space_state.intersect_ray(query)
|
||||
|
||||
## When doing raycasts with [method PhysicsDirectSpaceState3D.intersect_ray] and you hit a portal
|
||||
## that you want to go through, pass the [PhysicsRayQueryParameters3D] you are using to this
|
||||
## function. It will calculate the ray's continuation and execute the raycast again, returning the
|
||||
## result dictionary. [br][br]
|
||||
## If you are using [RayCast3D] for raycasting, see [method forward_raycast].
|
||||
func forward_raycast_query(params: PhysicsRayQueryParameters3D) -> Dictionary:
|
||||
var start := to_exit_position(params.from)
|
||||
var end := to_exit_position(params.to)
|
||||
start = exit_portal.line_intersection(start, end)
|
||||
|
||||
var excludes = [self.teleport_area, exit_portal.teleport_area]
|
||||
excludes.append_array(params.exclude)
|
||||
|
||||
var query = PhysicsRayQueryParameters3D.create(
|
||||
start, end, params.collision_mask, excludes
|
||||
)
|
||||
query.collide_with_areas = params.collide_with_areas
|
||||
query.collide_with_bodies = params.collide_with_bodies
|
||||
query.hit_back_faces = params.hit_back_faces
|
||||
query.hit_from_inside = params.hit_from_inside
|
||||
|
||||
return get_world_3d().direct_space_state.intersect_ray(query)
|
||||
|
||||
|
||||
## This method will be called on a teleported node if [member TeleportInteractions.CALLBACK]
|
||||
## is checked in [member teleport_interactions]. The portal will try to call the method
|
||||
## [code]on_teleport[/code] on any object being teleported by it.[br][br]
|
||||
## Example:
|
||||
## [codeblock]
|
||||
## func on_teleport(portal: Portal3D) -> void:
|
||||
## print("Teleported by %s!" % portal.name)
|
||||
## [/codeblock]
|
||||
const ON_TELEPORT_CALLBACK: StringName = &"on_teleport"
|
||||
|
||||
## This method will be called on a node that will get into close proximity of a portal that has
|
||||
## [member TeleportInteractions.DUPLICATE_MESHES] turned on. The method is expected to return an
|
||||
## array of [MeshInstance3D]s.[br][br]
|
||||
## Example:
|
||||
## [codeblock]
|
||||
## @onready var character_mesh: MeshInstance = $CharacterMesh
|
||||
##
|
||||
## func get_teleportable_meshes() -> Array[MeshInstance3D]:
|
||||
## return [character_mesh]
|
||||
## [/codeblock]
|
||||
##
|
||||
## The returned meshes require a special material. Check out the plugin's README for more
|
||||
## information!
|
||||
const DUPLICATE_MESHES_CALLBACK: StringName = &"get_teleportable_meshes"
|
||||
|
||||
## By default, object triggering the teleport gets teleported. You can override this with a
|
||||
## metadata property that contains a [NodePath]. If the metadata property is set, then the node at
|
||||
## the node path will be teleported instead. Setting this to ancestor nodes is recommended.[br][br]
|
||||
## Example:
|
||||
## [codeblock]
|
||||
## func _ready() -> void:
|
||||
## self.set_meta("teleport_root", ^"..") # parent
|
||||
## [/codeblock]
|
||||
## Or you can set the metadata property via the inspector!
|
||||
const TELEPORT_ROOT_META: StringName = &"teleport_root"
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
## Size of the portal rectangle, height and width.
|
||||
var portal_size: Vector2 = Vector2(2.0, 2.5):
|
||||
set(v):
|
||||
portal_size = v
|
||||
if _caused_by_user_interaction():
|
||||
_on_portal_size_changed()
|
||||
update_configuration_warnings()
|
||||
if exit_portal:
|
||||
exit_portal.update_configuration_warnings()
|
||||
|
||||
## The exit of this particular portal. Portal camera renders what it sees through this
|
||||
## [member exit_portal] and teleports take you here. This is a [b]required[/b] property, it
|
||||
## can never be [code]null[/code].
|
||||
## [br][br]
|
||||
## You can change this property during gameplay to switch the portal to a different destination.
|
||||
## To disable a portal, see [method deactivate].
|
||||
## [br][br]
|
||||
## [b]TIP:[/b] Commonly, two portals have set each other as [member exit_portal], which
|
||||
## allows you to travel back and forth. But you can experiment with one-way portals too!
|
||||
var exit_portal: Portal3D:
|
||||
set(v):
|
||||
exit_portal = v
|
||||
update_configuration_warnings()
|
||||
notify_property_list_changed()
|
||||
|
||||
var _tb_pair_portals: Callable = _editor_pair_portals.bind()
|
||||
var _tb_sync_portal_sizes: Callable = _editor_sync_portal_sizes.bind()
|
||||
|
||||
## Manually override what's the main camera of the scene. By default it's inferred as the camera
|
||||
## rendering the parent viewport of the portal. You might have to specify this, if your game uses
|
||||
## multiple [SubViewport]s.
|
||||
var player_camera: Camera3D
|
||||
|
||||
## The portal camera sets its [member Camera3D.near] as close to the portal as possible, in an
|
||||
## effort to clip objects close behind the portal. This value offsets the [member portal_camera]'s
|
||||
## near clip plane. Might be useful, if the portal has a thick frame around it.
|
||||
var portal_frame_width: float = 0
|
||||
|
||||
## Options for different sizes of the internal viewports. It helps to reduce the memory usage
|
||||
## by not rendering the portals at full resolution. Viewports are resized on window resize.
|
||||
enum PortalViewportSizeMode {
|
||||
## Render at full window resolution.
|
||||
FULL,
|
||||
## The portal will be [b]at most[/b] this wide. Height is calculated from window aspect ratio.
|
||||
MAX_WIDTH_ABSOLUTE,
|
||||
## Portal viewport will be a fraction of full window size.
|
||||
FRACTIONAL
|
||||
}
|
||||
|
||||
## Size mode to use for the portal viewport size. Only set this via the inspector.
|
||||
var viewport_size_mode: PortalViewportSizeMode = PortalViewportSizeMode.FULL:
|
||||
set(v):
|
||||
viewport_size_mode = v
|
||||
notify_property_list_changed()
|
||||
var _viewport_size_max_width_absolute: int = ProjectSettings.get_setting("display/window/size/viewport_width")
|
||||
var _viewport_size_fractional: float = 0.5
|
||||
|
||||
|
||||
## Hints the direction from which you expect the portal to be viewed.[br][br]
|
||||
## Use cases: one-way portals, visual-only portals (with [member is_teleport] set to
|
||||
## [code]false[/code]), or portals that are flush with a wall.
|
||||
enum ViewDirection {
|
||||
## Portal is expected to be viewed from either side (default)
|
||||
FRONT_AND_BACK,
|
||||
## Corresponds to portal's FORWARD direction (-Z)
|
||||
ONLY_FRONT,
|
||||
## Corresponds to portal's BACK direction (+Z)
|
||||
ONLY_BACK,
|
||||
}
|
||||
|
||||
## The direction from which you expect the portal to be viewed. Restricting this restricts the
|
||||
## way the portal mesh is shifted around when player looks at the portal from different sides.[br]
|
||||
## Restrict this if the portal can be seen from the sides and has no portal frame around it to
|
||||
## cover the shifting mesh.[br][br]
|
||||
## Also see [member teleport_direction]
|
||||
var view_direction: ViewDirection = ViewDirection.FRONT_AND_BACK
|
||||
|
||||
|
||||
## The [member portal_mesh] setting for [member VisualInstance3D.layers], so that the portal
|
||||
## cameras don't see other portals.
|
||||
var portal_render_layer: int = 1 << 19:
|
||||
set(v):
|
||||
portal_render_layer = v
|
||||
if _caused_by_user_interaction():
|
||||
portal_mesh.layers = v
|
||||
|
||||
## If [code]true[/code], the portal is also a teleport. If [code]false[/code], the portal is
|
||||
## visual-only.
|
||||
## [br][br]
|
||||
## You are expected to toggle this in the editor. For runtime teleport toggling, see
|
||||
## [method activate] and [method deactivate].
|
||||
var is_teleport: bool = true:
|
||||
set(v):
|
||||
is_teleport = v
|
||||
if _caused_by_user_interaction():
|
||||
_setup_teleport()
|
||||
notify_property_list_changed()
|
||||
|
||||
## Dictates from which direction an object has to enter the portal to be teleported.
|
||||
enum TeleportDirection {
|
||||
## Corresponds to portal's FORWARD direction (-Z)
|
||||
FRONT,
|
||||
## Corresponds to portal's BACK direction (+Z)
|
||||
BACK,
|
||||
## Teleports stuff coming from either side. (default)
|
||||
FRONT_AND_BACK
|
||||
}
|
||||
|
||||
## Portal will only teleport things coming from this direction.
|
||||
var teleport_direction: TeleportDirection = TeleportDirection.FRONT_AND_BACK
|
||||
|
||||
## When a [RigidBody3D] goes through the portal, give its new normalized velocity a
|
||||
## little boost. Makes stuff flying out of portals more fun. [br][br]
|
||||
## Recommended values: 1 to 3
|
||||
var rigidbody_boost: float = 0.0
|
||||
|
||||
## When teleporting, the portal checks if the teleported object is less than [b]this[/b] near.
|
||||
## Prevents false negatives when multiple portals are on top of each other.
|
||||
var teleport_tolerance: float = 0.5
|
||||
|
||||
## Flags for everything that happens when a something is teleported.
|
||||
enum TeleportInteractions {
|
||||
## The portal will try to call [constant ON_TELEPORT_CALLBACK] method on the teleported
|
||||
## node. You need to implement this function with a script.
|
||||
CALLBACK = 1 << 0,
|
||||
## When the player is teleported, his X and Z rotations are tweened to zero. Resets unwanted
|
||||
## from going through a tilted portal. If checked, this will happen BEFORE the callback.
|
||||
PLAYER_UPRIGHT = 1 << 1,
|
||||
## Duplicate meshes present on the teleported object, resulting in a [i]smooth teleport[/i]
|
||||
## from a 3rd point of view. [br]
|
||||
## To use this feature, implement a method named [constant DUPLICATE_MESHES_CALLBACK] on the
|
||||
## teleported body, which returns an array of mesh instances that should be duplicated.
|
||||
## Every one of those meshes also needs to implement a special shader material to clip it along
|
||||
## the portal plane.
|
||||
## See shaderinclude at [code]addons/portals/materials/portalclip_mesh.gdshaderinc[/code]
|
||||
DUPLICATE_MESHES = 1 << 2
|
||||
}
|
||||
|
||||
## See [enum TeleportInteractions] for options.
|
||||
var teleport_interactions: int = TeleportInteractions.CALLBACK \
|
||||
| TeleportInteractions.PLAYER_UPRIGHT
|
||||
|
||||
|
||||
## Any [CollisionObject3D]s detected by this mask will be registered by the portal and teleported,
|
||||
## when they cross the portal boundary.
|
||||
var teleport_collision_mask: int = 1 << 15
|
||||
|
||||
## If the portal is not immediately visible on scene start, you can start it in [i]disabled
|
||||
## mode[/i]. This just means it will not create the appropriate subviewports, saving memory.
|
||||
## It will also not be processed.[br][br]
|
||||
## You have to call [method activate] on it to wake it up! Also see [method disable]
|
||||
var start_deactivated: bool = false
|
||||
|
||||
#region INTERNALS
|
||||
|
||||
@export_storage var _portal_thickness: float = 0.05:
|
||||
set(v):
|
||||
_portal_thickness = v
|
||||
if _caused_by_user_interaction(): _on_portal_size_changed()
|
||||
|
||||
@export_storage var _portal_mesh_path: NodePath
|
||||
## Mesh used to visualize the portal surface. Created when the portal is added to the scene
|
||||
## [b]in the editor[/b].
|
||||
var portal_mesh: MeshInstance3D:
|
||||
get():
|
||||
return get_node(_portal_mesh_path) if _portal_mesh_path else null
|
||||
set(v): assert(false, "Proxy variable, use '_portal_mesh_path' instead")
|
||||
|
||||
@export_storage var _teleport_area_path: NodePath
|
||||
## When a teleportable object comes near the portal, it's registered by this area and watched
|
||||
## every frame to trigger the teleport. [br][br] Created by toggling [member is_teleport] in editor.
|
||||
var teleport_area: Area3D:
|
||||
get():
|
||||
return get_node(_teleport_area_path) if _teleport_area_path else null
|
||||
set(v): assert(false, "Proxy variable, use '_teleport_area_path' instead")
|
||||
|
||||
@export_storage var _teleport_collider_path: NodePath
|
||||
## Collider for [member teleport_area].
|
||||
var teleport_collider: CollisionShape3D:
|
||||
get():
|
||||
return get_node(_teleport_collider_path) if _teleport_collider_path else null
|
||||
set(v): assert(false, "Proxy variable, use '_teleport_collider_path' instead")
|
||||
|
||||
|
||||
## Camera that looks through the exit portal and renders to [member portal_viewport].
|
||||
## Created in [method Node._ready]
|
||||
var portal_camera: Camera3D = null
|
||||
|
||||
## Viewport that supplies the albedo texture to portal mesh. Rendered by [member portal_camera].
|
||||
## Created in [method Node._ready]
|
||||
var portal_viewport: SubViewport = null
|
||||
|
||||
## Metadata about teleported objects.
|
||||
##
|
||||
## When the portal detects a teleportable body (or area) nearby, it gathers this metadata and
|
||||
## starts watching it every frame for teleportation.
|
||||
class TeleportableMeta:
|
||||
## Forward distance from the portal
|
||||
var forward: float = 0
|
||||
## Meshes that the object gave for duplication. Retrieved by the
|
||||
## [constant Portal3D.DUPLICATE_MESHES_CALLBACK] callback.
|
||||
var meshes: Array[MeshInstance3D] = []
|
||||
## Cloned [member Portal3D.TeleportableMeta.meshes] with [method Node.duplicate]
|
||||
var mesh_clones: Array[MeshInstance3D] = []
|
||||
|
||||
# These physics bodies are being watched by the portal. They are registered with their instance IDs
|
||||
# as the keys of the dictionary. Registering them by their object references becomes unreliable
|
||||
# when the teleport candidate gets freed.
|
||||
var _watchlist_teleportables: Dictionary[int, TeleportableMeta] = {}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Editor Configuration
|
||||
|
||||
const _PORTAL_SHADER: Shader = preload("uid://bhdb2skdxehes")
|
||||
const _EDITOR_PREVIEW_PORTAL_MATERIAL: StandardMaterial3D = preload("uid://dcfkcyddxkglf")
|
||||
|
||||
# _ready(), but only in editor.
|
||||
func _editor_ready() -> void:
|
||||
add_to_group(PortalSettings.get_setting("portals_group_name"), true)
|
||||
set_notify_transform(true)
|
||||
|
||||
process_priority = 100
|
||||
process_physics_priority = 100
|
||||
|
||||
_setup_mesh()
|
||||
_setup_teleport()
|
||||
|
||||
self._group_node(self)
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_TRANSFORM_CHANGED:
|
||||
update_gizmos()
|
||||
|
||||
func _editor_pair_portals() -> void:
|
||||
assert(exit_portal != null, "My own exit has to be set!")
|
||||
exit_portal.exit_portal = self
|
||||
notify_property_list_changed()
|
||||
|
||||
func _editor_sync_portal_sizes() -> void:
|
||||
assert(exit_portal != null, "My own exit has to be set!")
|
||||
portal_size = exit_portal.portal_size
|
||||
notify_property_list_changed()
|
||||
|
||||
func _setup_teleport():
|
||||
if is_teleport == false:
|
||||
if teleport_area:
|
||||
teleport_area.queue_free()
|
||||
_teleport_area_path = NodePath("")
|
||||
if teleport_collider:
|
||||
teleport_collider.queue_free()
|
||||
_teleport_collider_path = NodePath("")
|
||||
return
|
||||
|
||||
# Teleport is already set up
|
||||
if teleport_area and teleport_collider:
|
||||
return
|
||||
|
||||
var area = Area3D.new()
|
||||
area.name = "TeleportArea"
|
||||
|
||||
_add_child_in_editor(self, area)
|
||||
_teleport_area_path = get_path_to(area)
|
||||
|
||||
var collider = CollisionShape3D.new()
|
||||
collider.name = "Collider"
|
||||
var box = BoxShape3D.new()
|
||||
box.size.x = portal_size.x
|
||||
box.size.y = portal_size.y
|
||||
collider.shape = box
|
||||
|
||||
_add_child_in_editor(teleport_area, collider)
|
||||
_teleport_collider_path = get_path_to(collider)
|
||||
|
||||
|
||||
func _on_portal_size_changed() -> void:
|
||||
if portal_mesh == null:
|
||||
push_error("Failed to update portal size, portal has no mesh")
|
||||
return
|
||||
|
||||
var p: PortalBoxMesh = portal_mesh.mesh
|
||||
p.size = Vector3(portal_size.x, portal_size.y, 1)
|
||||
portal_mesh.scale.z = _portal_thickness
|
||||
|
||||
if is_teleport and teleport_collider:
|
||||
var box: BoxShape3D = teleport_collider.shape
|
||||
box.size.x = portal_size.x
|
||||
box.size.y = portal_size.y
|
||||
|
||||
#endregion
|
||||
|
||||
#region GAMEPLAY LOGIC
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
_editor_ready.call_deferred()
|
||||
return
|
||||
|
||||
if player_camera == null:
|
||||
player_camera = get_viewport().get_camera_3d()
|
||||
assert(player_camera != null, "Player camera is missing!")
|
||||
|
||||
|
||||
var mat: ShaderMaterial = ShaderMaterial.new()
|
||||
mat.shader = _PORTAL_SHADER
|
||||
portal_mesh.material_override = mat
|
||||
|
||||
if not start_deactivated:
|
||||
_setup_cameras()
|
||||
get_viewport().size_changed.connect(_on_window_resize)
|
||||
else:
|
||||
deactivate.call_deferred(true)
|
||||
|
||||
if is_teleport:
|
||||
assert(teleport_area, "Teleport area should be already set up from editor")
|
||||
teleport_area.area_entered.connect(self._on_teleport_area_entered)
|
||||
teleport_area.area_exited.connect(self._on_teleport_area_exited)
|
||||
teleport_area.body_entered.connect(self._on_teleport_body_entered)
|
||||
teleport_area.body_exited.connect(self._on_teleport_body_exited)
|
||||
teleport_area.collision_mask = teleport_collision_mask
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if is_teleport:
|
||||
_process_teleports()
|
||||
|
||||
_process_cameras()
|
||||
|
||||
|
||||
func _process_cameras() -> void:
|
||||
if portal_camera == null:
|
||||
push_error("%s: No portal camera" % name)
|
||||
return
|
||||
if player_camera == null:
|
||||
push_error("%s: No player camera" % name)
|
||||
return
|
||||
if exit_portal == null:
|
||||
push_error("%s: No exit portal" % name)
|
||||
return
|
||||
|
||||
# Update camera
|
||||
portal_camera.global_transform = self.to_exit_transform(player_camera.global_transform)
|
||||
portal_camera.near = _calculate_near_plane()
|
||||
portal_camera.fov = player_camera.fov
|
||||
|
||||
# Prevent flickering
|
||||
var pv_size: Vector2i = portal_viewport.size
|
||||
var half_height: float = player_camera.near * tan(deg_to_rad(player_camera.fov * 0.5))
|
||||
var half_width: float = half_height * pv_size.x / float(pv_size.y)
|
||||
var near_diagonal: float = Vector3(half_width, half_height, player_camera.near).length()
|
||||
portal_mesh.scale.z = near_diagonal
|
||||
|
||||
var player_in_front_of_portal: bool = forward_distance(player_camera) > 0
|
||||
var portal_shift: float = 0
|
||||
match view_direction:
|
||||
ViewDirection.ONLY_FRONT:
|
||||
portal_shift = 1
|
||||
ViewDirection.ONLY_BACK:
|
||||
portal_shift = -1
|
||||
ViewDirection.FRONT_AND_BACK:
|
||||
portal_shift = 1 if player_in_front_of_portal else -1
|
||||
|
||||
portal_mesh.scale.z *= signf(portal_shift) # Turn the portal towards the player
|
||||
|
||||
|
||||
func _process_teleports() -> void:
|
||||
for body_id: int in _watchlist_teleportables.keys():
|
||||
if not is_instance_id_valid(body_id): # Watched body has been freed
|
||||
_erase_tp_metadata(body_id)
|
||||
continue
|
||||
|
||||
var tp_meta: TeleportableMeta = _watchlist_teleportables.get(body_id)
|
||||
var body = instance_from_id(body_id) as Node3D
|
||||
var last_fw_angle: float = tp_meta.forward
|
||||
var current_fw_angle: float = forward_distance(body)
|
||||
|
||||
var should_teleport: bool = false
|
||||
match teleport_direction:
|
||||
TeleportDirection.FRONT:
|
||||
should_teleport = last_fw_angle > 0 and current_fw_angle <= 0
|
||||
TeleportDirection.BACK:
|
||||
should_teleport = last_fw_angle < 0 and current_fw_angle >= 0
|
||||
TeleportDirection.FRONT_AND_BACK:
|
||||
should_teleport = sign(last_fw_angle) != sign(current_fw_angle)
|
||||
_:
|
||||
assert(false, "This match statement should be exhaustive")
|
||||
|
||||
if should_teleport and abs(current_fw_angle) < teleport_tolerance:
|
||||
var teleportable_path = body.get_meta(TELEPORT_ROOT_META, ".")
|
||||
var teleportable: Node3D = body.get_node(teleportable_path)
|
||||
|
||||
teleportable.global_transform = self.to_exit_transform(teleportable.global_transform)
|
||||
|
||||
if teleportable is RigidBody3D:
|
||||
teleportable.linear_velocity = to_exit_direction(teleportable.linear_velocity)
|
||||
teleportable.apply_central_impulse(
|
||||
teleportable.linear_velocity.normalized() * rigidbody_boost
|
||||
)
|
||||
|
||||
|
||||
on_teleport.emit(teleportable)
|
||||
exit_portal.on_teleport_receive.emit(teleportable)
|
||||
|
||||
# Force the cameras to refresh if we just teleported a player
|
||||
var was_player := not str(teleportable.get_path_to(player_camera)).begins_with(".")
|
||||
if was_player:
|
||||
_process_cameras()
|
||||
exit_portal._process_cameras()
|
||||
|
||||
# Resolve teleport interactions
|
||||
if was_player and _check_tp_interaction(TeleportInteractions.PLAYER_UPRIGHT):
|
||||
get_tree().create_tween().tween_property(teleportable, "rotation:x", 0, 0.3)
|
||||
get_tree().create_tween().tween_property(teleportable, "rotation:z", 0, 0.3)
|
||||
|
||||
if _check_tp_interaction(TeleportInteractions.CALLBACK):
|
||||
if teleportable.has_method(ON_TELEPORT_CALLBACK):
|
||||
teleportable.call(ON_TELEPORT_CALLBACK, self)
|
||||
|
||||
# transfer the thing to exit portal
|
||||
_transfer_tp_metadata_to_exit(body)
|
||||
else:
|
||||
tp_meta.forward = current_fw_angle
|
||||
for i in tp_meta.mesh_clones.size():
|
||||
var mesh = tp_meta.meshes[i]
|
||||
var clone = tp_meta.mesh_clones[i]
|
||||
clone.global_transform = to_exit_transform(mesh.global_transform)
|
||||
|
||||
func _calculate_near_plane() -> float:
|
||||
# Adjustment for cube portals. This AABB is basically a plane.
|
||||
var _aabb: AABB = AABB(
|
||||
Vector3(-exit_portal.portal_size.x / 2, -exit_portal.portal_size.y / 2, 0),
|
||||
Vector3(exit_portal.portal_size.x, exit_portal.portal_size.y, 0)
|
||||
)
|
||||
var _pos := _aabb.position
|
||||
var _size := _aabb.size
|
||||
|
||||
var corner_1: Vector3 = exit_portal.to_global(Vector3(_pos.x, _pos.y, 0))
|
||||
var corner_2: Vector3 = exit_portal.to_global(Vector3(_pos.x + _size.x, _pos.y, 0))
|
||||
var corner_3: Vector3 = exit_portal.to_global(Vector3(_pos.x + _size.x, _pos.y + _size.y, 0))
|
||||
var corner_4: Vector3 = exit_portal.to_global(Vector3(_pos.x, _pos.y + _size.y, 0))
|
||||
|
||||
# Calculate the distance along the exit camera forward vector at which each of the portal
|
||||
# corners projects
|
||||
var camera_forward: Vector3 = - portal_camera.global_transform.basis.z.normalized()
|
||||
|
||||
var d_1: float = (corner_1 - portal_camera.global_position).dot(camera_forward)
|
||||
var d_2: float = (corner_2 - portal_camera.global_position).dot(camera_forward)
|
||||
var d_3: float = (corner_3 - portal_camera.global_position).dot(camera_forward)
|
||||
var d_4: float = (corner_4 - portal_camera.global_position).dot(camera_forward)
|
||||
|
||||
# The near clip distance is the shortest distance which still contains all the corners
|
||||
return max(0.01, min(d_1, d_2, d_3, d_4) - exit_portal.portal_frame_width)
|
||||
|
||||
func _setup_mesh() -> void:
|
||||
if portal_mesh:
|
||||
return
|
||||
|
||||
var mi = MeshInstance3D.new()
|
||||
|
||||
mi = MeshInstance3D.new()
|
||||
mi.name = self.name + "_Mesh"
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
mi.layers = portal_render_layer
|
||||
|
||||
var p := PortalBoxMesh.new()
|
||||
p.size = Vector3(portal_size.x, portal_size.y, 1)
|
||||
mi.mesh = p
|
||||
mi.scale.z = _portal_thickness
|
||||
|
||||
# Editor-only material. Will be replaced when game starts.
|
||||
mi.material_override = _EDITOR_PREVIEW_PORTAL_MATERIAL
|
||||
|
||||
_add_child_in_editor(self, mi)
|
||||
_portal_mesh_path = get_path_to(mi)
|
||||
|
||||
func _setup_cameras() -> void:
|
||||
assert(not Engine.is_editor_hint(), "This should never run in editor")
|
||||
assert(portal_camera == null)
|
||||
assert(portal_viewport == null)
|
||||
|
||||
if exit_portal != null:
|
||||
portal_viewport = SubViewport.new()
|
||||
portal_viewport.name = self.name + "_SubViewport"
|
||||
portal_viewport.size = _calculate_viewport_size()
|
||||
self.add_child(portal_viewport, true)
|
||||
|
||||
# Disable tonemapping on portal cameras
|
||||
var adjusted_env: Environment = player_camera.environment.duplicate() \
|
||||
if player_camera.environment \
|
||||
else player_camera.get_world_3d().environment.duplicate()
|
||||
|
||||
adjusted_env.tonemap_mode = Environment.TONE_MAPPER_LINEAR
|
||||
adjusted_env.tonemap_exposure = 1
|
||||
|
||||
portal_camera = Camera3D.new()
|
||||
portal_camera.name = self.name + "_Camera3D"
|
||||
portal_camera.environment = adjusted_env
|
||||
|
||||
# Ensure that portals don't see other portals.
|
||||
portal_camera.cull_mask = portal_camera.cull_mask ^ portal_render_layer
|
||||
|
||||
portal_viewport.add_child(portal_camera, true)
|
||||
portal_camera.global_position = exit_portal.global_position
|
||||
|
||||
# Connect the viewport to the mesh. Mesh material setup has to run BEFORE this
|
||||
portal_mesh.material_override.set_shader_parameter("albedo", portal_viewport.get_texture())
|
||||
else:
|
||||
push_error("%s has no exit_portal! Failed to setup cameras." % name)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
func _on_teleport_area_entered(area: Area3D) -> void:
|
||||
if _watchlist_teleportables.has(area.get_instance_id()):
|
||||
# Already on watchlist
|
||||
return
|
||||
|
||||
_construct_tp_metadata(area)
|
||||
|
||||
func _on_teleport_body_entered(body: Node3D) -> void:
|
||||
if _watchlist_teleportables.has(body.get_instance_id()):
|
||||
# Already on watchlist
|
||||
return
|
||||
|
||||
_construct_tp_metadata(body)
|
||||
|
||||
func _on_teleport_area_exited(area: Area3D) -> void:
|
||||
_erase_tp_metadata(area.get_instance_id())
|
||||
|
||||
func _on_teleport_body_exited(body: Node3D) -> void:
|
||||
_erase_tp_metadata(body.get_instance_id())
|
||||
|
||||
func _on_window_resize() -> void:
|
||||
if portal_viewport:
|
||||
portal_viewport.size = _calculate_viewport_size()
|
||||
|
||||
#endregion
|
||||
|
||||
#region UTILS
|
||||
|
||||
func _construct_tp_metadata(node: Node3D) -> void:
|
||||
var meta = TeleportableMeta.new()
|
||||
meta.forward = forward_distance(node)
|
||||
|
||||
if _check_tp_interaction(TeleportInteractions.DUPLICATE_MESHES) and \
|
||||
node.has_method(DUPLICATE_MESHES_CALLBACK):
|
||||
meta.meshes = node.call(DUPLICATE_MESHES_CALLBACK)
|
||||
for m: MeshInstance3D in meta.meshes:
|
||||
var dupe = m.duplicate(0)
|
||||
dupe.name = m.name + "_Clone"
|
||||
meta.mesh_clones.append(dupe)
|
||||
self.add_child(dupe, true)
|
||||
|
||||
_enable_mesh_clipping(meta, self)
|
||||
|
||||
_watchlist_teleportables.set(node.get_instance_id(), meta)
|
||||
|
||||
func _erase_tp_metadata(node_id: int) -> void:
|
||||
var meta = _watchlist_teleportables.get(node_id)
|
||||
if meta != null:
|
||||
meta = meta as TeleportableMeta
|
||||
for m in meta.meshes: _disable_mesh_clipping(m)
|
||||
for c in meta.mesh_clones: c.queue_free()
|
||||
|
||||
_watchlist_teleportables.erase(node_id)
|
||||
|
||||
func _enable_mesh_clipping(meta: TeleportableMeta, along_portal: Portal3D) -> void:
|
||||
for mi: MeshInstance3D in meta.meshes:
|
||||
var clip_normal = signf(meta.forward) * along_portal.global_basis.z
|
||||
mi.set_instance_shader_parameter("portal_clip_active", true)
|
||||
mi.set_instance_shader_parameter("portal_clip_point", along_portal.global_position)
|
||||
mi.set_instance_shader_parameter("portal_clip_normal", clip_normal)
|
||||
|
||||
var exit = along_portal.exit_portal
|
||||
for clone: MeshInstance3D in meta.mesh_clones:
|
||||
var clip_normal = signf(meta.forward) * exit.global_basis.z
|
||||
clone.set_instance_shader_parameter("portal_clip_active", true)
|
||||
clone.set_instance_shader_parameter("portal_clip_point", exit.global_position)
|
||||
clone.set_instance_shader_parameter("portal_clip_normal", clip_normal)
|
||||
|
||||
func _disable_mesh_clipping(mi: MeshInstance3D) -> void:
|
||||
mi.set_instance_shader_parameter("portal_clip_active", false)
|
||||
|
||||
func _transfer_tp_metadata_to_exit(for_body: Node3D) -> void:
|
||||
if not exit_portal.is_teleport:
|
||||
return # One-way teleport scenario
|
||||
|
||||
var body_id = for_body.get_instance_id()
|
||||
var tp_meta = _watchlist_teleportables[body_id]
|
||||
if tp_meta == null:
|
||||
push_error("Attempted to trasfer teleport metadata for a node that is not being watched.")
|
||||
return
|
||||
|
||||
tp_meta.forward = exit_portal.forward_distance(for_body)
|
||||
_enable_mesh_clipping(tp_meta, exit_portal) # Switch, the main mesh is clipped by exit portal!
|
||||
|
||||
exit_portal._watchlist_teleportables.set(body_id, tp_meta)
|
||||
# NOTE: Not using '_erase_tp_metadata' here, as it also frees the cloned meshes!
|
||||
_watchlist_teleportables.erase(body_id)
|
||||
|
||||
## [b]Crucial[/b] piece of a portal - transforming where objects should appear
|
||||
## on the other side. Used for both cameras and teleports.
|
||||
func to_exit_transform(g_transform: Transform3D) -> Transform3D:
|
||||
var relative_to_portal: Transform3D = global_transform.affine_inverse() * g_transform
|
||||
var flipped: Transform3D = relative_to_portal.rotated(Vector3.UP, PI)
|
||||
var relative_to_target = exit_portal.global_transform * flipped
|
||||
return relative_to_target
|
||||
|
||||
|
||||
## Similar to [method to_exit_transform], but this one uses [member global_basis] for calculations,
|
||||
## so it [b]only transforms rotation[/b], since portal scale should aways be 1. Use for transforming
|
||||
## directions.
|
||||
func to_exit_direction(real: Vector3) -> Vector3:
|
||||
var relative_to_portal: Vector3 = global_basis.inverse() * real
|
||||
var flipped: Vector3 = relative_to_portal.rotated(Vector3.UP, PI)
|
||||
var relative_to_target: Vector3 = exit_portal.global_basis * flipped
|
||||
return relative_to_target
|
||||
|
||||
|
||||
## Similar to [method to_exit_transform], but expects a global position.
|
||||
func to_exit_position(g_pos: Vector3) -> Vector3:
|
||||
var local: Vector3 = global_transform.affine_inverse() * g_pos
|
||||
var rotated = local.rotated(Vector3.UP, PI)
|
||||
var local_at_exit: Vector3 = exit_portal.global_transform * rotated
|
||||
return local_at_exit
|
||||
|
||||
|
||||
## Calculates the dot product of portal's forward vector with the global
|
||||
## position of [param node] relative to the portal. Used for detecting teleports.
|
||||
## [br]
|
||||
## The result is positive when the node is in front of the portal. The value measures how far in
|
||||
## front (or behind) the other node is compared to the portal.
|
||||
func forward_distance(node: Node3D) -> float:
|
||||
var portal_front: Vector3 = self.global_transform.basis.z.normalized()
|
||||
var node_relative: Vector3 = (node.global_transform.origin - self.global_transform.origin)
|
||||
return portal_front.dot(node_relative)
|
||||
|
||||
# Helper function meant to be used in editor. Adds [param node] as a child to
|
||||
# [param parent]. Forces a readable name and sets the child's owner to the same
|
||||
# as parent's.
|
||||
func _add_child_in_editor(parent: Node, node: Node) -> void:
|
||||
parent.add_child(node, true)
|
||||
# self.owner is null if this node is the scene root. Supply self.
|
||||
node.owner = self if self.owner == null else self.owner
|
||||
|
||||
# Used to conditionally run property setters.
|
||||
# [br]
|
||||
# Setters fire both on editor set and when the scene starts up (the engine is
|
||||
# assigning exported members). This should prevent the second case.
|
||||
func _caused_by_user_interaction() -> bool:
|
||||
return Engine.is_editor_hint() and is_node_ready()
|
||||
|
||||
# Editor helper function. Groups nodes in 3D editor view.
|
||||
func _group_node(node: Node) -> void:
|
||||
node.set_meta("_edit_group_", true)
|
||||
|
||||
func _calculate_viewport_size() -> Vector2i:
|
||||
var vp_size: Vector2i = get_viewport().size
|
||||
var aspect_ratio: float = float(vp_size.x) / float(vp_size.y)
|
||||
|
||||
match viewport_size_mode:
|
||||
PortalViewportSizeMode.FULL:
|
||||
return vp_size
|
||||
PortalViewportSizeMode.MAX_WIDTH_ABSOLUTE:
|
||||
var width = min(_viewport_size_max_width_absolute, vp_size.x)
|
||||
return Vector2i(width, int(width / aspect_ratio))
|
||||
PortalViewportSizeMode.FRACTIONAL:
|
||||
return Vector2i(vp_size * _viewport_size_fractional)
|
||||
|
||||
push_error("Failed to determine desired viewport size")
|
||||
return Vector2i(
|
||||
ProjectSettings.get_setting("display/window/size/viewport_width"),
|
||||
ProjectSettings.get_setting("display/window/size/viewport_height")
|
||||
)
|
||||
|
||||
func _check_tp_interaction(flag: int) -> bool:
|
||||
return (teleport_interactions & flag) > 0
|
||||
|
||||
## Get a point where the portal plane intersects a line. Line [param start] and [param end]
|
||||
## are in global coordinates and so is the result. Used for forwarding raycast queries.
|
||||
func line_intersection(start: Vector3, end: Vector3) -> Vector3:
|
||||
var plane_normal = - global_basis.z
|
||||
var plane_point = global_position
|
||||
|
||||
var line_dir = end - start
|
||||
var denom = plane_normal.dot(line_dir)
|
||||
|
||||
if abs(denom) < 1e-6:
|
||||
return Vector3.ZERO # No intersection, line is parallel to the plane
|
||||
|
||||
var t = plane_normal.dot(plane_point - start) / denom
|
||||
return start + line_dir * t
|
||||
|
||||
#endregion
|
||||
|
||||
#region GODOT EDITOR INTEGRATIONS
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: Array[String] = []
|
||||
|
||||
var global_scale = global_basis.get_scale()
|
||||
if not global_scale.is_equal_approx(Vector3.ONE):
|
||||
warnings.append(
|
||||
("Portals should NOT be scaled. Global portal scale is %v, " % global_scale) +
|
||||
"but should be (1.0, 1.0, 1.0). Make sure the portal and any of portal parents " +
|
||||
"aren't scaled."
|
||||
)
|
||||
|
||||
if exit_portal == null:
|
||||
warnings.append("Exit portal is null")
|
||||
|
||||
if exit_portal != null:
|
||||
if not portal_size.is_equal_approx(exit_portal.portal_size):
|
||||
warnings.append(
|
||||
"Portal size should be the same as exit portal's (it's %s, but should be %s)" %
|
||||
[portal_size, exit_portal.portal_size]
|
||||
)
|
||||
|
||||
return PackedStringArray(warnings)
|
||||
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var config: Array[Dictionary] = []
|
||||
|
||||
config.append(AtExport.vector2("portal_size"))
|
||||
|
||||
if exit_portal != null and not portal_size.is_equal_approx(exit_portal.portal_size):
|
||||
config.append(
|
||||
AtExport.button("_tb_sync_portal_sizes", "Take Exit Portal's Size", "Vector2"))
|
||||
|
||||
config.append(AtExport.node("exit_portal", "Portal3D"))
|
||||
|
||||
if exit_portal != null and exit_portal.exit_portal == null:
|
||||
config.append(AtExport.button("_tb_pair_portals", "Pair Portals", "SliderJoint3D"))
|
||||
|
||||
|
||||
config.append(AtExport.group("Rendering"))
|
||||
config.append(AtExport.node("player_camera", "Camera3D"))
|
||||
config.append(AtExport.float_range("portal_frame_width", 0.0, 10.0, 0.01))
|
||||
|
||||
config.append(AtExport.enum_(
|
||||
"viewport_size_mode", &"Portal3D.PortalViewportSizeMode", PortalViewportSizeMode))
|
||||
|
||||
if viewport_size_mode == PortalViewportSizeMode.MAX_WIDTH_ABSOLUTE:
|
||||
config.append(AtExport.int_range("_viewport_size_max_width_absolute", 2, 4096))
|
||||
elif viewport_size_mode == PortalViewportSizeMode.FRACTIONAL:
|
||||
config.append(AtExport.float_range("_viewport_size_fractional", 0, 1))
|
||||
|
||||
config.append(AtExport.enum_("view_direction", &"Portal3D.ViewDirection", ViewDirection))
|
||||
|
||||
config.append(AtExport.int_render_3d("portal_render_layer"))
|
||||
|
||||
config.append(AtExport.group_end())
|
||||
|
||||
config.append(AtExport.bool_("is_teleport"))
|
||||
|
||||
if is_teleport:
|
||||
config.append(AtExport.group("Teleport"))
|
||||
|
||||
config.append(
|
||||
AtExport.enum_("teleport_direction", &"Portal3D.TeleportDirection", TeleportDirection))
|
||||
config.append(AtExport.float_range("rigidbody_boost", 0, 5, 0.1, ["or_greater"]))
|
||||
config.append(AtExport.float_range("teleport_tolerance", 0.0, 5.0, 0.1, ["or_greater"]))
|
||||
var opts: Array = TeleportInteractions.keys().map(func(s): return s.capitalize())
|
||||
config.append(AtExport.int_flags("teleport_interactions", opts))
|
||||
config.append(AtExport.int_physics_3d("teleport_collision_mask"))
|
||||
config.append(AtExport.group_end())
|
||||
|
||||
config.append(AtExport.group("Advanced"))
|
||||
config.append(AtExport.bool_("start_deactivated"))
|
||||
|
||||
return config
|
||||
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
return property in [
|
||||
&"portal_size",
|
||||
&"player_camera",
|
||||
&"portal_frame_width",
|
||||
&"_viewport_size_max_width_absolute",
|
||||
&"view_direction",
|
||||
&"portal_render_layer",
|
||||
&"teleport_direction",
|
||||
&"rigidbody_boost",
|
||||
&"teleport_tolerance",
|
||||
&"teleport_interactions",
|
||||
&"teleport_collision_mask",
|
||||
&"start_deactivated",
|
||||
]
|
||||
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
match property:
|
||||
&"portal_size":
|
||||
return Vector2(2, 2.5)
|
||||
&"portal_frame_width":
|
||||
return 0.0
|
||||
&"_viewport_size_max_width_absolute":
|
||||
return ProjectSettings.get_setting("display/window/size/viewport_width")
|
||||
&"view_direction":
|
||||
return ViewDirection.FRONT_AND_BACK
|
||||
&"portal_render_layer":
|
||||
return 1 << 19
|
||||
&"teleport_direction":
|
||||
return TeleportDirection.FRONT_AND_BACK
|
||||
&"rigidbody_boost":
|
||||
return 0.0
|
||||
&"teleport_tolerance":
|
||||
return 0.5
|
||||
&"teleport_interactions":
|
||||
return TeleportInteractions.CALLBACK | TeleportInteractions.PLAYER_UPRIGHT
|
||||
&"teleport_collision_mask":
|
||||
return 1 << 15
|
||||
&"start_deactivated":
|
||||
return false
|
||||
return null
|
||||
|
||||
#endregion
|
1
addons/portals/scripts/portal_3d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cw1r4c1d7beyv
|
96
addons/portals/scripts/portal_boxmesh.gd
Normal file
@ -0,0 +1,96 @@
|
||||
@tool
|
||||
extends ArrayMesh
|
||||
class_name PortalBoxMesh
|
||||
|
||||
## Inverted box with a flipped front side
|
||||
##
|
||||
## This mesh class generates a mesh similar to [BoxMesh]. However, its sides are all facing
|
||||
## [i]inwards[/i], except for the fron side, which is facing outwards. The origin point of this
|
||||
## mesh is in the middle of its front face, instead of in the center of its volume (like you'd
|
||||
## expect with a box).[br]
|
||||
## It is a special mesh built for portal surfaces. The front face provides a nice flat surface and
|
||||
## the other sides try to reduce clipping issues when traveling through portals. See [Portal3D]
|
||||
|
||||
@export var size: Vector3 = Vector3(1, 1, 1):
|
||||
set(v):
|
||||
size = v
|
||||
generate_portal_mesh()
|
||||
|
||||
func _init() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
generate_portal_mesh()
|
||||
|
||||
func generate_portal_mesh() -> void:
|
||||
var _start_time: int = Time.get_ticks_usec()
|
||||
clear_surfaces() # Reset
|
||||
|
||||
var surface_array: Array = []
|
||||
surface_array.resize(Mesh.ARRAY_MAX)
|
||||
|
||||
var verts: PackedVector3Array = PackedVector3Array()
|
||||
var uvs: PackedVector2Array = PackedVector2Array()
|
||||
var normals: PackedVector3Array = PackedVector3Array()
|
||||
var indices: PackedInt32Array = PackedInt32Array()
|
||||
|
||||
# Just to save some chars
|
||||
var w: float = size.x / 2
|
||||
var h: float = size.y / 2
|
||||
var depth: Vector3 = Vector3(0, 0, -size.z)
|
||||
|
||||
# Outside rect
|
||||
var TOP_LEFT: Vector3 = Vector3(-w, h, 0)
|
||||
var TOP_RIGHT: Vector3 = Vector3(w, h, 0)
|
||||
var BOTTOM_LEFT: Vector3 = Vector3(-w, -h, 0)
|
||||
var BOTTOM_RIGHT: Vector3 = Vector3(w, -h, 0)
|
||||
|
||||
|
||||
verts.append_array([
|
||||
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
|
||||
TOP_LEFT + depth, TOP_RIGHT + depth, BOTTOM_LEFT + depth, BOTTOM_RIGHT + depth,
|
||||
])
|
||||
uvs.append_array([
|
||||
Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1), # Front UVs
|
||||
Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1), # Back UVs (the same)
|
||||
])
|
||||
|
||||
# We are going for a flat-surface look here. Portals should be unshaded anyways.
|
||||
normals.append_array([
|
||||
Vector3.BACK, Vector3.BACK, Vector3.BACK, Vector3.BACK,
|
||||
Vector3.BACK, Vector3.BACK, Vector3.BACK, Vector3.BACK
|
||||
])
|
||||
|
||||
# 0 ----------- 1
|
||||
# | \ / |
|
||||
# | 4-------5 |
|
||||
# | | | |
|
||||
# | | | |
|
||||
# | 6-------7 |
|
||||
# | / \ |
|
||||
# 2 ----------- 3
|
||||
|
||||
# Triangles are clockwise!
|
||||
|
||||
indices.append_array([
|
||||
0, 1, 4,
|
||||
4, 1, 5, # Top section done
|
||||
1, 3, 5,
|
||||
5, 3, 7, # right section done
|
||||
3, 2, 7,
|
||||
7, 2, 6, # bottom section done
|
||||
2, 0, 6,
|
||||
6, 0, 4, # left section done
|
||||
|
||||
4, 5, 6,
|
||||
6, 5, 7, # back section done
|
||||
|
||||
0, 1, 2,
|
||||
2, 1, 3, # front section done
|
||||
])
|
||||
|
||||
surface_array[Mesh.ARRAY_VERTEX] = verts
|
||||
surface_array[Mesh.ARRAY_TEX_UV] = uvs
|
||||
surface_array[Mesh.ARRAY_NORMAL] = normals
|
||||
surface_array[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
|
||||
|
1
addons/portals/scripts/portal_boxmesh.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bxcel82b180o3
|
38
addons/portals/scripts/portal_settings.gd
Normal file
@ -0,0 +1,38 @@
|
||||
class_name PortalSettings extends Object
|
||||
|
||||
## Static helper class for portal project settings.
|
||||
##
|
||||
## Features helper methods for inserting addon-related settings into [ProjectSettings].
|
||||
## Used mainly in plugin initialization and for getting defaults in [Portal3D]
|
||||
|
||||
static func _qual_name(setting: String) -> String:
|
||||
return "addons/portals/" + setting
|
||||
|
||||
## Initializes a setting, it it's not present already. The setting is [i]basic[/i] by default.
|
||||
static func init_setting(setting: String,
|
||||
default_value: Variant,
|
||||
requires_restart: bool = false) -> void:
|
||||
setting = _qual_name(setting)
|
||||
|
||||
# This would mean the setting is already overriden
|
||||
if not ProjectSettings.has_setting(setting):
|
||||
ProjectSettings.set_setting(setting, default_value)
|
||||
|
||||
ProjectSettings.set_initial_value(setting, default_value)
|
||||
ProjectSettings.set_restart_if_changed(setting, requires_restart)
|
||||
ProjectSettings.set_as_basic(setting, true)
|
||||
|
||||
## See companion class [class AtExport], it has some utilities which might be helpful!
|
||||
static func add_info(config: Dictionary) -> void:
|
||||
var qual_name = _qual_name(config["name"])
|
||||
|
||||
config["name"] = qual_name
|
||||
# In case this is coming from AtExport, which is geared towards inspector properties
|
||||
config.erase("usage")
|
||||
|
||||
ProjectSettings.add_property_info(config)
|
||||
|
||||
## Calls [method ProjectSettings.get_setting]
|
||||
static func get_setting(setting: String) -> Variant:
|
||||
setting = _qual_name(setting)
|
||||
return ProjectSettings.get_setting(setting)
|
1
addons/portals/scripts/portal_settings.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://yb4p7f7n5gid
|
21
addons/proton_scatter/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 HungryProton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
23
addons/proton_scatter/demos/assets/brick.tscn
Normal file
@ -0,0 +1,23 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://b4ted6l27vuyd"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d1d1fag0m04yc" path="res://addons/proton_scatter/demos/assets/models/brick.glb" id="1_bkmk2"]
|
||||
[ext_resource type="Texture2D" uid="uid://dqa2jfs1jy0hq" path="res://addons/proton_scatter/demos/assets/textures/t_rock.jpg" id="2_235bd"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nwvh2"]
|
||||
albedo_color = Color(0.678431, 0.596078, 0.466667, 1)
|
||||
albedo_texture = ExtResource("2_235bd")
|
||||
uv1_scale = Vector3(0.75, 0.75, 0.75)
|
||||
uv1_triplanar = true
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_0rrnn"]
|
||||
size = Vector3(0.4, 0.4, 0.4)
|
||||
|
||||
[node name="brick" instance=ExtResource("1_bkmk2")]
|
||||
|
||||
[node name="Cube" parent="." index="0"]
|
||||
material_override = SubResource("StandardMaterial3D_nwvh2")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="." index="1"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" index="0"]
|
||||
shape = SubResource("BoxShape3D_0rrnn")
|
10
addons/proton_scatter/demos/assets/bush.tscn
Normal file
@ -0,0 +1,10 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://b8abs8me7ckgo"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dbb4culid55v5" path="res://addons/proton_scatter/demos/assets/models/bush.glb" id="1_kv8tm"]
|
||||
[ext_resource type="Material" uid="uid://bn3fr3m3glrnp" path="res://addons/proton_scatter/demos/assets/materials/m_bush.tres" id="2_bkwoq"]
|
||||
|
||||
[node name="bush" instance=ExtResource("1_kv8tm")]
|
||||
|
||||
[node name="Bush" parent="." index="0"]
|
||||
material_override = ExtResource("2_bkwoq")
|
||||
instance_shader_parameters/camera_bend_strength = 0.0
|
9
addons/proton_scatter/demos/assets/dead_branch.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://ctkii8aivl17n"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://cmqlv88xp71tw" path="res://addons/proton_scatter/demos/assets/models/dead_branch.glb" id="1_5foyv"]
|
||||
[ext_resource type="Material" uid="uid://d01d0h08lqqn6" path="res://addons/proton_scatter/demos/assets/materials/m_trunk.tres" id="2_tldro"]
|
||||
|
||||
[node name="dead_branch" instance=ExtResource("1_5foyv")]
|
||||
|
||||
[node name="DeadBranch" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2_tldro")
|
18
addons/proton_scatter/demos/assets/fence_planks.tscn
Normal file
@ -0,0 +1,18 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://bfcjigq0vdl4d"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://6gxiul1pw13t" path="res://addons/proton_scatter/demos/assets/models/fence_planks.glb" id="1"]
|
||||
[ext_resource type="Material" path="res://addons/proton_scatter/demos/assets/materials/m_fence.tres" id="2"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_fesk1"]
|
||||
size = Vector3(1, 0.5, 0.1)
|
||||
|
||||
[node name="fence_planks" instance=ExtResource("1")]
|
||||
|
||||
[node name="fence_planks2" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="." index="1"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.197684, 0.0236663)
|
||||
shape = SubResource("BoxShape3D_fesk1")
|
16
addons/proton_scatter/demos/assets/gobot.tscn
Normal file
@ -0,0 +1,16 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bmglbfn5jaubp"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d3f4d3m7n8tpr" path="res://addons/proton_scatter/demos/assets/models/gobot.glb" id="1_gfyx7"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_rhuea"]
|
||||
albedo_color = Color(0.278431, 0.54902, 0.74902, 1)
|
||||
metallic = 0.4
|
||||
metallic_specular = 0.2
|
||||
roughness = 0.15
|
||||
rim_enabled = true
|
||||
rim = 0.3
|
||||
|
||||
[node name="gobot" instance=ExtResource("1_gfyx7")]
|
||||
|
||||
[node name="Sphere001" parent="." index="0"]
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_rhuea")
|
9
addons/proton_scatter/demos/assets/grass.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://c3c76je2y6vfj"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://018flajgtx7t" path="res://addons/proton_scatter/demos/assets/models/grass.glb" id="1_203fe"]
|
||||
[ext_resource type="Material" uid="uid://c4mot1fo3siox" path="res://addons/proton_scatter/demos/assets/materials/m_grass.tres" id="2_sv1ar"]
|
||||
|
||||
[node name="grass" instance=ExtResource("1_203fe")]
|
||||
|
||||
[node name="Plane_011" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2_sv1ar")
|
9
addons/proton_scatter/demos/assets/grass_2.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cia3jakp3wj1d"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dcnm2ijk7hj4j" path="res://addons/proton_scatter/demos/assets/models/grass_2.glb" id="1_xyqky"]
|
||||
[ext_resource type="Material" uid="uid://c4mot1fo3siox" path="res://addons/proton_scatter/demos/assets/materials/m_grass.tres" id="2_63qe5"]
|
||||
|
||||
[node name="grass_2" instance=ExtResource("1_xyqky")]
|
||||
|
||||
[node name="Grass" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2_63qe5")
|
17
addons/proton_scatter/demos/assets/large_rock.tscn
Normal file
62
addons/proton_scatter/demos/assets/materials/grass.gdshader
Normal file
@ -0,0 +1,62 @@
|
||||
shader_type spatial;
|
||||
|
||||
render_mode depth_draw_opaque, cull_disabled;
|
||||
|
||||
// Texture settings
|
||||
uniform sampler2D texture_albedo : hint_default_white, repeat_disable;
|
||||
uniform sampler2D texture_gradient : hint_default_white, repeat_disable;
|
||||
uniform sampler2D texture_noise : hint_default_white;
|
||||
uniform float alpha_scissor_threshold : hint_range(0.0, 1.0);
|
||||
uniform vec4 transmission : source_color;
|
||||
uniform vec4 secondary_color : source_color;
|
||||
uniform float secondary_attenuation = 0.2;
|
||||
uniform float grass_height = 1.0;
|
||||
|
||||
// Wind settings
|
||||
uniform vec2 wind_direction = vec2(1, -0.5);
|
||||
uniform float wind_speed = 1.0;
|
||||
uniform float wind_strength = 2.0;
|
||||
uniform float noise_scale = 20.0;
|
||||
|
||||
instance uniform float camera_bend_strength : hint_range(0.0, 3.0) = 0.2;
|
||||
|
||||
varying float color;
|
||||
varying float height;
|
||||
|
||||
void vertex() {
|
||||
height = VERTEX.y;
|
||||
float influence = smoothstep(0, 1, height / 2.0);
|
||||
vec4 world_pos = MODEL_MATRIX * vec4(VERTEX, 1.0);
|
||||
vec2 uv = world_pos.xz / (noise_scale + 1e-2);
|
||||
vec2 panning_uv = uv + fract(TIME * wind_direction * wind_speed);
|
||||
float wind = texture(texture_noise, panning_uv).r * 2.0 - 0.4;
|
||||
color = texture(texture_noise, uv).r;
|
||||
|
||||
vec2 wind_offset = -wind_direction * wind_strength * influence * wind;
|
||||
world_pos.xz += wind_offset;
|
||||
world_pos.y -= wind * influence * smoothstep(0.0, height, wind_strength);
|
||||
|
||||
//Push the top vertex away from the camera to bend the grass clump
|
||||
float ndotv = 1.0 - dot(vec3(0.0, 1.0, 0.0), normalize(INV_VIEW_MATRIX[1].xyz));
|
||||
world_pos.xz += INV_VIEW_MATRIX[1].xz * camera_bend_strength * height * ndotv;
|
||||
|
||||
vec4 local_pos = inverse(MODEL_MATRIX) * world_pos;
|
||||
local_pos.x += wind_strength * influence * cos(TIME * 1.0) / 8.0;
|
||||
local_pos.z += wind_strength * influence * sin(TIME * 1.5) / 8.0;
|
||||
|
||||
VERTEX = local_pos.xyz;
|
||||
//NORMAL = vec3(0.0, 1.0, 0.0);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 tex = texture(texture_albedo, UV);
|
||||
if (tex.a < alpha_scissor_threshold) {
|
||||
discard;
|
||||
}
|
||||
|
||||
BACKLIGHT = transmission.rgb;
|
||||
vec4 gradient = texture(texture_gradient, vec2(height / grass_height, 0.0));
|
||||
float secondary_weight = smoothstep(0.0, 1.0, color - secondary_attenuation);
|
||||
ALBEDO = tex.rbg * gradient.rgb;
|
||||
//ALBEDO = mix(ALBEDO, secondary_color.rgb, secondary_weight);
|
||||
}
|
@ -0,0 +1 @@
|
||||
uid://cktbgarifkbc8
|
51
addons/proton_scatter/demos/assets/materials/leaves.gdshader
Normal file
@ -0,0 +1,51 @@
|
||||
shader_type spatial;
|
||||
|
||||
render_mode depth_draw_opaque, cull_disabled;
|
||||
|
||||
// Texture settings
|
||||
uniform sampler2D texture_albedo : hint_default_white, repeat_disable;
|
||||
uniform sampler2D texture_gradient : hint_default_white;
|
||||
uniform sampler2D texture_noise : hint_default_white;
|
||||
uniform float alpha_scissor_threshold : hint_range(0.0, 1.0);
|
||||
uniform vec4 transmission : source_color;
|
||||
uniform float total_height = 1.0;
|
||||
|
||||
// Wind settings
|
||||
uniform vec2 wind_direction = vec2(1, -0.5);
|
||||
uniform float wind_speed = 1.0;
|
||||
uniform float wind_strength = 2.0;
|
||||
uniform float noise_scale = 20.0;
|
||||
|
||||
varying float color;
|
||||
varying float height;
|
||||
|
||||
void vertex() {
|
||||
height = VERTEX.y;
|
||||
|
||||
vec4 world_pos = MODEL_MATRIX * vec4(VERTEX, 1.0);
|
||||
vec2 uv = (world_pos.xz + VERTEX.yy) / (noise_scale + 1e-2) ;
|
||||
vec2 panning_uv = uv + fract(TIME * wind_direction * wind_speed);
|
||||
float wind = texture(texture_noise, panning_uv).r * 2.0 - 0.4;
|
||||
color = texture(texture_noise, uv).r;
|
||||
|
||||
float wind_influence = smoothstep(0, 1, 1.0 - UV.y);
|
||||
vec2 wind_offset = -wind_direction * wind_strength * wind_influence * wind;
|
||||
world_pos.xz += wind_offset;
|
||||
world_pos.y -= wind * wind_influence * wind_strength * 0.45;
|
||||
|
||||
vec4 local_pos = inverse(MODEL_MATRIX) * world_pos;
|
||||
|
||||
VERTEX = local_pos.xyz;
|
||||
//NORMAL = vec3(0.0, 1.0, 0.0);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 tex = texture(texture_albedo, UV);
|
||||
if (tex.a < alpha_scissor_threshold) {
|
||||
discard;
|
||||
}
|
||||
|
||||
BACKLIGHT = transmission.rgb;
|
||||
vec4 gradient = texture(texture_gradient, vec2(height / total_height, 0.0));
|
||||
ALBEDO = tex.rbg * gradient.rgb;
|
||||
}
|
@ -0,0 +1 @@
|
||||
uid://bv00dvm0n7u46
|
37
addons/proton_scatter/demos/assets/materials/m_bush.tres
Normal file
@ -0,0 +1,37 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://bn3fr3m3glrnp"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://cktbgarifkbc8" path="res://addons/proton_scatter/demos/assets/materials/grass.gdshader" id="1_peshr"]
|
||||
[ext_resource type="Texture2D" uid="uid://b2a6ylo2enm4g" path="res://addons/proton_scatter/demos/assets/textures/t_bush.png" id="2_mbhvd"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_122hb"]
|
||||
offsets = PackedFloat32Array(0, 0.5, 1)
|
||||
colors = PackedColorArray(0.179688, 0.0759602, 0.0183228, 1, 0.386532, 0.390625, 0.0230687, 1, 1, 0.693237, 0.0687054, 1)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_i0bw2"]
|
||||
gradient = SubResource("Gradient_122hb")
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_eeqpx"]
|
||||
seed = 1
|
||||
frequency = 0.002
|
||||
|
||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7l0n1"]
|
||||
in_3d_space = true
|
||||
seamless = true
|
||||
seamless_blend_skirt = 0.65
|
||||
noise = SubResource("FastNoiseLite_eeqpx")
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_peshr")
|
||||
shader_parameter/texture_albedo = ExtResource("2_mbhvd")
|
||||
shader_parameter/texture_gradient = SubResource("GradientTexture1D_i0bw2")
|
||||
shader_parameter/texture_noise = SubResource("NoiseTexture2D_7l0n1")
|
||||
shader_parameter/alpha_scissor_threshold = 0.25
|
||||
shader_parameter/transmission = Color(0.619608, 0.541176, 0.101961, 1)
|
||||
shader_parameter/secondary_color = Color(0, 0, 0, 1)
|
||||
shader_parameter/secondary_attenuation = 0.2
|
||||
shader_parameter/grass_height = 0.829
|
||||
shader_parameter/wind_direction = Vector2(1, -0.5)
|
||||
shader_parameter/wind_speed = 0.5
|
||||
shader_parameter/wind_strength = 0.15
|
||||
shader_parameter/noise_scale = 6.0
|
@ -0,0 +1,6 @@
|
||||
[gd_resource type="SpatialMaterial" format=2]
|
||||
|
||||
[resource]
|
||||
resource_name = "wood"
|
||||
vertex_color_use_as_albedo = true
|
||||
albedo_color = Color( 0.568627, 0.466667, 0.372549, 1 )
|
37
addons/proton_scatter/demos/assets/materials/m_grass.tres
Normal file
@ -0,0 +1,37 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://c4mot1fo3siox"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://cktbgarifkbc8" path="res://addons/proton_scatter/demos/assets/materials/grass.gdshader" id="1_fntgl"]
|
||||
[ext_resource type="Texture2D" uid="uid://d23p13yi7asw0" path="res://addons/proton_scatter/demos/assets/textures/t_grass_2.png" id="2_1odx0"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_122hb"]
|
||||
offsets = PackedFloat32Array(0, 0.473451, 1)
|
||||
colors = PackedColorArray(0.179688, 0.0855483, 0.00322032, 1, 0.251693, 0.390625, 0.0117187, 1, 1, 0.964706, 0.129412, 1)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_i0bw2"]
|
||||
gradient = SubResource("Gradient_122hb")
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_eeqpx"]
|
||||
seed = 1
|
||||
frequency = 0.002
|
||||
|
||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7l0n1"]
|
||||
in_3d_space = true
|
||||
seamless = true
|
||||
seamless_blend_skirt = 0.65
|
||||
noise = SubResource("FastNoiseLite_eeqpx")
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_fntgl")
|
||||
shader_parameter/texture_albedo = ExtResource("2_1odx0")
|
||||
shader_parameter/texture_gradient = SubResource("GradientTexture1D_i0bw2")
|
||||
shader_parameter/texture_noise = SubResource("NoiseTexture2D_7l0n1")
|
||||
shader_parameter/alpha_scissor_threshold = 0.3
|
||||
shader_parameter/transmission = Color(0.737255, 0.72549, 0, 1)
|
||||
shader_parameter/secondary_color = Color(0, 0, 0, 1)
|
||||
shader_parameter/secondary_attenuation = 0.2
|
||||
shader_parameter/grass_height = 0.6
|
||||
shader_parameter/wind_direction = Vector2(1, -0.5)
|
||||
shader_parameter/wind_speed = 0.5
|
||||
shader_parameter/wind_strength = 0.15
|
||||
shader_parameter/noise_scale = 6.0
|
38
addons/proton_scatter/demos/assets/materials/m_leaves.tres
Normal file
@ -0,0 +1,38 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://djo80ucamk643"]
|
||||
|
||||
[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/grass.gdshader" id="1_8py1k"]
|
||||
[ext_resource type="Texture2D" uid="uid://cgenco43aneod" path="res://addons/proton_scatter/demos/assets/textures/t_leaves_1.png" id="2_l2uea"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_yy7fg"]
|
||||
offsets = PackedFloat32Array(0, 0.726872, 0.934272)
|
||||
colors = PackedColorArray(0.333333, 0.486275, 0.556863, 1, 0.496467, 0.55, 0.1485, 1, 0.898039, 0.670588, 0.0196078, 1)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_rwvaq"]
|
||||
gradient = SubResource("Gradient_yy7fg")
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_wpihy"]
|
||||
seed = 1
|
||||
frequency = 0.002
|
||||
|
||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture_tgrrr"]
|
||||
in_3d_space = true
|
||||
seamless = true
|
||||
seamless_blend_skirt = 0.65
|
||||
noise = SubResource("FastNoiseLite_wpihy")
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_8py1k")
|
||||
shader_parameter/alpha_scissor_threshold = 0.5
|
||||
shader_parameter/camera_bend_strength = 0.0
|
||||
shader_parameter/grass_height = 1.0
|
||||
shader_parameter/noise_scale = 12.0
|
||||
shader_parameter/secondary_attenuation = 0.2
|
||||
shader_parameter/secondary_color = Color(0, 0.305882, 0.211765, 1)
|
||||
shader_parameter/texture_albedo = ExtResource("2_l2uea")
|
||||
shader_parameter/texture_gradient = SubResource("GradientTexture1D_rwvaq")
|
||||
shader_parameter/texture_noise = SubResource("NoiseTexture_tgrrr")
|
||||
shader_parameter/transmission = Color(1, 0.975296, 0.943663, 1)
|
||||
shader_parameter/wind_direction = Vector2(1, -0.5)
|
||||
shader_parameter/wind_speed = 0.5
|
||||
shader_parameter/wind_strength = 0.05
|
@ -0,0 +1,6 @@
|
||||
[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://ds2hjlo70hglg"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bjdgw8o5tr1a3" path="res://addons/proton_scatter/demos/assets/textures/mushroom.png" id="1_y0tuv"]
|
||||
|
||||
[resource]
|
||||
albedo_texture = ExtResource("1_y0tuv")
|
@ -0,0 +1,35 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://d28lq2qtgdyie"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://bv00dvm0n7u46" path="res://addons/proton_scatter/demos/assets/materials/leaves.gdshader" id="1_hlncd"]
|
||||
[ext_resource type="Texture2D" uid="uid://ctpb1w0cr8tqc" path="res://addons/proton_scatter/demos/assets/textures/t_pine_branch.png" id="2_yef44"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_pookg"]
|
||||
offsets = PackedFloat32Array(0.38342, 0.694301, 1)
|
||||
colors = PackedColorArray(0.059375, 0.078125, 0.07, 1, 0.628287, 0.73, 0.1752, 1, 0.897921, 1, 0, 1)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_n86jv"]
|
||||
gradient = SubResource("Gradient_pookg")
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_t7o5y"]
|
||||
seed = 1
|
||||
frequency = 0.002
|
||||
|
||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_03p8g"]
|
||||
in_3d_space = true
|
||||
seamless = true
|
||||
seamless_blend_skirt = 0.65
|
||||
noise = SubResource("FastNoiseLite_t7o5y")
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_hlncd")
|
||||
shader_parameter/texture_albedo = ExtResource("2_yef44")
|
||||
shader_parameter/texture_gradient = SubResource("GradientTexture1D_n86jv")
|
||||
shader_parameter/texture_noise = SubResource("NoiseTexture2D_03p8g")
|
||||
shader_parameter/alpha_scissor_threshold = 0.3
|
||||
shader_parameter/transmission = Color(0.745098, 0.741176, 0, 1)
|
||||
shader_parameter/total_height = 4.046
|
||||
shader_parameter/wind_direction = Vector2(1, -0.5)
|
||||
shader_parameter/wind_speed = 0.2
|
||||
shader_parameter/wind_strength = 0.05
|
||||
shader_parameter/noise_scale = 12.0
|
10
addons/proton_scatter/demos/assets/materials/m_rock.tres
Normal file
@ -0,0 +1,10 @@
|
||||
[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://i0jgjmbbl2m5"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://drdh36j6mu3ah" path="res://addons/proton_scatter/demos/assets/textures/t_rock_dirty.png" id="1_hx37f"]
|
||||
|
||||
[resource]
|
||||
albedo_color = Color(0.439216, 0.407843, 0.388235, 1)
|
||||
albedo_texture = ExtResource("1_hx37f")
|
||||
metallic_specular = 0.3
|
||||
uv1_triplanar = true
|
||||
uv1_world_triplanar = true
|
@ -0,0 +1,7 @@
|
||||
[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://d01d0h08lqqn6"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://c7pop5xgpxtiv" path="res://addons/proton_scatter/demos/assets/textures/t_tree_bark_rough.png" id="1_g4son"]
|
||||
|
||||
[resource]
|
||||
albedo_color = Color(0.470588, 0.376471, 0.309804, 1)
|
||||
albedo_texture = ExtResource("1_g4son")
|
@ -0,0 +1,90 @@
|
||||
// Source: https://godotshaders.com/shader/toon-water-shader/
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
const float SMOOTHSTEP_AA = 0.01;
|
||||
uniform sampler2D surfaceNoise;
|
||||
uniform sampler2D distortNoise;
|
||||
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
|
||||
|
||||
uniform float beer_factor = 0.8;
|
||||
|
||||
uniform float foam_distance = 0.01;
|
||||
uniform float foam_max_distance = 0.4;
|
||||
uniform float foam_min_distance = 0.04;
|
||||
uniform vec4 foam_color: source_color = vec4(1.0);
|
||||
|
||||
uniform vec2 surface_noise_tiling = vec2(1.0, 4.0);
|
||||
uniform vec3 surface_noise_scroll = vec3(0.03, 0.03, 0.0);
|
||||
uniform float surface_noise_cutoff: hint_range(0, 1) = 0.777;
|
||||
uniform float surface_distortion_amount: hint_range(0, 1) = 0.27;
|
||||
|
||||
uniform vec4 _DepthGradientShallow: source_color = vec4(0.325, 0.807, 0.971, 0.725);
|
||||
uniform vec4 _DepthGradientDeep: source_color = vec4(0.086, 0.407, 1, 0.749);
|
||||
uniform float _DepthMaxDistance: hint_range(0, 1) = 1.0;
|
||||
uniform float _DepthFactor = 1.0;
|
||||
|
||||
uniform float roughness = 0.25;
|
||||
uniform float specular = 0.75;
|
||||
|
||||
varying vec2 noiseUV;
|
||||
varying vec2 distortUV;
|
||||
varying vec3 viewNormal;
|
||||
|
||||
|
||||
vec4 alphaBlend(vec4 top, vec4 bottom)
|
||||
{
|
||||
vec3 color = (top.rgb * top.a) + (bottom.rgb * (1.0 - top.a));
|
||||
float alpha = top.a + bottom.a * (1.0 - top.a);
|
||||
|
||||
return vec4(color, alpha);
|
||||
}
|
||||
|
||||
void vertex() {
|
||||
viewNormal = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;
|
||||
noiseUV = UV * surface_noise_tiling;
|
||||
distortUV = UV;
|
||||
}
|
||||
|
||||
void fragment(){
|
||||
// https://www.youtube.com/watch?v=Jq3he9Lbj7M
|
||||
float depthVal = texture(DEPTH_TEXTURE, SCREEN_UV).r;
|
||||
float depth = PROJECTION_MATRIX[3][2] / (depthVal + PROJECTION_MATRIX[2][2]);
|
||||
depth = depth + VERTEX.z;
|
||||
depth = exp(-depth * beer_factor);
|
||||
depth = 1.0 - depth;
|
||||
|
||||
// Still unsure how to get properly the NORMAL from the camera
|
||||
// This was generated by ChatGPT xD
|
||||
vec4 view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depthVal, 1.0);
|
||||
view_pos /= view_pos.w;
|
||||
vec3 existingNormal = normalize(cross( dFdx(view_pos.xyz), dFdy(view_pos.xyz)));
|
||||
|
||||
float normalDot = clamp(dot(existingNormal.xyz, viewNormal), 0.0, 1.0);
|
||||
float foamDistance = mix(foam_max_distance, foam_min_distance, normalDot);
|
||||
|
||||
float foamDepth = clamp(depth / foamDistance, 0.0, 1.0);
|
||||
float surfaceNoiseCutoff = foamDepth * surface_noise_cutoff;
|
||||
|
||||
vec4 distortNoiseSample = texture(distortNoise, distortUV);
|
||||
vec2 distortAmount = (distortNoiseSample.xy * 2.0 -1.0) * surface_distortion_amount;
|
||||
|
||||
vec2 noise_uv = vec2(
|
||||
(noiseUV.x + TIME * surface_noise_scroll.x) + distortAmount.x ,
|
||||
(noiseUV.y + TIME * surface_noise_scroll.y + distortAmount.y)
|
||||
);
|
||||
float surfaceNoiseSample = texture(surfaceNoise, noise_uv).r;
|
||||
float surfaceNoiseAmount = smoothstep(surfaceNoiseCutoff - SMOOTHSTEP_AA, surfaceNoiseCutoff + SMOOTHSTEP_AA, surfaceNoiseSample);
|
||||
|
||||
float waterDepth = clamp(depth / _DepthMaxDistance, 0.0, 1.0) * _DepthFactor;
|
||||
vec4 waterColor = mix(_DepthGradientShallow, _DepthGradientDeep, waterDepth);
|
||||
|
||||
vec4 surfaceNoiseColor = foam_color;
|
||||
surfaceNoiseColor.a *= surfaceNoiseAmount;
|
||||
vec4 color = alphaBlend(surfaceNoiseColor, waterColor);
|
||||
|
||||
ALBEDO = color.rgb;
|
||||
ALPHA = color.a;
|
||||
ROUGHNESS = roughness;
|
||||
SPECULAR = specular;
|
||||
}
|
@ -0,0 +1 @@
|
||||
uid://d0qg1ivc7h7j8
|
40
addons/proton_scatter/demos/assets/materials/m_water.tres
Normal file
@ -0,0 +1,40 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=6 format=3 uid="uid://c7mw5tryqfggw"]
|
||||
|
||||
[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/m_water.gdshader" id="1_j8rl3"]
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_7bjdc"]
|
||||
noise_type = 2
|
||||
fractal_type = 3
|
||||
|
||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_wxuht"]
|
||||
seamless = true
|
||||
noise = SubResource("FastNoiseLite_7bjdc")
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_dx86n"]
|
||||
noise_type = 2
|
||||
domain_warp_enabled = true
|
||||
|
||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_1j0ob"]
|
||||
seamless = true
|
||||
noise = SubResource("FastNoiseLite_dx86n")
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_j8rl3")
|
||||
shader_parameter/beer_factor = 4.0
|
||||
shader_parameter/foam_distance = 0.01
|
||||
shader_parameter/foam_max_distance = 0.345
|
||||
shader_parameter/foam_min_distance = 0.05
|
||||
shader_parameter/foam_color = Color(1, 1, 1, 0.784314)
|
||||
shader_parameter/surface_noise_tiling = Vector2(1, 4)
|
||||
shader_parameter/surface_noise_scroll = Vector3(0.03, 0.03, 0)
|
||||
shader_parameter/surface_noise_cutoff = 0.875
|
||||
shader_parameter/surface_distortion_amount = 0.65
|
||||
shader_parameter/_DepthGradientShallow = Color(0.435294, 0.647059, 0.972549, 0.72549)
|
||||
shader_parameter/_DepthGradientDeep = Color(0.0823529, 0.392157, 0.701961, 0.862745)
|
||||
shader_parameter/_DepthMaxDistance = 1.0
|
||||
shader_parameter/_DepthFactor = 1.0
|
||||
shader_parameter/roughness = 0.001
|
||||
shader_parameter/specular = 0.5
|
||||
shader_parameter/surfaceNoise = SubResource("NoiseTexture2D_1j0ob")
|
||||
shader_parameter/distortNoise = SubResource("NoiseTexture2D_wxuht")
|
BIN
addons/proton_scatter/demos/assets/models/brick.glb
Normal file
37
addons/proton_scatter/demos/assets/models/brick.glb.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://d1d1fag0m04yc"
|
||||
path="res://.godot/imported/brick.glb-d79404ecf88b29143e6e07e77bacb44c.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/brick.glb"
|
||||
dest_files=["res://.godot/imported/brick.glb-d79404ecf88b29143e6e07e77bacb44c.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/bush.glb
Normal file
37
addons/proton_scatter/demos/assets/models/bush.glb.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dbb4culid55v5"
|
||||
path="res://.godot/imported/bush.glb-28e0128066fe8d913839a6b96204b1c6.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/bush.glb"
|
||||
dest_files=["res://.godot/imported/bush.glb-28e0128066fe8d913839a6b96204b1c6.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/dead_branch.glb
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cmqlv88xp71tw"
|
||||
path="res://.godot/imported/dead_branch.glb-e4e41ce877f1ef0b2d20a7b89af5de7b.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/dead_branch.glb"
|
||||
dest_files=["res://.godot/imported/dead_branch.glb-e4e41ce877f1ef0b2d20a7b89af5de7b.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/fence_planks.glb
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://6gxiul1pw13t"
|
||||
path="res://.godot/imported/fence_planks.glb-4cee642c3e514323763ee9631fb323e9.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/fence_planks.glb"
|
||||
dest_files=["res://.godot/imported/fence_planks.glb-4cee642c3e514323763ee9631fb323e9.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/gobot.glb
Normal file
37
addons/proton_scatter/demos/assets/models/gobot.glb.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://d3f4d3m7n8tpr"
|
||||
path="res://.godot/imported/gobot.glb-36505aa16090f2bc2f34fbe5362f44e8.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/gobot.glb"
|
||||
dest_files=["res://.godot/imported/gobot.glb-36505aa16090f2bc2f34fbe5362f44e8.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/grass.glb
Normal file
37
addons/proton_scatter/demos/assets/models/grass.glb.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://018flajgtx7t"
|
||||
path="res://.godot/imported/grass.glb-0ef73576363e4c601b9f45b1787e1487.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/grass.glb"
|
||||
dest_files=["res://.godot/imported/grass.glb-0ef73576363e4c601b9f45b1787e1487.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/grass_2.glb
Normal file
37
addons/proton_scatter/demos/assets/models/grass_2.glb.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dcnm2ijk7hj4j"
|
||||
path="res://.godot/imported/grass_2.glb-2dc56a32acf64077863c701e8b94ea02.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/grass_2.glb"
|
||||
dest_files=["res://.godot/imported/grass_2.glb-2dc56a32acf64077863c701e8b94ea02.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/large_rock.glb
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bxqkgplyoipsx"
|
||||
path="res://.godot/imported/large_rock.glb-f7a7a73f49167cee4ed84e7342d1f507.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/large_rock.glb"
|
||||
dest_files=["res://.godot/imported/large_rock.glb-f7a7a73f49167cee4ed84e7342d1f507.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/mushrooms.glb
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c38uugpgw7hjm"
|
||||
path="res://.godot/imported/mushrooms.glb-64c83b02a53711f9983c978d53ab0f12.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/mushrooms.glb"
|
||||
dest_files=["res://.godot/imported/mushrooms.glb-64c83b02a53711f9983c978d53ab0f12.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/pine_tree.glb
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bhums0j31gm5n"
|
||||
path="res://.godot/imported/pine_tree.glb-662cc3d34707ccadde24f89b98fadf88.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/pine_tree.glb"
|
||||
dest_files=["res://.godot/imported/pine_tree.glb-662cc3d34707ccadde24f89b98fadf88.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/small_rock.glb
Normal file
@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://b81l25tbebki4"
|
||||
path="res://.godot/imported/small_rock.glb-9b9690e480edfa6e23f0243045338de9.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/small_rock.glb"
|
||||
dest_files=["res://.godot/imported/small_rock.glb-9b9690e480edfa6e23f0243045338de9.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={
|
||||
"materials": {
|
||||
"@MATERIAL:0": {
|
||||
"use_external/path": "uid://i0jgjmbbl2m5"
|
||||
}
|
||||
}
|
||||
}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
BIN
addons/proton_scatter/demos/assets/models/tree.glb
Normal file
37
addons/proton_scatter/demos/assets/models/tree.glb.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c3mfolo7c5uvh"
|
||||
path="res://.godot/imported/tree.glb-86fae6fb2428df7d74097b1a7c75b288.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/models/tree.glb"
|
||||
dest_files=["res://.godot/imported/tree.glb-86fae6fb2428df7d74097b1a7c75b288.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
9
addons/proton_scatter/demos/assets/mushroom.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bodkixm8bubes"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://c38uugpgw7hjm" path="res://addons/proton_scatter/demos/assets/models/mushrooms.glb" id="1_spmys"]
|
||||
[ext_resource type="Material" uid="uid://ds2hjlo70hglg" path="res://addons/proton_scatter/demos/assets/materials/m_mushroom.tres" id="2_y6jw1"]
|
||||
|
||||
[node name="mushrooms" instance=ExtResource("1_spmys")]
|
||||
|
||||
[node name="Sphere001" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2_y6jw1")
|
31
addons/proton_scatter/demos/assets/pine_tree.tscn
Normal file
@ -0,0 +1,31 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://caqxfqurbp3ku"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bhums0j31gm5n" path="res://addons/proton_scatter/demos/assets/models/pine_tree.glb" id="1_hw1e5"]
|
||||
[ext_resource type="Material" uid="uid://d01d0h08lqqn6" path="res://addons/proton_scatter/demos/assets/materials/m_trunk.tres" id="2_cgtpc"]
|
||||
[ext_resource type="Material" uid="uid://d28lq2qtgdyie" path="res://addons/proton_scatter/demos/assets/materials/m_pine_leaves.tres" id="2_xnytt"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_2xqpo"]
|
||||
radius = 0.0750397
|
||||
height = 1.3553
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uhp33"]
|
||||
radius = 0.101768
|
||||
height = 0.489166
|
||||
|
||||
[node name="pine_tree" instance=ExtResource("1_hw1e5")]
|
||||
|
||||
[node name="Trunk" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2_cgtpc")
|
||||
|
||||
[node name="Leaves" parent="." index="1"]
|
||||
surface_material_override/0 = ExtResource("2_xnytt")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="." index="2"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.661681, 0)
|
||||
shape = SubResource("CapsuleShape3D_2xqpo")
|
||||
|
||||
[node name="CollisionShape3D2" type="CollisionShape3D" parent="StaticBody3D" index="1"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.015189, 0)
|
||||
shape = SubResource("CapsuleShape3D_uhp33")
|
9
addons/proton_scatter/demos/assets/small_rock.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bltmr2xgs8nq1"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://b81l25tbebki4" path="res://addons/proton_scatter/demos/assets/models/small_rock.glb" id="1_e2qk6"]
|
||||
[ext_resource type="Material" uid="uid://i0jgjmbbl2m5" path="res://addons/proton_scatter/demos/assets/materials/m_rock.tres" id="2_clsfy"]
|
||||
|
||||
[node name="small_rock" instance=ExtResource("1_e2qk6")]
|
||||
|
||||
[node name="SmallRock" parent="." index="0"]
|
||||
surface_material_override/0 = ExtResource("2_clsfy")
|
BIN
addons/proton_scatter/demos/assets/source.blend
Normal file
53
addons/proton_scatter/demos/assets/source.blend.import
Normal file
@ -0,0 +1,53 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c45jyo5mdo5pj"
|
||||
path="res://.godot/imported/source.blend-6553bbdea542ba64489fdff7990920e8.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/source.blend"
|
||||
dest_files=["res://.godot/imported/source.blend-6553bbdea542ba64489fdff7990920e8.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
blender/nodes/visible=0
|
||||
blender/nodes/active_collection_only=false
|
||||
blender/nodes/punctual_lights=true
|
||||
blender/nodes/cameras=true
|
||||
blender/nodes/custom_properties=true
|
||||
blender/nodes/modifiers=1
|
||||
blender/meshes/colors=false
|
||||
blender/meshes/uvs=true
|
||||
blender/meshes/normals=true
|
||||
blender/meshes/export_geometry_nodes_instances=false
|
||||
blender/meshes/tangents=true
|
||||
blender/meshes/skins=2
|
||||
blender/meshes/export_bones_deforming_mesh_only=false
|
||||
blender/materials/unpack_enabled=true
|
||||
blender/materials/export_materials=1
|
||||
blender/animation/limit_playback=true
|
||||
blender/animation/always_sample=true
|
||||
blender/animation/group_tracks=true
|
BIN
addons/proton_scatter/demos/assets/textures/grid.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
35
addons/proton_scatter/demos/assets/textures/grid.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://6xc5b38d25gf"
|
||||
path.s3tc="res://.godot/imported/grid.png-491581bd748087c94a4b25c27dcb904c.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/grid.png"
|
||||
dest_files=["res://.godot/imported/grid.png-491581bd748087c94a4b25c27dcb904c.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
|
BIN
addons/proton_scatter/demos/assets/textures/mushroom.png
Normal file
After Width: | Height: | Size: 264 KiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bjdgw8o5tr1a3"
|
||||
path.s3tc="res://.godot/imported/mushroom.png-36c0c492b0f6a79e2aa68780d9a86c03.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/mushroom.png"
|
||||
dest_files=["res://.godot/imported/mushroom.png-36c0c492b0f6a79e2aa68780d9a86c03.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
|
BIN
addons/proton_scatter/demos/assets/textures/sky_2.png
Normal file
After Width: | Height: | Size: 4.6 MiB |
35
addons/proton_scatter/demos/assets/textures/sky_2.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bgc5rl13dopuj"
|
||||
path.s3tc="res://.godot/imported/sky_2.png-3246d9ba45b69131effdf515c69428b4.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/sky_2.png"
|
||||
dest_files=["res://.godot/imported/sky_2.png-3246d9ba45b69131effdf515c69428b4.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_bush.png
Normal file
After Width: | Height: | Size: 424 KiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b2a6ylo2enm4g"
|
||||
path.s3tc="res://.godot/imported/t_bush.png-644d0e155c07db6d89949c275e110f2a.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_bush.png"
|
||||
dest_files=["res://.godot/imported/t_bush.png-644d0e155c07db6d89949c275e110f2a.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_grass.png
Normal file
After Width: | Height: | Size: 76 KiB |
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c08mm3p2ehvr6"
|
||||
path="res://.godot/imported/t_grass.png-2144df75763a0a189eba3035fc0b94aa.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_grass.png"
|
||||
dest_files=["res://.godot/imported/t_grass.png-2144df75763a0a189eba3035fc0b94aa.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_grass_2.png
Normal file
After Width: | Height: | Size: 101 KiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d23p13yi7asw0"
|
||||
path.s3tc="res://.godot/imported/t_grass_2.png-e3f17c2ee365553e0f39f2b5865e73de.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_grass_2.png"
|
||||
dest_files=["res://.godot/imported/t_grass_2.png-e3f17c2ee365553e0f39f2b5865e73de.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_leaves_1.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://drmmcy11y7mho"
|
||||
path.s3tc="res://.godot/imported/t_leaves_1.png-1d55b008d9a51575d696e027028d7b90.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_leaves_1.png"
|
||||
dest_files=["res://.godot/imported/t_leaves_1.png-1d55b008d9a51575d696e027028d7b90.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_pine_branch.png
Normal file
After Width: | Height: | Size: 120 KiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ctpb1w0cr8tqc"
|
||||
path.s3tc="res://.godot/imported/t_pine_branch.png-912fabf99bebd2eee6af2f445a54650e.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_pine_branch.png"
|
||||
dest_files=["res://.godot/imported/t_pine_branch.png-912fabf99bebd2eee6af2f445a54650e.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_rock.jpg
Normal file
After Width: | Height: | Size: 345 KiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dqa2jfs1jy0hq"
|
||||
path.s3tc="res://.godot/imported/t_rock.jpg-ae52c049ee9fab72f1ddf136050fd9ee.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_rock.jpg"
|
||||
dest_files=["res://.godot/imported/t_rock.jpg-ae52c049ee9fab72f1ddf136050fd9ee.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_rock_dirty.png
Normal file
After Width: | Height: | Size: 124 KiB |
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://drdh36j6mu3ah"
|
||||
path.s3tc="res://.godot/imported/t_rock_dirty.png-da395e5af8ffe9e04730e7e21eb6a86a.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/proton_scatter/demos/assets/textures/t_rock_dirty.png"
|
||||
dest_files=["res://.godot/imported/t_rock_dirty.png-da395e5af8ffe9e04730e7e21eb6a86a.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
|
BIN
addons/proton_scatter/demos/assets/textures/t_sand.png
Normal file
After Width: | Height: | Size: 744 KiB |