stuff
This commit is contained in:
9
addons/lightmap_probe_grid/Depth.gdshader
Normal file
9
addons/lightmap_probe_grid/Depth.gdshader
Normal file
@@ -0,0 +1,9 @@
|
||||
shader_type spatial;
|
||||
render_mode unshaded;
|
||||
|
||||
uniform sampler2D depth_texture : source_color, hint_depth_texture, filter_nearest, repeat_disable;
|
||||
|
||||
void fragment() {
|
||||
float depth = texture(depth_texture, SCREEN_UV).x;
|
||||
ALBEDO = vec3(depth);
|
||||
}
|
||||
1
addons/lightmap_probe_grid/Depth.gdshader.uid
Normal file
1
addons/lightmap_probe_grid/Depth.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://0dexnhbddxfl
|
||||
21
addons/lightmap_probe_grid/LICENSE.txt
Normal file
21
addons/lightmap_probe_grid/LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 SpockBauru
|
||||
|
||||
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.
|
||||
79
addons/lightmap_probe_grid/README.md
Normal file
79
addons/lightmap_probe_grid/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# LightmapProbeGrid
|
||||
Create a grid of Lightmap Probes and cut unwanted ones!
|
||||
|
||||
LightmapProbeGrid is an extension for [Godot Engine](https://godotengine.org/) that helps on the demanding task of placing Lightmap Probes where LightmapGI fails to do it.
|
||||
|
||||
**Disclaimer:** If you are getting the error `scene/3d/lightmap_gi.cpp:529 - Inconsistency found in triangulation...` is because [Godot Issue 82642](https://github.com/godotengine/godot/issues/82642). If you have the knowledge, would you kindly contrubute to fix the issue please? \o/
|
||||
|
||||
### Video Tutorial
|
||||
https://www.youtube.com/watch?v=HzZSQ0BPpuk
|
||||
|
||||
# Index
|
||||
* [What's new](#whats-new)
|
||||
* [How to install](#how-to-install)
|
||||
* [Making a grid of Light Probes](#making-a-grid-of-light-probes)
|
||||
* [Cut Obstructed Probes](#cut-obstructed-probes)
|
||||
* [Cut probes inside objects](#cut-probes-inside-objects)
|
||||
* [Cut probes far from objects](#cut-probes-far-from-objects)
|
||||
* [Limitations](#limitations)
|
||||
* [Compatibility](#compatibility)
|
||||
* [Ending notes](#ending-notes)
|
||||
* [Changelog](#changelog)
|
||||
|
||||
# What's New
|
||||
Thanks to [dwarfses](https://twitter.com/dwarfses/status/1731691097263362513), LightmapProbeGrid v2.0 now uses GPU raycast instead of the regular Physics raycast. It may be slower but means that colliders are not needed anymore! If the camera can see, it should work!
|
||||
|
||||
NOTE: The Cull Mask from v1.0 is not compatible with v2.0.
|
||||
|
||||
# How to install
|
||||
1) Download the file `LightmapProbeGrid_v2.0.zip` from the [Download Page](https://github.com/SpockBauru/LightmapProbeGrid/releases)
|
||||
2) Extract the `addons` folder on the root of your project (`res://`). Other files/folders are optional.
|
||||
3) Go to Godot's "Project" menu -> "Project Settings" -> "Plugins" tab -> enable "LightmapProbeGrid".
|
||||
4) Restart Godot.
|
||||
|
||||
You can also open the `DemoScene` to see how it works.
|
||||
|
||||
# Making a grid of Light Probes
|
||||
- Place the LightmapProbeGrid Node in the scene. It's located at "Add Node" -> Node3D -> LightmapProbeGrid.
|
||||
- Use the handles (red dots) to resize the grid.
|
||||
- In the LightmapProbeGrid Inspector you can set the number of Light Probes on each axis with the minimum of 2. Press "Generate Probes" to apply the settings and place your grid of Light Probes in the Scene.
|
||||
|
||||
Now you can cut unwanted probes with the methods bellow.
|
||||
|
||||
## Cut Obstructed Probes
|
||||
This method is designed to cut probes that are placed beyond visual limits such as the ground or the walls of a cave.
|
||||
|
||||
On LightmapProbeGrid Inscpector click on "Cut Obstructed Probes". It will test each Light Probe from the center of the grid to the probe and see if the line intercepts an object. The probe will be cut if there's something blocking the line.
|
||||
|
||||
## Cut probes inside objects
|
||||
This method is designed to delete probes that are inside objects. It will test all 6 axis of each Light Probe: Up, Down, Left, Right, Forward and Backward by the distance indicated in `Max Object Size`.
|
||||
|
||||
If at least 4 of these lines hit something the probe will be cut. It considers only 4 hits to cut probes on long objects like pillars and trees.
|
||||
|
||||
## Cut probes far from objects
|
||||
This method is designed to delete probes that are far away from any object. Normally these probes don't contain any relevant light information but use with care in places that have a high usage of spotlights.
|
||||
|
||||
When you click the button the area around the Light Probe is tested on various directions by the distance indicated in `Max Distance`. The probe will be cut if none of the rays intercept an object.
|
||||
|
||||
## Using masks
|
||||
You can select which 3D render layers LightmapProbeGrid will interact on the section Visual Cull Mask. Only selected layers will be used on detection for the Cut methods above.
|
||||
|
||||
Use masks to filter out objects to not interact with the rays, like characters or moving objects.
|
||||
|
||||
# Limitations
|
||||
LightmapProbeGrid is not designed to work with a huge number of Light Probes at once covering a vast area. It is designed to be placed multiple times in a scene with relatively small grids (less than 1,000 probes).
|
||||
|
||||
# Compatibility
|
||||
LightmapProbeGrid is compatible with Godot 4.2 and there are plans to continue supporting onward.
|
||||
|
||||
# Ending notes
|
||||
This tool was entirely made on my free time. If you want to support me, please make an awesome asset and publish for free to the community!
|
||||
|
||||
# Changelog
|
||||
v2.0:
|
||||
- Major changes: now uses GPU Raycast instead of Physics raycast
|
||||
- This means that colliders are not needed anymore!
|
||||
- The Cull Mask from v1.0 is not compatible with v2.0
|
||||
|
||||
v1.0:
|
||||
- First release.
|
||||
89
addons/lightmap_probe_grid/UI.gd
Normal file
89
addons/lightmap_probe_grid/UI.gd
Normal file
@@ -0,0 +1,89 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
@export var probes_x: SpinBox
|
||||
@export var probes_y: SpinBox
|
||||
@export var probes_z: SpinBox
|
||||
@export var planned_probes: RichTextLabel
|
||||
|
||||
@export var generate_button: Button
|
||||
|
||||
@export var far_distance: SpinBox
|
||||
@export var object_size: SpinBox
|
||||
|
||||
var root_node: Node
|
||||
var probe_grid: LightmapProbeGrid
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
root_node = EditorInterface.get_edited_scene_root()
|
||||
if EditorInterface.get_selection().get_selected_nodes().size() == 1:
|
||||
probe_grid = EditorInterface.get_selection().get_selected_nodes()[0] as LightmapProbeGrid
|
||||
else:
|
||||
return
|
||||
|
||||
# connecting signals
|
||||
if not probe_grid.probes_changed.is_connected(_get_probes):
|
||||
probe_grid.probes_changed.connect(_get_probes)
|
||||
if not probe_grid.probes_changed.is_connected(planned_probes_text):
|
||||
probe_grid.probes_changed.connect(planned_probes_text)
|
||||
|
||||
# initializing values
|
||||
far_distance.value = probe_grid.far_distance
|
||||
object_size.value = probe_grid.object_size
|
||||
_get_probes()
|
||||
planned_probes_text()
|
||||
planned_probes.tooltip_text = "Maximum is %s" % probe_grid.max_probes
|
||||
|
||||
|
||||
func planned_probes_text() -> void:
|
||||
var total: int = probe_grid.planned_probes
|
||||
var current: int = probe_grid.current_probes
|
||||
var max_probes: int = probe_grid.max_probes
|
||||
|
||||
if total <= max_probes:
|
||||
planned_probes.text = "Probes Planned/Current: " + str(total) + " / " + str(current)
|
||||
generate_button.disabled = false
|
||||
else:
|
||||
planned_probes.text = "[color=red]Planned Probes: %s [/color] \
|
||||
\nWarning: Max number of probes is %s" % [total, max_probes]
|
||||
generate_button.disabled = true
|
||||
|
||||
|
||||
func _set_probes_number(_value: float) -> void:
|
||||
var number_of_probes: Vector3i = Vector3i.ONE
|
||||
number_of_probes.x = int(probes_x.value)
|
||||
number_of_probes.y = int(probes_y.value)
|
||||
number_of_probes.z = int(probes_z.value)
|
||||
probe_grid.probes_number = number_of_probes
|
||||
|
||||
|
||||
func _on_generate_probes_pressed() -> void:
|
||||
probe_grid.generate_probes()
|
||||
|
||||
|
||||
func _get_probes() -> void:
|
||||
var number: Vector3i = probe_grid.probes_number
|
||||
probes_x.value = number.x
|
||||
probes_y.value = number.y
|
||||
probes_z.value = number.z
|
||||
|
||||
|
||||
func _on_cut_by_mask_pressed() -> void:
|
||||
probe_grid.cut_obstructed()
|
||||
|
||||
|
||||
func _cut_far_probes():
|
||||
probe_grid.cut_far()
|
||||
|
||||
|
||||
func _set_far_distance(value):
|
||||
probe_grid.far_distance = value
|
||||
|
||||
|
||||
func _cut_inside():
|
||||
probe_grid.cut_inside()
|
||||
|
||||
|
||||
func _set_object_size(value):
|
||||
probe_grid.object_size = value
|
||||
1
addons/lightmap_probe_grid/UI.gd.uid
Normal file
1
addons/lightmap_probe_grid/UI.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://l34dvbnsrsna
|
||||
198
addons/lightmap_probe_grid/controls_scene.tscn
Normal file
198
addons/lightmap_probe_grid/controls_scene.tscn
Normal file
@@ -0,0 +1,198 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://xp1820rv0uy"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/lightmap_probe_grid/UI.gd" id="1_4a60v"]
|
||||
[ext_resource type="StyleBox" uid="uid://4nfudftbnexo" path="res://addons/lightmap_probe_grid/style_box_flat.tres" id="2_rdj4j"]
|
||||
|
||||
[node name="RootContainer" type="VBoxContainer" node_paths=PackedStringArray("probes_x", "probes_y", "probes_z", "planned_probes", "generate_button", "far_distance", "object_size")]
|
||||
offset_right = 257.0
|
||||
offset_bottom = 346.0
|
||||
script = ExtResource("1_4a60v")
|
||||
probes_x = NodePath("ProbesContainer/ProbesX")
|
||||
probes_y = NodePath("ProbesContainer/ProbesY")
|
||||
probes_z = NodePath("ProbesContainer/ProbesZ")
|
||||
planned_probes = NodePath("PlannedLabel")
|
||||
generate_button = NodePath("GenerateProbes")
|
||||
far_distance = NodePath("CutFarProbesContainer/Distance")
|
||||
object_size = NodePath("CutInsideContainer/Size")
|
||||
|
||||
[node name="ProbesLabel" type="Label" parent="."]
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Number of probes on each axis"
|
||||
mouse_filter = 0
|
||||
text = "Number of Probes"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="ProbesContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ProbesX" type="SpinBox" parent="ProbesContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
tooltip_text = "Minimum is 2"
|
||||
min_value = 2.0
|
||||
max_value = 250.0
|
||||
value = 2.0
|
||||
alignment = 2
|
||||
prefix = "x:"
|
||||
select_all_on_focus = true
|
||||
|
||||
[node name="ProbesY" type="SpinBox" parent="ProbesContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
tooltip_text = "Minimum is 2"
|
||||
min_value = 2.0
|
||||
max_value = 250.0
|
||||
value = 2.0
|
||||
alignment = 2
|
||||
prefix = "y:"
|
||||
select_all_on_focus = true
|
||||
|
||||
[node name="ProbesZ" type="SpinBox" parent="ProbesContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
tooltip_text = "Minimum is 2"
|
||||
min_value = 2.0
|
||||
max_value = 250.0
|
||||
value = 2.0
|
||||
alignment = 2
|
||||
prefix = "z:"
|
||||
select_all_on_focus = true
|
||||
|
||||
[node name="PlannedLabel" type="RichTextLabel" parent="."]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Maximum is xxxx"
|
||||
bbcode_enabled = true
|
||||
text = "Planned Probes: xxxx"
|
||||
fit_content = true
|
||||
|
||||
[node name="GenerateProbes" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_styles/normal = ExtResource("2_rdj4j")
|
||||
text = "Generate Probes"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ObscuredLabel" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Test from center to each probe,
|
||||
and cut the probe if the path is obstructed by an object.
|
||||
|
||||
Use Collision Mask to filter objects."
|
||||
mouse_filter = 0
|
||||
text = "Probes obscured from center"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Cut by Mask" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Test from center to each probe,
|
||||
and cut the probe if the path is obstructed by an object.
|
||||
|
||||
Use Collision Mask to filter objects."
|
||||
theme_override_styles/normal = ExtResource("2_rdj4j")
|
||||
text = "Cut Obstructed Probes"
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
tooltip_text = "For each probe, test if there is any object within an Max distance.
|
||||
If there isn't any object the probe is cut.
|
||||
|
||||
Use Collision Mask to filter objects."
|
||||
mouse_filter = 0
|
||||
text = "Probes far from any object"
|
||||
|
||||
[node name="CutFarProbesContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="DistanceLabel" type="Label" parent="CutFarProbesContainer"]
|
||||
custom_minimum_size = Vector2(67, 0)
|
||||
layout_mode = 2
|
||||
tooltip_text = "The distance from the probe that objects will be detected"
|
||||
mouse_filter = 0
|
||||
text = "Max distance"
|
||||
|
||||
[node name="Distance" type="SpinBox" parent="CutFarProbesContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "The distance from the probe that objects will be detected"
|
||||
step = 0.0
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
suffix = "m"
|
||||
custom_arrow_step = 1.0
|
||||
select_all_on_focus = true
|
||||
|
||||
[node name="CutFarProbes" type="Button" parent="."]
|
||||
custom_minimum_size = Vector2(148, 0)
|
||||
layout_mode = 2
|
||||
tooltip_text = "For each probe, test if there is any object within an Max distance.
|
||||
If there isn't any object the probe is cut.
|
||||
|
||||
Use Collision Mask to filter objects."
|
||||
theme_override_styles/normal = ExtResource("2_rdj4j")
|
||||
text = "Cut Far Probes"
|
||||
|
||||
[node name="HSeparator3" type="HSeparator" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
tooltip_text = "For each probe, test if the same object is intercepted by the yellow lines.
|
||||
|
||||
If the same object is detected 4 times or more,
|
||||
the probe is considered to be inside and will be cut.
|
||||
|
||||
Only 4 lines are tested instead of 6 because is common for
|
||||
objects to not have 2 faces, like pillars.
|
||||
|
||||
Use Collision Mask to filter objects."
|
||||
mouse_filter = 0
|
||||
text = "Probes inside objects"
|
||||
|
||||
[node name="CutInsideContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="SizeLabel" type="Label" parent="CutInsideContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Distance tested from the probes (size of yellow lines)"
|
||||
mouse_filter = 0
|
||||
text = "Max object size"
|
||||
|
||||
[node name="Size" type="SpinBox" parent="CutInsideContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Distance tested from the probes (size of yellow lines)"
|
||||
step = 0.0
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
suffix = "m"
|
||||
custom_arrow_step = 1.0
|
||||
|
||||
[node name="CutInsideObjects" type="Button" parent="."]
|
||||
custom_minimum_size = Vector2(148, 0)
|
||||
layout_mode = 2
|
||||
tooltip_text = "For each probe, test if the same object is intercepted by the yellow lines.
|
||||
|
||||
If the same object is detected 4 times or more,
|
||||
the probe is considered to be inside and will be cut.
|
||||
|
||||
Only 4 lines are tested instead of 6 because is common for
|
||||
objects to not have 2 faces, like pillars.
|
||||
|
||||
Use Collision Mask to filter objects."
|
||||
theme_override_styles/normal = ExtResource("2_rdj4j")
|
||||
text = "Cut Inside Objects"
|
||||
|
||||
[connection signal="value_changed" from="ProbesContainer/ProbesX" to="." method="_set_probes_number"]
|
||||
[connection signal="value_changed" from="ProbesContainer/ProbesY" to="." method="_set_probes_number"]
|
||||
[connection signal="value_changed" from="ProbesContainer/ProbesZ" to="." method="_set_probes_number"]
|
||||
[connection signal="pressed" from="GenerateProbes" to="." method="_on_generate_probes_pressed"]
|
||||
[connection signal="pressed" from="Cut by Mask" to="." method="_on_cut_by_mask_pressed"]
|
||||
[connection signal="value_changed" from="CutFarProbesContainer/Distance" to="." method="_set_far_distance"]
|
||||
[connection signal="pressed" from="CutFarProbes" to="." method="_cut_far_probes"]
|
||||
[connection signal="value_changed" from="CutInsideContainer/Size" to="." method="_set_object_size"]
|
||||
[connection signal="pressed" from="CutInsideObjects" to="." method="_cut_inside"]
|
||||
16
addons/lightmap_probe_grid/editor_inspector_plugin.gd
Normal file
16
addons/lightmap_probe_grid/editor_inspector_plugin.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
var ui_control: PackedScene = preload("controls_scene.tscn")
|
||||
var ui: Control = null
|
||||
|
||||
func _can_handle(object: Object) -> bool:
|
||||
if object is LightmapProbeGrid:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
func _parse_category(_object: Object, category: String) -> void:
|
||||
if category.begins_with("lightmap_probe_grid"):
|
||||
if ui == null:
|
||||
ui = ui_control.instantiate()
|
||||
add_property_editor("LighmapProbeGrid", ui)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cyh1thwnyn2ra
|
||||
159
addons/lightmap_probe_grid/gizmo.gd
Normal file
159
addons/lightmap_probe_grid/gizmo.gd
Normal file
@@ -0,0 +1,159 @@
|
||||
# my_custom_gizmo_plugin.gd
|
||||
extends EditorNode3DGizmoPlugin
|
||||
|
||||
const handles_axis: PackedVector3Array = [
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(0, 1, 0),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(-1, 0, 0),
|
||||
Vector3(0, -1, 0),
|
||||
Vector3(0, 0, -1)
|
||||
]
|
||||
|
||||
const box_lines: PackedVector3Array = [
|
||||
# plane -x
|
||||
Vector3(-1, -1, -1), Vector3(-1, -1, 1),
|
||||
Vector3(-1, -1, 1), Vector3(-1, 1, 1),
|
||||
Vector3(-1, 1, 1), Vector3(-1, 1, -1),
|
||||
Vector3(-1, 1, -1), Vector3(-1, -1, -1),
|
||||
# plane +x
|
||||
Vector3(1, -1, -1), Vector3(1, -1, 1),
|
||||
Vector3(1, -1, 1), Vector3(1, 1, 1),
|
||||
Vector3(1, 1, 1), Vector3(1, 1, -1),
|
||||
Vector3(1, 1, -1), Vector3(1, -1, -1),
|
||||
# connecting plane x with -x
|
||||
Vector3(1, -1, -1), Vector3(-1, -1, -1),
|
||||
Vector3(1, -1, 1), Vector3(-1, -1, 1),
|
||||
Vector3(1, 1, -1), Vector3(-1, 1, -1),
|
||||
Vector3(1, 1, 1), Vector3(-1, 1, 1),
|
||||
]
|
||||
|
||||
var icon: Texture2D = preload("lightmap_probe_grid_icon.svg")
|
||||
|
||||
var timer: Timer = Timer.new()
|
||||
var is_awayting: bool = false
|
||||
|
||||
|
||||
func _get_gizmo_name() -> String:
|
||||
return "LightmapProbeGrid"
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
create_material("main_material", Color(0,0,0))
|
||||
create_material("tool_material", Color(1, 0.9, 0))
|
||||
create_handle_material("handles_material")
|
||||
create_icon_material("icon_material", icon)
|
||||
|
||||
|
||||
func _has_gizmo(node: Node3D) -> bool:
|
||||
if node is LightmapProbeGrid:
|
||||
if not node.size_changed.is_connected(node.update_gizmos):
|
||||
node.size_changed.connect(node.update_gizmos)
|
||||
if not node.probes_changed.is_connected(node.update_gizmos):
|
||||
node.probes_changed.connect(node.update_gizmos)
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
|
||||
func _redraw(gizmo: EditorNode3DGizmo) -> void:
|
||||
gizmo.clear()
|
||||
var box: LightmapProbeGrid = gizmo.get_node_3d() as LightmapProbeGrid
|
||||
var size: Vector3 = box.size
|
||||
|
||||
var icon_gizmo: Material = get_material("icon_material")
|
||||
gizmo.add_unscaled_billboard(icon_gizmo, 0.05)
|
||||
|
||||
# Setting box lines
|
||||
var lines: PackedVector3Array = []
|
||||
for pos: Vector3 in box_lines:
|
||||
var scaled: Vector3 = 0.5 * pos * size
|
||||
lines.append(scaled)
|
||||
gizmo.add_lines(lines, get_material("main_material", gizmo))
|
||||
|
||||
# Setting handles
|
||||
var handles: PackedVector3Array = []
|
||||
for pos: Vector3 in handles_axis:
|
||||
var scaled: Vector3 = 0.5 * pos * size
|
||||
handles.append(scaled)
|
||||
gizmo.add_handles(handles, get_material("handles_material", gizmo), [])
|
||||
|
||||
# Setting extra tool lines from main script
|
||||
var tool_lines: PackedVector3Array = box.gizmo_lines
|
||||
if not tool_lines.is_empty():
|
||||
gizmo.add_lines(tool_lines, get_material("tool_material", gizmo))
|
||||
clear_tool_await(box)
|
||||
|
||||
|
||||
# Wait 3 seconds before clear the main script gizmos. If called twice the timer is reset
|
||||
func clear_tool_await(box: LightmapProbeGrid):
|
||||
if timer == null:
|
||||
timer = Timer.new()
|
||||
# Add timer to the scene
|
||||
if timer.get_parent() == null:
|
||||
var root_node = EditorInterface.get_edited_scene_root()
|
||||
root_node.add_child(timer)
|
||||
timer.name = "lightmap_probe_grid_timer"
|
||||
|
||||
timer.wait_time = 3.0
|
||||
timer.start()
|
||||
|
||||
if is_awayting:
|
||||
return
|
||||
|
||||
is_awayting = true
|
||||
await timer.timeout
|
||||
is_awayting = false
|
||||
|
||||
timer.stop
|
||||
box.gizmo_lines.clear()
|
||||
box.update_gizmos()
|
||||
|
||||
|
||||
# Based on github.com/godotengine/godot/blob/master/editor/plugins/gizmos/gizmo_3d_helper.cpp
|
||||
# please, make it available to GDScript plugin developers...
|
||||
func _set_handle(gizmo: EditorNode3DGizmo, index: int, _sec: bool, camera: Camera3D, point: Vector2) -> void:
|
||||
var box: LightmapProbeGrid = gizmo.get_node_3d() as LightmapProbeGrid
|
||||
var axis: Vector3 = handles_axis[index]
|
||||
var axis_index: int = axis.abs().max_axis_index()
|
||||
|
||||
var inverse: Transform3D = box.global_transform.affine_inverse()
|
||||
var ray_from: Vector3 = camera.project_ray_origin(point)
|
||||
var ray_to: Vector3 = camera.project_ray_normal(point)
|
||||
var camera_position: Vector3 = inverse * ray_from
|
||||
var camera_to_mouse: Vector3 = inverse * (ray_from + ray_to * 5000)
|
||||
|
||||
var segment1: Vector3 = axis * 5000
|
||||
var segment2: Vector3 = axis * -5000
|
||||
|
||||
var intersection: PackedVector3Array = Geometry3D.get_closest_points_between_segments(segment2,
|
||||
segment1, camera_position, camera_to_mouse)
|
||||
|
||||
# Distance between the center and the handle (without scale)
|
||||
var distance: float = intersection[0][axis_index]
|
||||
# multiply axis signal to cancel distance signal
|
||||
distance *= axis[axis_index]
|
||||
|
||||
var old_distance: float = 0.5 * box.size[axis_index]
|
||||
|
||||
# Defining new size and positions
|
||||
var new_size: float = distance + old_distance
|
||||
|
||||
# Translate halfway through the size difference
|
||||
var translate: Vector3 = 0.5 * (distance - old_distance) * axis
|
||||
|
||||
# Updating size and position
|
||||
box.size[axis_index] = new_size
|
||||
box.translate(translate)
|
||||
|
||||
# Update Gizmo
|
||||
box.update_gizmos()
|
||||
|
||||
|
||||
func _get_handle_name(_gizmo: EditorNode3DGizmo, _handle_id: int, _sec: bool) -> String:
|
||||
return "Probe Grid Size"
|
||||
|
||||
|
||||
func _get_handle_value(gizmo: EditorNode3DGizmo, _id: int, _sec: bool) -> Vector3:
|
||||
var box: LightmapProbeGrid = gizmo.get_node_3d() as LightmapProbeGrid
|
||||
return box.size
|
||||
1
addons/lightmap_probe_grid/gizmo.gd.uid
Normal file
1
addons/lightmap_probe_grid/gizmo.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cww446exifamp
|
||||
453
addons/lightmap_probe_grid/lightmap_probe_grid.gd
Normal file
453
addons/lightmap_probe_grid/lightmap_probe_grid.gd
Normal file
@@ -0,0 +1,453 @@
|
||||
@tool
|
||||
extends Node3D
|
||||
class_name LightmapProbeGrid
|
||||
|
||||
signal size_changed
|
||||
signal probes_changed
|
||||
|
||||
const max_probes: int = 1000
|
||||
|
||||
## Only selected layers will be seen by LightmapProbeGrid. Works like Camera3D Cull Mask.[br][br]
|
||||
## NOTE: NOT compatible with LightmapProbeGrid v1.0
|
||||
@export_flags_3d_render var visual_cull_mask: int = 1048575
|
||||
|
||||
@export var size: Vector3 = Vector3.ONE:
|
||||
set(value):
|
||||
# size cannot be zero or negative
|
||||
size = value.clamp(Vector3(1E-6, 1E-6, 1E-6), Vector3.INF)
|
||||
size_changed.emit()
|
||||
get:
|
||||
return size
|
||||
|
||||
@onready var depth_shader: Shader = preload("Depth.gdshader")
|
||||
|
||||
var probes_number: Vector3i = Vector3i(2, 2, 2):
|
||||
set(value):
|
||||
probes_number = value
|
||||
set_probes_number()
|
||||
get:
|
||||
return probes_number
|
||||
|
||||
var planned_probes: int = 8
|
||||
var current_probes: int = 8
|
||||
|
||||
var old_size: Vector3 = Vector3.ONE
|
||||
var old_scale: Vector3 = Vector3.ONE
|
||||
var warned: bool = false
|
||||
|
||||
var far_distance: float = 1
|
||||
var object_size: float = 1
|
||||
|
||||
var gizmo_lines: PackedVector3Array = []
|
||||
|
||||
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var properties: Array[Dictionary] = []
|
||||
|
||||
properties.append({
|
||||
"name": "probes_number",
|
||||
"type": TYPE_VECTOR3I,
|
||||
"usage": PROPERTY_USAGE_STORAGE
|
||||
})
|
||||
|
||||
properties.append({
|
||||
"name": "far_distance",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_STORAGE
|
||||
})
|
||||
|
||||
properties.append({
|
||||
"name": "object_size",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_STORAGE
|
||||
})
|
||||
|
||||
return properties
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
if get_child_count() < 1:
|
||||
generate_probes()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
size_changed.connect(scale_probes)
|
||||
set_notify_local_transform(true)
|
||||
old_size = size
|
||||
old_scale = scale
|
||||
current_probes = get_child_count()
|
||||
|
||||
|
||||
# Keep local scale fixed. Reflect in "size" if the user try to scale
|
||||
func _notification(what: int) -> void:
|
||||
if (what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) and not scale.is_equal_approx(Vector3.ONE):
|
||||
if not warned:
|
||||
printerr("LightmapProbeGrid: Resetting Scale, please use the handles (red dots) or ",
|
||||
"the property \"Size\" in LightmapProbeGridsection")
|
||||
warned = true
|
||||
|
||||
if(scale.x <= 0):
|
||||
scale = Vector3.ONE
|
||||
return
|
||||
|
||||
# TODO take a look on this workaround
|
||||
var scale_diff: Vector3 = abs(scale - Vector3.ONE)
|
||||
var size_sign: Vector3 = sign(scale - old_scale)
|
||||
size += size_sign * scale_diff / 10.0
|
||||
|
||||
old_scale = scale
|
||||
scale = Vector3.ONE
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = []
|
||||
|
||||
if planned_probes > max_probes:
|
||||
var text: String = "LightmapProbeGrid: The maximum number of Probes must be " + \
|
||||
str(max_probes) + ". Please consider add more instances of LightmapProbeGrid"
|
||||
warnings.append(text)
|
||||
printerr(text)
|
||||
|
||||
# Returning an empty array means "no warning".
|
||||
return warnings
|
||||
|
||||
|
||||
func set_probes_number() -> void:
|
||||
planned_probes = probes_number.x * probes_number.y * probes_number.z
|
||||
update_configuration_warnings()
|
||||
probes_changed.emit()
|
||||
|
||||
|
||||
func scale_probes() -> void:
|
||||
var new_size: Vector3 = size / old_size
|
||||
|
||||
# Scaling all probes
|
||||
for probe: Node3D in get_children():
|
||||
probe.position *= new_size
|
||||
|
||||
old_size = size
|
||||
|
||||
|
||||
func generate_probes() -> void:
|
||||
# check number of probes
|
||||
if planned_probes > max_probes:
|
||||
return
|
||||
|
||||
# Clear all previews probes
|
||||
for i: int in get_child_count():
|
||||
get_child(i).queue_free()
|
||||
# Wait for the last one to be cleaned
|
||||
if i == get_child_count() -1:
|
||||
await get_child(i).tree_exited
|
||||
|
||||
# Defining probe arrays
|
||||
var probes_positions: Array[Vector3] = []
|
||||
var probes_names: Array[String] = []
|
||||
|
||||
var probes_x: float = probes_number.x
|
||||
var probes_y: float = probes_number.y
|
||||
var probes_z: float = probes_number.z
|
||||
|
||||
# Distance between probes
|
||||
var step_x: float = size.x / (probes_x - 1)
|
||||
var step_y: float = size.y / (probes_y - 1)
|
||||
var step_z: float = size.z / (probes_z - 1)
|
||||
|
||||
# Starting relative positions
|
||||
var start_position: Vector3 = Vector3.ONE * size / 2.0
|
||||
var current_position: Vector3 = Vector3.ZERO
|
||||
|
||||
# Defining Probes relative positions and names
|
||||
for x: float in probes_x:
|
||||
for y: float in probes_y:
|
||||
for z: float in probes_z:
|
||||
current_position.x = start_position.x - step_x * x
|
||||
current_position.y = start_position.y - step_y * y
|
||||
current_position.z = start_position.z - step_z * z
|
||||
probes_positions.append(current_position)
|
||||
probes_names.append("LightmapProbe %.f, %.f, %.f" % [x, y, z])
|
||||
|
||||
# Generating probes
|
||||
var root_node: Node = get_tree().edited_scene_root
|
||||
|
||||
for i: int in range(probes_positions.size()):
|
||||
var probe: LightmapProbe = LightmapProbe.new()
|
||||
probe.position = probes_positions[i]
|
||||
probe.name = probes_names[i]
|
||||
add_child(probe)
|
||||
probe.set_owner(root_node)
|
||||
|
||||
current_probes = probes_number.x * probes_number.y * probes_number.z
|
||||
set_probes_number()
|
||||
|
||||
|
||||
# Workaround to raycast without colliders. Consists in a camera with a filter in front that shows
|
||||
# the depth texture. The camera.far is the "ray" lenght and camera rotation is the "ray" orientation
|
||||
# https://docs.godotengine.org/en/stable/tutorials/shaders/advanced_postprocessing.html#depth-texture
|
||||
func add_GPU_raycaster(probe: Node3D) -> void:
|
||||
var root_node: Node = get_tree().edited_scene_root
|
||||
|
||||
# SubViewport that will host the camera
|
||||
var sub_viewport: SubViewport = SubViewport.new()
|
||||
sub_viewport.name = "GPUraycast"
|
||||
sub_viewport.size = Vector2(2, 2)
|
||||
sub_viewport.render_target_update_mode = SubViewport.UPDATE_DISABLED
|
||||
sub_viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_NEVER
|
||||
sub_viewport.handle_input_locally = false
|
||||
sub_viewport.debug_draw = Viewport.DEBUG_DRAW_UNSHADED
|
||||
|
||||
sub_viewport.positional_shadow_atlas_size = 0
|
||||
sub_viewport.positional_shadow_atlas_quad_0 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED
|
||||
sub_viewport.positional_shadow_atlas_quad_1 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED
|
||||
sub_viewport.positional_shadow_atlas_quad_2 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED
|
||||
sub_viewport.positional_shadow_atlas_quad_3 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED
|
||||
|
||||
probe.add_child(sub_viewport)
|
||||
sub_viewport.set_owner(root_node)
|
||||
|
||||
# Camera for the viewport
|
||||
var camera_3d: Camera3D = Camera3D.new()
|
||||
camera_3d.projection = Camera3D.PROJECTION_ORTHOGONAL
|
||||
camera_3d.size = 0.001
|
||||
camera_3d.near = 0.001
|
||||
camera_3d.far = 1.0
|
||||
sub_viewport.add_child(camera_3d)
|
||||
camera_3d.set_owner(root_node)
|
||||
camera_3d.position = probe.global_position
|
||||
camera_3d.rotation = Vector3.ZERO
|
||||
camera_3d.cull_mask = visual_cull_mask
|
||||
|
||||
# Depth filter: A quad with a material that shows the Depth texture. This goes in front of the
|
||||
# camera
|
||||
var depth_material: ShaderMaterial = ShaderMaterial.new()
|
||||
depth_material.shader = depth_shader
|
||||
|
||||
var depth_filter: MeshInstance3D = MeshInstance3D.new()
|
||||
var depth_mesh: QuadMesh = QuadMesh.new()
|
||||
depth_filter.mesh = depth_mesh
|
||||
depth_mesh.material = depth_material
|
||||
depth_mesh.size = Vector2.ONE * 0.001
|
||||
camera_3d.add_child(depth_filter)
|
||||
depth_filter.set_owner(root_node)
|
||||
depth_filter.position = Vector3(0, 0, -0.002)
|
||||
depth_filter.rotation = Vector3.ZERO
|
||||
|
||||
|
||||
func generate_probes_raycasters(distance: float) -> void:
|
||||
for probe in get_children():
|
||||
add_GPU_raycaster(probe)
|
||||
|
||||
|
||||
func remove_probes_raycasters() -> void:
|
||||
for probe in get_children():
|
||||
for child in probe.get_children():
|
||||
child.queue_free();
|
||||
|
||||
|
||||
# The function look_at not always work. Exceptions are handled here
|
||||
func rotate_camera(camera: Camera3D, to: Vector3) -> void:
|
||||
var from: Vector3 = camera.position
|
||||
# look_at don't work if the node and target have the same position. You cannot look at yourself
|
||||
if from == to:
|
||||
return
|
||||
|
||||
# look_at don't work if the direction and rotation axix have same orientation. In this case,
|
||||
# change the rotation axis
|
||||
var direction: Vector3 = abs(to - from)
|
||||
var mag = (direction.normalized() - Vector3.UP).length()
|
||||
if mag > 0.001:
|
||||
camera.look_at(to)
|
||||
else:
|
||||
camera.look_at(to, Vector3.RIGHT)
|
||||
|
||||
|
||||
# Shoot rays from the center to all the probes. If any object is detected so the probe is
|
||||
# obstructed and will be cut
|
||||
func cut_obstructed() -> void:
|
||||
await generate_probes_raycasters(far_distance)
|
||||
|
||||
var probes_array: Array[LightmapProbe] = []
|
||||
var camera_array: Array[Camera3D] = []
|
||||
var subViewport_array: Array[SubViewport] = []
|
||||
var results_array: Array[float] = []
|
||||
|
||||
# Populating arrays
|
||||
for probe: LightmapProbe in get_children():
|
||||
probes_array.append(probe)
|
||||
gizmo_lines.append_array([Vector3.ZERO, probe.position])
|
||||
var sub_viewport: SubViewport = probe.get_child(0)
|
||||
subViewport_array.append(sub_viewport)
|
||||
var camera: Camera3D = sub_viewport.get_child(0)
|
||||
camera_array.append(camera)
|
||||
|
||||
# Rotating cameras and updating sub_viewports
|
||||
for i in range(camera_array.size()):
|
||||
var camera: Camera3D = camera_array[i]
|
||||
var probe_pos: Vector3 = probes_array[i].global_position
|
||||
var sub_viewport: SubViewport = subViewport_array[i]
|
||||
|
||||
camera.position = position
|
||||
# The lenght of the "Ray"
|
||||
camera.far = (probe_pos - position).length()
|
||||
# The direction of the "Ray"
|
||||
rotate_camera(camera, probe_pos)
|
||||
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
|
||||
|
||||
# Getting the values
|
||||
await RenderingServer.frame_post_draw
|
||||
for i in range(subViewport_array.size()):
|
||||
var sub_viewport = subViewport_array[i]
|
||||
var texture: Image = sub_viewport.get_texture().get_image()
|
||||
var color: Color = texture.get_pixel(0,0)
|
||||
var colorValue: float = color.r
|
||||
var result: float = colorValue
|
||||
results_array.append(result)
|
||||
|
||||
# Cutting probes
|
||||
for i in range(subViewport_array.size()):
|
||||
var result: float = results_array[i]
|
||||
if result < 1.0:
|
||||
var probe = probes_array[i]
|
||||
probe.queue_free()
|
||||
current_probes -= 1
|
||||
|
||||
set_probes_number()
|
||||
remove_probes_raycasters()
|
||||
|
||||
|
||||
# Detect if the probe is far from any object. It will shoot rays on all 6 axis and 8 quadrants.
|
||||
# If there aren't any objects the probe will be cut
|
||||
func cut_far() -> void:
|
||||
await generate_probes_raycasters(far_distance)
|
||||
|
||||
# 6 axis and 8 quadrants
|
||||
var directions: Array[Vector3] = [
|
||||
# 6 Axis
|
||||
Vector3(0, 0, 1), Vector3(0, 1, 0), Vector3(1, 0, 0),
|
||||
Vector3(0, 0, -1), Vector3(0, -1, 0), Vector3(-1, 0, 0),
|
||||
# 8 Quadrants
|
||||
Vector3(1, 1, 1).normalized(), Vector3(1, 1, -1).normalized(),
|
||||
Vector3(1, -1, 1).normalized(), Vector3(1, -1, -1).normalized(),
|
||||
Vector3(-1, 1, 1).normalized(), Vector3(-1, 1, -1).normalized(),
|
||||
Vector3(-1, -1, 1).normalized(), Vector3(-1, -1, -1).normalized()
|
||||
]
|
||||
|
||||
var probes_array: Array[LightmapProbe] = []
|
||||
var camera_array: Array[Camera3D] = []
|
||||
var subViewport_array: Array[SubViewport] = []
|
||||
var collisions_number: Array[int] = []
|
||||
|
||||
# Populating arrays
|
||||
for probe: LightmapProbe in get_children():
|
||||
probes_array.append(probe)
|
||||
var sub_viewport: SubViewport = probe.get_child(0)
|
||||
subViewport_array.append(sub_viewport)
|
||||
var camera: Camera3D = sub_viewport.get_child(0)
|
||||
camera_array.append(camera)
|
||||
collisions_number.resize(camera_array.size())
|
||||
collisions_number.fill(0)
|
||||
|
||||
# Getting data for all cameras on each direction
|
||||
for dir in directions:
|
||||
# Rotating all cameras to the same direction, and updating viewport
|
||||
for i in camera_array.size():
|
||||
var probe: LightmapProbe = probes_array[i]
|
||||
var sub_viewport: SubViewport = subViewport_array[i]
|
||||
var camera: Camera3D = camera_array[i]
|
||||
|
||||
camera.position = probe.global_position
|
||||
# The lenght of the "Ray"
|
||||
camera.far = far_distance
|
||||
# The direction of the "Ray"
|
||||
rotate_camera(camera, probe.global_position + dir)
|
||||
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
|
||||
|
||||
gizmo_lines.append_array([probe.position, probe.position + dir * far_distance])
|
||||
|
||||
# Getting all values for the current direction
|
||||
await RenderingServer.frame_post_draw
|
||||
for i in range(subViewport_array.size()):
|
||||
var sub_viewport = subViewport_array[i]
|
||||
var texture: Image = sub_viewport.get_texture().get_image()
|
||||
var color: Color = texture.get_pixel(0,0)
|
||||
var colorValue: float = color.r
|
||||
var result: float = colorValue
|
||||
if result < 1.0:
|
||||
collisions_number[i] += 1
|
||||
|
||||
# Cut probes if there are no collisions
|
||||
for i in probes_array.size():
|
||||
if collisions_number[i] < 1:
|
||||
var probe = probes_array[i]
|
||||
probe.queue_free()
|
||||
current_probes -= 1
|
||||
|
||||
set_probes_number()
|
||||
remove_probes_raycasters()
|
||||
|
||||
|
||||
# Detect if probe is inside an object. It will shoot rays from all 6 axis to the probe. If at least
|
||||
# 4 are obstructed, the probe will be cut
|
||||
func cut_inside() -> void:
|
||||
await generate_probes_raycasters(far_distance)
|
||||
|
||||
# 6 Axis
|
||||
var axis: Array[Vector3] = [
|
||||
Vector3(0, 0, 1), Vector3(0, 1, 0), Vector3(1, 0, 0),
|
||||
Vector3(0, 0, -1), Vector3(0, -1, 0), Vector3(-1, 0, 0),
|
||||
]
|
||||
|
||||
var probes_array: Array[LightmapProbe] = []
|
||||
var camera_array: Array[Camera3D] = []
|
||||
var subViewport_array: Array[SubViewport] = []
|
||||
var collisions_number: Array[int] = []
|
||||
|
||||
# Populating arrays
|
||||
for probe: LightmapProbe in get_children():
|
||||
probes_array.append(probe)
|
||||
var sub_viewport: SubViewport = probe.get_child(0)
|
||||
subViewport_array.append(sub_viewport)
|
||||
var camera: Camera3D = sub_viewport.get_child(0)
|
||||
camera_array.append(camera)
|
||||
collisions_number.resize(camera_array.size())
|
||||
collisions_number.fill(0)
|
||||
|
||||
# Getting data for all cameras on each axis
|
||||
for dir in axis:
|
||||
# For each direction, position all cameras to look from outside
|
||||
# to each probe in object_size distance
|
||||
for i in camera_array.size():
|
||||
var probe: LightmapProbe = probes_array[i]
|
||||
var sub_viewport: SubViewport = subViewport_array[i]
|
||||
var camera: Camera3D = camera_array[i]
|
||||
|
||||
camera.position = probe.global_position + dir * object_size
|
||||
# The lenght of the "Ray"
|
||||
camera.far = object_size
|
||||
# The direction of the "Ray"
|
||||
rotate_camera(camera, probe.global_position)
|
||||
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
|
||||
|
||||
gizmo_lines.append_array([probe.position, probe.position + dir * object_size])
|
||||
|
||||
# Getting all values for the current direction
|
||||
await RenderingServer.frame_post_draw
|
||||
for i in range(subViewport_array.size()):
|
||||
var sub_viewport = subViewport_array[i]
|
||||
var texture: Image = sub_viewport.get_texture().get_image()
|
||||
var color: Color = texture.get_pixel(0,0)
|
||||
var colorValue: float = color.r
|
||||
var result: float = colorValue
|
||||
if result < 1.0:
|
||||
collisions_number[i] += 1
|
||||
|
||||
# Cut probes if there are more than 4 collisions
|
||||
for i in probes_array.size():
|
||||
if collisions_number[i] > 3:
|
||||
var probe = probes_array[i]
|
||||
probe.queue_free()
|
||||
current_probes -= 1
|
||||
|
||||
set_probes_number()
|
||||
remove_probes_raycasters()
|
||||
|
||||
1
addons/lightmap_probe_grid/lightmap_probe_grid.gd.uid
Normal file
1
addons/lightmap_probe_grid/lightmap_probe_grid.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dy5lhlfkbk5t6
|
||||
4
addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg
Normal file
4
addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M7 10A1 1 0 0 0 13 10 1 1 0 0 0 7 10zM10 4A1 1 0 0 1 10 16 V14A1 1 0 0 0 10 6zM4 9h2v2H4z M5 6.6 6.4 5.2 7.8 6.6 6.4 8z M5 13.4 6.4 12 7.8 13.4 6.4 14.8z" fill="#fc7f7f"></path> <!-- Probe -->
|
||||
<path d="M3.7 1 H12.3A2 2 0 1 1 12.3 3H3.7 A2 2 0 0 1 3 3.7 V12.3A2 2 0 1 1 1 12.3 V3.7A2 2 0 1 1 3.7 1z" fill="#fc7f7f"></path> <!-- Frame -->
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 436 B |
@@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dtmcycli3j3j1"
|
||||
path="res://.godot/imported/lightmap_probe_grid_icon.svg-71f84c7339e4d62a065aeef79112e123.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg"
|
||||
dest_files=["res://.godot/imported/lightmap_probe_grid_icon.svg-71f84c7339e4d62a065aeef79112e123.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=0
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
7
addons/lightmap_probe_grid/plugin.cfg
Normal file
7
addons/lightmap_probe_grid/plugin.cfg
Normal file
@@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="LightmapProbeGrid"
|
||||
description="Create a grid of LightmapProbes, and cut unwanted ones!"
|
||||
author="SpockBauru"
|
||||
version="2.0"
|
||||
script="plugin.gd"
|
||||
25
addons/lightmap_probe_grid/plugin.gd
Normal file
25
addons/lightmap_probe_grid/plugin.gd
Normal file
@@ -0,0 +1,25 @@
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
var custom_node: Script = preload("lightmap_probe_grid.gd")
|
||||
var icon: Texture2D = preload("lightmap_probe_grid_icon.svg")
|
||||
|
||||
var inspector_script: Script = preload("editor_inspector_plugin.gd")
|
||||
var inspector_plugin: EditorInspectorPlugin = inspector_script.new()
|
||||
|
||||
var gizmo_script: Script = preload("gizmo.gd")
|
||||
var gizmo_plugin: EditorNode3DGizmoPlugin = gizmo_script.new()
|
||||
|
||||
func _get_plugin_name() -> String:
|
||||
return "LightmapProbeGrid"
|
||||
|
||||
func _enter_tree() -> void:
|
||||
add_custom_type("LightmapProbeGrid", "Node3D", custom_node, icon)
|
||||
add_inspector_plugin(inspector_plugin)
|
||||
add_node_3d_gizmo_plugin(gizmo_plugin)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_custom_type("LightmapProbeGrid")
|
||||
remove_inspector_plugin(inspector_plugin)
|
||||
remove_node_3d_gizmo_plugin(gizmo_plugin)
|
||||
1
addons/lightmap_probe_grid/plugin.gd.uid
Normal file
1
addons/lightmap_probe_grid/plugin.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://df4lt60aa1ac8
|
||||
13
addons/lightmap_probe_grid/style_box_flat.tres
Normal file
13
addons/lightmap_probe_grid/style_box_flat.tres
Normal file
@@ -0,0 +1,13 @@
|
||||
[gd_resource type="StyleBoxFlat" format=3 uid="uid://4nfudftbnexo"]
|
||||
|
||||
[resource]
|
||||
draw_center = false
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.5, 0.5, 0.5, 1)
|
||||
corner_radius_top_left = 2
|
||||
corner_radius_top_right = 2
|
||||
corner_radius_bottom_right = 2
|
||||
corner_radius_bottom_left = 2
|
||||
Reference in New Issue
Block a user