└── addons └── light_sensor_3d ├── icon.png ├── plugin.cfg ├── plugin.gd ├── example ├── sensor_label.gd └── root.tscn ├── icon.png.import ├── LICENSE.md ├── light_sensor_scene.tscn ├── light_sensor_3d_gizmo_plugin.gd ├── light_sensor_3d.gd └── README.md /addons/light_sensor_3d/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanodeath/godot-light-sensor/HEAD/addons/light_sensor_3d/icon.png -------------------------------------------------------------------------------- /addons/light_sensor_3d/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Light Sensor 3D" 4 | description="Detect the amount of light and color reaching a particular point/surface." 5 | author="Max Aller" 6 | version="1.0.1" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const GizmoPlugin = preload("res://addons/light_sensor_3d/light_sensor_3d_gizmo_plugin.gd") 5 | 6 | var gizmo_plugin = GizmoPlugin.new() 7 | 8 | func _enter_tree(): 9 | add_node_3d_gizmo_plugin(gizmo_plugin) 10 | 11 | func _exit_tree(): 12 | remove_node_3d_gizmo_plugin(gizmo_plugin) 13 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/example/sensor_label.gd: -------------------------------------------------------------------------------- 1 | extends Label3D 2 | 3 | @export var light_probe: LightSensor3D 4 | @export var log_color_change_event: bool 5 | @export var log_light_level_change_event: bool 6 | 7 | func _ready(): 8 | # These logs are just kinda for fun. 9 | 10 | if log_color_change_event: 11 | light_probe.color_updated.connect( 12 | func (new_color: Color): print(get_parent().name, ": color updated to ", new_color)) 13 | 14 | if log_light_level_change_event: 15 | light_probe.light_level_updated.connect( 16 | func (new_lum: float): print(get_parent().name, ": luminance updated to ", new_lum)) 17 | 18 | func _process(_delta): 19 | text = "%.2f" % light_probe.light_level 20 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bxixh6nyo1xdj" 6 | path="res://.godot/imported/icon.png-a585805b9ed1f594c85e64c52122a7b3.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/light_sensor_3d/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-a585805b9ed1f594c85e64c52122a7b3.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Max Aller 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /addons/light_sensor_3d/light_sensor_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://y2eiqhi3ylv5"] 2 | 3 | [sub_resource type="Environment" id="Environment_subviewport"] 4 | background_mode = 1 5 | 6 | [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_jxs1c"] 7 | shading_mode = 2 8 | 9 | [sub_resource type="PlaneMesh" id="PlaneMesh_ojrjg"] 10 | material = SubResource("StandardMaterial3D_jxs1c") 11 | size = Vector2(0.1, 0.1) 12 | orientation = 2 13 | 14 | [sub_resource type="ViewportTexture" id="ViewportTexture_tmbga"] 15 | viewport_path = NodePath("SubViewport") 16 | 17 | [node name="LightSensorScene" type="Node3D"] 18 | 19 | [node name="SubViewport" type="SubViewport" parent="."] 20 | handle_input_locally = false 21 | size = Vector2i(4, 4) 22 | render_target_update_mode = 4 23 | 24 | [node name="Camera3D" type="Camera3D" parent="SubViewport"] 25 | transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.1, 0) 26 | cull_mask = 0 27 | environment = SubResource("Environment_subviewport") 28 | projection = 1 29 | size = 0.1 30 | far = 250.0 31 | 32 | [node name="SensorMesh" type="MeshInstance3D" parent="SubViewport/Camera3D"] 33 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.37114e-09, -0.1) 34 | layers = 0 35 | cast_shadow = 0 36 | gi_mode = 0 37 | mesh = SubResource("PlaneMesh_ojrjg") 38 | 39 | [node name="RemoteTransform3D" type="RemoteTransform3D" parent="."] 40 | transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.1, 0) 41 | remote_path = NodePath("../SubViewport/Camera3D") 42 | 43 | [node name="DebugViewportSprite" type="Sprite3D" parent="."] 44 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.25, 0, 0) 45 | cast_shadow = 0 46 | pixel_size = 0.1 47 | axis = 1 48 | texture_filter = 0 49 | texture = SubResource("ViewportTexture_tmbga") 50 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/light_sensor_3d_gizmo_plugin.gd: -------------------------------------------------------------------------------- 1 | extends EditorNode3DGizmoPlugin 2 | 3 | const sensor_distance := 0.1 4 | const sensor_distance_half := sensor_distance / 2.0 5 | const sensor_distance_quarter := sensor_distance / 4.0 6 | const sensor_half_size := 0.1 / 2.0 7 | 8 | const arrow_color := Color.WHITE 9 | const sensor_color := Color.ORANGE 10 | 11 | func _get_gizmo_name(): 12 | return "LightSensor3D" 13 | 14 | func _has_gizmo(node): 15 | return node is LightSensor3D 16 | 17 | func _init(): 18 | create_material("main", Color(1, 1, 1)) 19 | create_material("secondary", sensor_color) 20 | create_handle_material("handles") 21 | 22 | 23 | func _redraw(gizmo): 24 | gizmo.clear() 25 | 26 | var node3d = gizmo.get_node_3d() as LightSensor3D 27 | 28 | var lines := PackedVector3Array() 29 | 30 | lines.push_back(Vector3(0, sensor_distance, 0)) 31 | lines.push_back(Vector3.ZERO) 32 | 33 | lines.push_back(Vector3(sensor_distance_quarter, sensor_distance_half, 0)) 34 | lines.push_back(Vector3.ZERO) 35 | 36 | lines.push_back(Vector3(-sensor_distance_quarter, sensor_distance_half, 0)) 37 | lines.push_back(Vector3.ZERO) 38 | 39 | lines.push_back(Vector3(0, sensor_distance_half, sensor_distance_quarter)) 40 | lines.push_back(Vector3.ZERO) 41 | 42 | lines.push_back(Vector3(0, sensor_distance_half, -sensor_distance_quarter)) 43 | lines.push_back(Vector3.ZERO) 44 | 45 | var lines2 = PackedVector3Array() 46 | 47 | lines2.push_back(Vector3(sensor_half_size, 0, sensor_half_size)) # tr 48 | lines2.push_back(Vector3(sensor_half_size, 0, -sensor_half_size)) # br 49 | 50 | lines2.push_back(Vector3(sensor_half_size, 0, -sensor_half_size)) # br 51 | lines2.push_back(Vector3(-sensor_half_size, 0, -sensor_half_size)) # bl 52 | 53 | lines2.push_back(Vector3(-sensor_half_size, 0, -sensor_half_size)) # bl 54 | lines2.push_back(Vector3(-sensor_half_size, 0, sensor_half_size)) # tl 55 | 56 | lines2.push_back(Vector3(-sensor_half_size, 0, sensor_half_size)) # tl 57 | lines2.push_back(Vector3(sensor_half_size, 0, sensor_half_size)) # tr 58 | 59 | gizmo.add_lines(lines, get_material("main", gizmo)) 60 | gizmo.add_lines(lines2, get_material("secondary", gizmo)) 61 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/light_sensor_3d.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/light_sensor_3d/icon.png") 2 | @tool 3 | class_name LightSensor3D extends Node3D 4 | 5 | ## Emitted when the color the sensor observes has changed (after refresh()). 6 | signal color_updated(color: Color) 7 | 8 | ## Emitted when the light level the sensor observes has changed (after refresh()). 9 | ## Ranges from 0 (pitch dark) to 1 (bright as the sun). 10 | signal light_level_updated(luminance: float) 11 | 12 | ## Configure a layer for the sensor probe. 13 | ## Choose a layer visible to your lights but invisible to your camera. 14 | @export_flags_3d_render var layer: int = 0: 15 | set(value): 16 | layer = value 17 | update_configuration_warnings() 18 | 19 | @export_group("Advanced") 20 | ## Renders a square next to the sensor that shows what the subviewport is seeing. 21 | @export var enable_subviewport_debug = false 22 | 23 | ## Prints out how long the sensor took every time it refreshes. 24 | @export var print_timing_information = false 25 | 26 | ## Color recorded by the probe during refresh(). 27 | ## See also: light_level() 28 | var color: Color = Color.BLACK 29 | 30 | ## Gets the luminance of the last color generated using refresh(). 31 | ## Ranges from 0 (pitch dark) to 1 (bright as the sun). 32 | ## See also: color 33 | var light_level: float: 34 | get: 35 | # This calculates and returns the luminance. 36 | # Color#luminance exists but sounds broken: 37 | # https://github.com/godotengine/godot/issues/57015 38 | return 0.299 * color.r + 0.587 * color.g + 0.114 * color.b 39 | 40 | var _scene: Node3D 41 | var _sub_viewport: SubViewport 42 | 43 | func _ready(): 44 | if Engine.is_editor_hint(): 45 | return 46 | 47 | _scene = preload("res://addons/light_sensor_3d/light_sensor_scene.tscn").instantiate() as Node3D 48 | add_child(_scene) 49 | 50 | _sub_viewport = _scene.get_node("SubViewport") as SubViewport 51 | var camera := _scene.get_node("SubViewport/Camera3D") as Camera3D 52 | var sensor_mesh := _scene.get_node("SubViewport/Camera3D/SensorMesh") as MeshInstance3D 53 | 54 | camera.cull_mask = layer 55 | sensor_mesh.layers = layer 56 | 57 | 58 | var debug_sprite := _scene.get_node("DebugViewportSprite") as Sprite3D 59 | debug_sprite.visible = enable_subviewport_debug 60 | 61 | if print_timing_information: 62 | print_debug(get_path(), ": This LightSensor3D is configured to print out timing information.") 63 | 64 | ## Recalculates the light/color affecting this probe. 65 | func refresh() -> void: 66 | var t_start = Time.get_ticks_usec() 67 | var texture := _sub_viewport.get_texture() 68 | var image := texture.get_image() # this one's a doozy 69 | 70 | # Next, we want to get every pixel and average their colors. 71 | # Presumably calling get_data() once is faster than get_pixel() many times. 72 | var color_data := image.get_data() 73 | var color_data_size := color_data.size() 74 | assert(color_data_size % 3 == 0, "Expected 3 channels per pixel") 75 | 76 | # Sum all the values for each channel separately. 77 | var average_color_array := [0, 0, 0] 78 | for i in color_data.size(): 79 | average_color_array[i % 3] += color_data.decode_u8(i) 80 | 81 | # Finally, convert the sums of rgb values into the average color. 82 | var pixel_count := color_data.size() / 3.0 83 | var average_color := Color( 84 | average_color_array[0] / pixel_count / 255, 85 | average_color_array[1] / pixel_count / 255, 86 | average_color_array[2] / pixel_count / 255, 87 | ) 88 | 89 | var t_end := Time.get_ticks_usec() 90 | if print_timing_information: 91 | print(get_path(), " (a LightSensor3D) took %.2fms to refresh" % ((t_end - t_start) / 1000.0)) 92 | 93 | # Trigger updates if the color changed 94 | if not color.is_equal_approx(average_color): 95 | color = average_color 96 | color_updated.emit(color) 97 | light_level_updated.emit(light_level) 98 | 99 | func _get_configuration_warnings(): 100 | if layer == 0: 101 | return ["LightProbe won't work without a layer configured"] 102 | return [] 103 | -------------------------------------------------------------------------------- /addons/light_sensor_3d/README.md: -------------------------------------------------------------------------------- 1 | # Godot Light Sensor 3D 2 | 3 | Do you need to find the total amount of light reaching a point in your Godot 4 3D project? Then you've come to the right place. 4 | 5 | This asset contains a "light sensor" -- a tiny camera pointing at a tiny white plane that can be queried to determine how much light is reaching that plane. You can check the color, too, if you care about that. 6 | 7 | ## How to Use 8 | 9 | 1. Create a new node of type LightSensor3D. 10 | 2. Move it to the point you want to measure light. 11 | 3. Configure the layer. 12 | 4. Call the refresh() method on it periodically, e.g. every second. 13 | 5. Query it using the available properties, functions, and signals. 14 | 15 | ### Positioning and Orientation 16 | 17 | The sensor is not a single point in space, capable of reading detecting light in a sphere. It's a plane, which means a couple things: 18 | 1. You'll _probably_ want to place the sensor low to the ground of your game. 19 | 2. You'll _probably_ want to orient the sensor so the sensor is pointing downwards (this is the default). 20 | 21 | These suggestions are assuming you're measuring the amount of light some point on the ground is receiving. 22 | 23 | ### Configuring the Layer 24 | 25 | The internal sensor mesh uses the layer you configure on the light sensor itself. You'll want to choose layer(s) based on the following principles: 26 | 1. The layer shouldn't be visible to your main camera, otherwise you'll see a small white plane near the center of the sensor. 27 | 2. The layer _should_ be visible to the lights you want to register on the light sensor, otherwise you won't get the desired readings. 28 | 29 | ### Calling the `refresh()` Method 30 | 31 | Refreshing the sensor's state is pretty expensive, since it requires downloading data from the GPU back to the CPU (see [Texture2D#get_image](https://docs.godotengine.org/en/stable/classes/class_texture2d.html#class-texture2d-method-get-image)). In my tests, it takes on the order of 0.2ms, which is potentially a big chunk of the frame budget if you have multiple sensors updating every frame. Therefore, it's recommended to only update as often as you need -- once every 250ms or even less often. 32 | 33 | Calling `refresh()` is left up to you. The easiest way to do this is to add a child node of the LightSensor3D of type [Timer](https://docs.godotengine.org/en/stable/classes/class_timer.html) that: 34 | * has a wait time of 1 second, or whatever your update frequency need is. 35 | * does **not** have one-shot enabled. 36 | * does have autostart enabled. 37 | * and lastly and most importantly, triggers the parent node's `refresh()` method in the `timeout` signal. 38 | 39 | There's an included example scene that does this that you can also check out. 40 | 41 | ### Outputs 42 | 43 | | | name | type | description | | | 44 | |----------|---------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|---|---| 45 | | property | `color` | [Color](https://docs.godotengine.org/en/stable/classes/class_color.html) | This is the current color the sensor is seeing. | | | 46 | | property | `light_level` | float | This is the [luminance](https://en.wikipedia.org/wiki/Luminance) seen by the sensor. 0=dark, 1=bright. | | | 47 | | signal | color_updated | (color: Color) | Emitted when the current color changes. | | | 48 | | signal | light_level_updated | (luminance: float) | Emitted when the light level changes. | 49 | Note that all of these require you to call `refresh()` before they'll be updated and triggered. -------------------------------------------------------------------------------- /addons/light_sensor_3d/example/root.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://bu3k5wgm6tyuq"] 2 | 3 | [ext_resource type="Script" path="res://addons/light_sensor_3d/example/sensor_label.gd" id="2_ljxqy"] 4 | [ext_resource type="Script" path="res://addons/light_sensor_3d/light_sensor_3d.gd" id="3_3whfq"] 5 | 6 | [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_5l7yt"] 7 | sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) 8 | ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) 9 | 10 | [sub_resource type="Sky" id="Sky_asboq"] 11 | sky_material = SubResource("ProceduralSkyMaterial_5l7yt") 12 | 13 | [sub_resource type="Environment" id="Environment_48oer"] 14 | background_mode = 2 15 | sky = SubResource("Sky_asboq") 16 | ambient_light_source = 1 17 | ssao_enabled = true 18 | ssil_enabled = true 19 | 20 | [sub_resource type="PlaneMesh" id="PlaneMesh_u00y1"] 21 | size = Vector2(10, 5) 22 | 23 | [node name="Root" type="Node3D"] 24 | 25 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."] 26 | environment = SubResource("Environment_48oer") 27 | 28 | [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] 29 | transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) 30 | visible = false 31 | shadow_enabled = true 32 | 33 | [node name="Ground" type="MeshInstance3D" parent="."] 34 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1) 35 | mesh = SubResource("PlaneMesh_u00y1") 36 | metadata/_edit_lock_ = true 37 | 38 | [node name="Camera3D" type="Camera3D" parent="."] 39 | transform = Transform3D(1, 0, 0, 0, 0.776882, 0.629646, 0, -0.629646, 0.776882, 0, 1.74211, 2.50796) 40 | cull_mask = 1048573 41 | metadata/_edit_lock_ = true 42 | 43 | [node name="NoLight" type="Node3D" parent="."] 44 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0.5, 1) 45 | 46 | [node name="LightProbe3D" type="Node3D" parent="NoLight"] 47 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 48 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 49 | script = ExtResource("3_3whfq") 50 | layer = 2 51 | 52 | [node name="Timer" type="Timer" parent="NoLight/LightProbe3D"] 53 | autostart = true 54 | 55 | [node name="SpotLight3D" type="SpotLight3D" parent="NoLight"] 56 | transform = Transform3D(1, 0, 0, 0, -2.98023e-08, 1, 0, -1, -2.98023e-08, 0, 0.5, 0) 57 | visible = false 58 | layers = 3 59 | light_energy = 0.333 60 | shadow_enabled = true 61 | spot_angle = 10.0 62 | 63 | [node name="Label3D" type="Label3D" parent="NoLight" node_paths=PackedStringArray("light_probe")] 64 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0) 65 | billboard = 1 66 | text = "Light_0" 67 | script = ExtResource("2_ljxqy") 68 | light_probe = NodePath("../LightProbe3D") 69 | 70 | [node name="LittleLight" type="Node3D" parent="."] 71 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 0.5, 1) 72 | 73 | [node name="LightProbe3D" type="Node3D" parent="LittleLight"] 74 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 75 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 76 | script = ExtResource("3_3whfq") 77 | layer = 2 78 | print_timing_information = null 79 | 80 | [node name="Timer" type="Timer" parent="LittleLight/LightProbe3D"] 81 | autostart = true 82 | 83 | [node name="SpotLight3D" type="SpotLight3D" parent="LittleLight"] 84 | transform = Transform3D(1, 0, 0, 0, -2.98023e-08, 1, 0, -1, -2.98023e-08, 0, 0.5, 0) 85 | layers = 3 86 | light_energy = 0.25 87 | shadow_enabled = true 88 | spot_angle = 10.0 89 | 90 | [node name="Label3D" type="Label3D" parent="LittleLight" node_paths=PackedStringArray("light_probe")] 91 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0) 92 | billboard = 1 93 | text = "Light_25" 94 | script = ExtResource("2_ljxqy") 95 | light_probe = NodePath("../LightProbe3D") 96 | 97 | [node name="SomeLight" type="Node3D" parent="."] 98 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 1) 99 | 100 | [node name="LightProbe3D" type="Node3D" parent="SomeLight"] 101 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 102 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 103 | script = ExtResource("3_3whfq") 104 | layer = 2 105 | print_timing_information = null 106 | 107 | [node name="Timer" type="Timer" parent="SomeLight/LightProbe3D"] 108 | autostart = true 109 | 110 | [node name="SpotLight3D" type="SpotLight3D" parent="SomeLight"] 111 | transform = Transform3D(1, 0, 0, 0, -2.98023e-08, 1, 0, -1, -2.98023e-08, 0, 0.5, 0) 112 | layers = 3 113 | light_energy = 0.5 114 | shadow_enabled = true 115 | spot_angle = 10.0 116 | 117 | [node name="Label3D" type="Label3D" parent="SomeLight" node_paths=PackedStringArray("light_probe")] 118 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0) 119 | billboard = 1 120 | text = "Light_50*" 121 | script = ExtResource("2_ljxqy") 122 | light_probe = NodePath("../LightProbe3D") 123 | log_color_change_event = true 124 | log_light_level_change_event = true 125 | 126 | [node name="BrightLight" type="Node3D" parent="."] 127 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0.5, 1) 128 | 129 | [node name="LightProbe3D" type="Node3D" parent="BrightLight"] 130 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 131 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 132 | script = ExtResource("3_3whfq") 133 | layer = 2 134 | print_timing_information = null 135 | 136 | [node name="Timer" type="Timer" parent="BrightLight/LightProbe3D"] 137 | autostart = true 138 | 139 | [node name="SpotLight3D" type="SpotLight3D" parent="BrightLight"] 140 | transform = Transform3D(1, 0, 0, 0, -2.98023e-08, 1, 0, -1, -2.98023e-08, 0, 0.5, 0) 141 | layers = 3 142 | light_energy = 0.75 143 | shadow_enabled = true 144 | spot_angle = 10.0 145 | 146 | [node name="Label3D" type="Label3D" parent="BrightLight" node_paths=PackedStringArray("light_probe")] 147 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0) 148 | billboard = 1 149 | text = "Light_75" 150 | script = ExtResource("2_ljxqy") 151 | light_probe = NodePath("../LightProbe3D") 152 | 153 | [node name="FullLight" type="Node3D" parent="."] 154 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0.5, 1) 155 | 156 | [node name="LightProbe3D" type="Node3D" parent="FullLight"] 157 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 158 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 159 | script = ExtResource("3_3whfq") 160 | layer = 2 161 | print_timing_information = null 162 | 163 | [node name="Timer" type="Timer" parent="FullLight/LightProbe3D"] 164 | autostart = true 165 | 166 | [node name="SpotLight3D" type="SpotLight3D" parent="FullLight"] 167 | transform = Transform3D(1, 0, 0, 0, -2.98023e-08, 1, 0, -1, -2.98023e-08, 0, 0.5, 0) 168 | layers = 3 169 | shadow_enabled = true 170 | spot_angle = 10.0 171 | 172 | [node name="Label3D" type="Label3D" parent="FullLight" node_paths=PackedStringArray("light_probe")] 173 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0) 174 | billboard = 1 175 | text = "Light_100" 176 | script = ExtResource("2_ljxqy") 177 | light_probe = NodePath("../LightProbe3D") 178 | 179 | [node name="TestLight1" type="Node3D" parent="."] 180 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.757093, 0.5, 1.77956) 181 | 182 | [node name="LightProbe3D" type="Node3D" parent="TestLight1"] 183 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 184 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 185 | script = ExtResource("3_3whfq") 186 | layer = 2 187 | enable_subviewport_debug = true 188 | print_timing_information = null 189 | 190 | [node name="Timer" type="Timer" parent="TestLight1/LightProbe3D"] 191 | autostart = true 192 | 193 | [node name="SpotLight3D" type="SpotLight3D" parent="TestLight1"] 194 | transform = Transform3D(1, -8.35189e-23, -4.37114e-08, 4.37114e-08, 1.91069e-15, 1, 0, -1, 1.91069e-15, -0.006, 0.302, 0) 195 | layers = 3 196 | light_energy = 0.5 197 | shadow_enabled = true 198 | spot_angle = 10.0 199 | 200 | [node name="TestLight2" type="Node3D" parent="."] 201 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.208557, 0.5, 1.77956) 202 | 203 | [node name="LightProbe3D" type="Node3D" parent="TestLight2"] 204 | editor_description = "This light probe can track the color and luminance at its position. It does need to be manually refreshed, typically using a Timer node." 205 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.45, 0) 206 | script = ExtResource("3_3whfq") 207 | layer = 2 208 | enable_subviewport_debug = true 209 | print_timing_information = null 210 | 211 | [node name="Timer" type="Timer" parent="TestLight2/LightProbe3D"] 212 | autostart = true 213 | 214 | [node name="SpotLight3D" type="SpotLight3D" parent="TestLight2"] 215 | transform = Transform3D(1, -8.35189e-23, -4.37114e-08, 4.37114e-08, 1.91069e-15, 1, 0, -1, 1.91069e-15, 0.104, 0.302, 0) 216 | layers = 3 217 | light_energy = 0.5 218 | shadow_enabled = true 219 | spot_angle = 10.0 220 | 221 | [connection signal="timeout" from="NoLight/LightProbe3D/Timer" to="NoLight/LightProbe3D" method="refresh"] 222 | [connection signal="timeout" from="LittleLight/LightProbe3D/Timer" to="LittleLight/LightProbe3D" method="refresh"] 223 | [connection signal="timeout" from="SomeLight/LightProbe3D/Timer" to="SomeLight/LightProbe3D" method="refresh"] 224 | [connection signal="timeout" from="BrightLight/LightProbe3D/Timer" to="BrightLight/LightProbe3D" method="refresh"] 225 | [connection signal="timeout" from="FullLight/LightProbe3D/Timer" to="FullLight/LightProbe3D" method="refresh"] 226 | [connection signal="timeout" from="TestLight1/LightProbe3D/Timer" to="TestLight1/LightProbe3D" method="refresh"] 227 | [connection signal="timeout" from="TestLight2/LightProbe3D/Timer" to="TestLight2/LightProbe3D" method="refresh"] 228 | --------------------------------------------------------------------------------