diff options
57 files changed, 2610 insertions, 812 deletions
diff --git a/.github/workflows/javascript_builds.yml b/.github/workflows/javascript_builds.disabled index 09c4c41d53..09c4c41d53 100644 --- a/.github/workflows/javascript_builds.yml +++ b/.github/workflows/javascript_builds.disabled @@ -2,10 +2,6 @@ Alexander Holland <alexander.holland@live.de> Alexander Holland <alexander.holland@live.de> <alexander.holland@haw-hamburg.de> Alexander Holland <alexander.holland@live.de> <AlexHolly> Andrea Catania <info@andreacatania.com> -Andreas Haas <liu.gam3@gmail.com> -Andreas Haas <liu.gam3@gmail.com> <hinsbart@gmail.com> -Andreas Haas <liu.gam3@gmail.com> <hinsbart@users.noreply.github.com> -Andreas Haas <liu.gam3@gmail.com> <entenflugstuhl@gmail.com> Anish Bhobe <anishbhobe@hotmail.com> Anutrix <numaanzaheerahmed@yahoo.com> Aren Villanueva <arenvillanueva@yomogi-soft.com> <aren@displaysweet.com> @@ -69,6 +65,11 @@ Kelly Thomas <kelly.thomas@hotmail.com.au> K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> Leon Krause <lk@leonkrause.com> <eska@eska.me> Leon Krause <lk@leonkrause.com> <eska014@users.noreply.github.com> +Liz Haas <27thLiz@gmail.com> +Liz Haas <27thLiz@gmail.com> <liu.gam3@gmail.com> +Liz Haas <27thLiz@gmail.com> <hinsbart@gmail.com> +Liz Haas <27thLiz@gmail.com> <hinsbart@users.noreply.github.com> +Liz Haas <27thLiz@gmail.com> <entenflugstuhl@gmail.com> Manuel Strey <manuel.strey@gmx.de> Marcelo Fernandez <marcelofg55@gmail.com> Marcin Zawiejski <dragmz@gmail.com> diff --git a/AUTHORS.md b/AUTHORS.md index f1803c672c..40aa50f8b6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -32,7 +32,6 @@ name is available. Alexey Khoroshavin (allkhor) Alket Rexhepi (alketii) Andrea Catania (AndreaCatania) - Andreas Haas (Hinsbart) Andrii Doroshenko (Xrayez) Andy Moss (MillionOstrich) Anish Bhobe (KidRigger) @@ -97,6 +96,7 @@ name is available. Jakub Grzesik (kubecz3k) James Buck (jbuck3) Jérôme Gully (Nutriz) + Jia Jun Chai (SkyLucilfer) Joan Fons Sanchez (JFonS) Johan Manuel (29jm) Joshua Grams (JoshuaGrams) @@ -107,7 +107,8 @@ name is available. Kostadin Damyanov (Max-Might) K. S. Ernest (iFire) Lee (fire) lawnjelly - Leon Krause (eska014) + Leon Krause (leonkrause) + Liz Haas (27thLiz) Lucien Menassol (Kanabenki) m4nu3lf Maganty Rushyendra (mrushyendra) @@ -134,6 +135,7 @@ name is available. muiroc Nathan Warden (NathanWarden) Nils André-Chang (NilsIrl) + Noah Beard (TwistedTwigleg) Nuno Donato (nunodonato) Ovnuniarchos Pascal Richter (ShyRed) @@ -157,12 +159,15 @@ name is available. Robin Hübner (profan) romulox-x Ruslan Mustakov (endragor) + Ryan Roden-Corrent (rrcore) Saniko (sanikoyes) santouits SaracenOne + Sergey Minakov (naithar) sersoong Shiqing (kawa-yoiko) Simon Wenner (swenner) + Stijn Hinlopen (hinlopen) Swarnim Arun (minraws) Thakee Nathees (ThakeeNathees) Theo Hallenius (TheoXD) @@ -12,26 +12,35 @@ generous deed immortalized in the next stable release of Godot Engine. ## Platinum sponsors + Gamblify <https://www.gamblify.com> Heroic Labs <https://heroiclabs.com> Interblock <http://interblockgaming.com> ## Gold sponsors - Gamblify <https://www.gamblify.com> + None currently, become one! <https://godotengine.org/donate> + +## Silver sponsors + Moonwards <https://www.moonwards.com> +## Bronze sponsors + + Brandon Lamb + ## Mini sponsors AD Ford Alan Beauchamp albinaask Alejandro Saucedo + alex brown Andrew Dunai - Brandon Lamb Christian Baune Christopher Montesano Darkhan Baimyrza Darrin Massena + David Mydlarz Digital Grows Dov Zimring Edward Flick @@ -43,6 +52,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jasper Brooks Javary Co. Jeffery Chiu + Jonah Stich Justin Arnold Kyle Szklenski Marcel Kräml @@ -51,45 +61,39 @@ generous deed immortalized in the next stable release of Godot Engine. Mike King Nathan Warden Neal Gompa (Conan Kudo) + Patrick Schmidt Ronnie Cheng Slobodan Milnovic Stephan Lanfermann Steve + Thomas Krampl Tristan Pemble VilliHaukka + Violin Iliev + 蕭惟允 ## Gold donors - Bjarke - David Gehrig - David Snopek - Ed Morley - Florian Rämisch - Jakub Grzesik - Manuele Finocchiaro - Officine Pixel S.n.c. - Rami - Ronan Zeegers - Sofox - Spicylewd - Taylor Ritenour - Zaven Muradyan - - Andreas Schüle - Andres Hernandez + Andrew Morsillo Asher Glick Austen McRae Bernhard Werner beVR Carlo Cabanilla + Chris Goddard Christopher Case Daniel James + David Gehrig David Giardi - Default Name + David Snopek + Ed Morley eggs + Ellen Poe Florian Breisch + Florian Rämisch Forge Gamejunkey + Jakub Grzesik Javier Roman Jon Woodward Karl Werf @@ -97,95 +101,89 @@ generous deed immortalized in the next stable release of Godot Engine. Lex Steers Luke Maciej Pendolski + Manuele Finocchiaro + Markus Wiesner Matthew Hillier Mohamed Ikbel Boulabiar Monster Vial + Officine Pixel S.n.c. + Rami Rene + Rene Tailleur Retro Village Rob Messick Roland Fredenhagen + Ronan Zeegers Ryan Badour Sandro Jenny Sarksus Scott Wadden Sergey - thechris + Sofox + Spicylewd + Taylor Ritenour Tom Langwaldt Tricky Fat Cat tukon William Wold + xagonist + Zaven Muradyan - Alex Khayrullin - alice gambrell - Andrew Harris - Barugon - Chris Goddard - Chris Serino - Christian Padilla - Conrad Curry - Craig Smith - Darrian Little - dragonage13 - GiulianoB - Hoai Nam Tran - Horváth Péter - Jeff Nyte - Joan Fons - Joshua Flores - Leo Fidel R Liban - Michael Dürwald - Péter Magyar - Petr Malac - Rob - Robert Willes - Ronnie Ashlock - SKison - Thomas Bjarnelöf - Valryia - Vincent Henderson - Vojtěch - Wojciech Chojnacki - Xavier PATRICELLI - Zoran Kukulj - + Aaron Winter Adam Nakonieczny Adam Neumann Alexander J Maynard Alexey Dyadchenko + Alex Khayrullin + alice gambrell Andreas Funke André Frélicot + Andrew Harris aoshiwik - Ben Powell + Barugon + Can Eris Carlos de Sousa Marques Charlie Whitfield Chase Taranto Chelsea Hash Chris Petrich + Chris Serino Christian Alexander Bjørklund Bøhler Christian Leth Jeppesen Cody Parker + Conrad Curry Craig Ostrin - curtis Kramer + Craig Smith D + Darrian Little Dev To be curious Digital Denizen Easypete Edgar Sun Eugenio Hugo Salgüero Jáñez + Felix Brückner flesk F S Gary Hulst gavlig GGGames.org + GiulianoB Guilherme Felipe de C. G. da Silva Heath Hayes + Hoai Nam Tran + Horváth Péter Hu Hund - Isaac Clausman + Jared Jared White - Joe Flood + Jeff Nyte + Joan Fons + Joel Fivat + Joel Höglund John G Gentzel Jose Malheiro Joseph Crane + Joshie Sparks + Joshua Flores Joshua Lesperance Juan Velandia Julian Todd @@ -194,14 +192,18 @@ generous deed immortalized in the next stable release of Godot Engine. Kelteseth kickmaniac kinfox + Lachie Lain Ballard + Leo Fidel R Liban luca duran + MadScientistCarl Marcelo Dornbusch Lopes - Marcelo Henrique Gonçalves + Marisa Clardy Markus Fehr - Markus Wiesner Martin Eigel Matt Eunson + Michael + Michael Dürwald Mikado069 m kaersten MuffinManKen @@ -211,48 +213,64 @@ generous deed immortalized in the next stable release of Godot Engine. Patrick Ting Paul Hocker Paul Von Zimmerman + Pedro Silva Pete Goodwin + Péter Magyar + Petr Malac PhaineOfCatz pl Ranoller + Raymond Harris + Ricardo Alcantara + Rob + Robert Willes Rob McInroy Rocknight Studios + Ronnie Ashlock Ryan + Ryan Wilson Samuel Judd Scott Pilet - Scott Ryan-Taylor Sean Morgan Sean Robertson Sébastien Serban Serafimescu + Shishir Tandale + SKison SleepCircle spilldata + Steven Landow Stoned Xander Tahiti Bos TheLevelOfDetail . + Thomas Bjarnelöf Thomas Kurz + Timothy Pulliam Tobias Bocanegra Trent Fehl - Urho + Valryia + VikFro + Vincent Henderson + Vojtěch William Foster + Wojciech Chojnacki + Xavier PATRICELLI + xzibiting Zhou Tuizhi Zie Weaver - 蕭惟允 + Zoran Kukulj ## Silver donors 1D_Inc Aaron - Aaron Winter - Abel Crunk Abraham Haskins Acheron Adam Adam Brunnmeier - Adam Carr + Adam Carr Adam Long Adam McCurdy - Adam Netzel Adam N Webber Adam Smeltzer Adam Szymański @@ -265,18 +283,22 @@ generous deed immortalized in the next stable release of Godot Engine. AleMax Alessandro Senese Alexander Erlemann + Alexandre Beaudoin alex clavelle + Ali Al-Khalifa Allan Davis Allen Schade Andreas Krampitz André Simões andrew james morris Andrew Mansuetti + Andrew Rosenwinkel Andrew Thomas Ano Nim Anthony Avina AP Condomines Arda Erol + Arisaka Mayuki Armin Preiml Arseniy M Arthur S. Muszynski @@ -285,43 +307,49 @@ generous deed immortalized in the next stable release of Godot Engine. Aubrey Falconer B A Balázs Batári - Balázs Hasprai Bartosz Bielecki Benedikt Ben Vercammen Bernd Jänichen - Bjarne + Bjarne Voigtländer Black Block Blair Allen Bobby CC Wong Bram brian + Brian mc gowan + Brodie Fairhall Burney Waring + Caleb Gartner Cameron Meyer Carl van der Geest Carwyn Edwards Cas Brugman Cassidy James + Chad Steadman Chris Brown Chris Chapin Christian Winter + Christoffer Dahlblom Christoffer Sundbom - Christoph Brodmann Christophe Gagnier Christopher Schmitt Christoph Woinke Clay Heaton Cole Johnson - Cuauhtemoc Moreno Curt King - Daniel Kimblad + CzechBlueBear + Daniel De Macedo Daniel Johnson DanielMaximiano + Daniel Szarfman Daniel Tebbutt + Daren Scot Wilson Dave Walker David May David Woodard - Dimitri Stanojevic + David Zanetti + Dmitry Fisher Dmytro Korchynskyi Dominik Wetzel Donn Eddy @@ -341,6 +369,7 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Ellingson Eric Williams Erkki Seppälä + ET Garcia Evan Rose Fain Faisal Alkubaisi @@ -378,22 +407,20 @@ generous deed immortalized in the next stable release of Godot Engine. Jaiden Gerig Jaime Ruiz-Borau Vizárraga Jako Danar + James James A F Manley Jamiee H Jamie Massey Janders JARKKO PARVIAINEN + Jason Uechi Jean-Baptiste LEPESME Jeff Hungerford Jennifer Graves Jesse Dubay Joe Alden - Joel Fivat - Joel Höglund - Joel Setterberg - Johannes Goslar + Joe Klemmer John Gabriel - John Walker Jomei Jackson Jonas Jonas Bernemann @@ -405,7 +432,6 @@ generous deed immortalized in the next stable release of Godot Engine. Jon Sully Jordy Goodridge Jorge Antunes - Jose Aleman Jose C. Rubio Joseph Catrambone Josh Mitchell @@ -422,19 +448,19 @@ generous deed immortalized in the next stable release of Godot Engine. Karel Němec Kauzig Keedong Park + Keinan Powers Keith Bradner Kent Jofur Kevin McPhillips - Kevin Velasco Kiri Jolly Kjetil Haugland - Klagsam KsyTek Games Kuan Cheang kycho Kyle Appelgate Kyuppin Laurent Tréguier + LEMMiNO Leonardo Dimano Lin Chear Linus Lind Lundgren @@ -447,6 +473,7 @@ generous deed immortalized in the next stable release of Godot Engine. Marco Lardelli Mark Jad Mark Krenz + Markus Martin Markus Michael Egger Martin FIbik Martin Holas @@ -456,16 +483,16 @@ generous deed immortalized in the next stable release of Godot Engine. Marvin Mathieu Matt Edwards - Mauro Pellegrini + Matthew Booe Max Fiedler Maxime Blade Maxwell Megasploot Melissa Mears mewin + Michael Cullen Michael Haney Michał Skwarek - Mikael Olsson Mikayla Mike Birkhead Mike Cunningham @@ -484,12 +511,16 @@ generous deed immortalized in the next stable release of Godot Engine. Nick Allen Nick Macholl Niclas Eriksen + Nicolas Goll-Perrier Nicolás Montaña Nicolas SAN AGUSTIN NZ + '@oddgoo + OKV Oleg Reva Olivier Omar Delarosa + Oscar Domingo Oscar Norlander Pan Ip Parinya Teerakasemsuk @@ -502,16 +533,16 @@ generous deed immortalized in the next stable release of Godot Engine. Penguin Peter Philip Cohoe + Pierre-Nicolas Tollitte Piotr Góral Point08 + Preethi Vaidyanathan pwab Rad Cat Rafa Laguna - Ram Remi Rampin Rémi Verschelde Reneator - Ricardo Alcantara Richard Diss Richard Ivánek Robert Farr (Larington) @@ -522,7 +553,9 @@ generous deed immortalized in the next stable release of Godot Engine. Ronald Ho Hip (CrimsonZA) Ronan Ronny Mühle + Ross Squires Ryan Groom + Sam Caulfield Sam Edson Samuele Zolfanelli Scott D. Yelich @@ -531,15 +564,16 @@ generous deed immortalized in the next stable release of Godot Engine. Sebastian Michailidis Sebastian Vetter Sergio Mello-Grand + Shaher Shane Shane Sicienski Shane Spoor - Shiomi '- Duy Kevin Nguyen + Shiomi - Duy Kevin Nguyen Siim Raidma Simon Jonas Larsen + Simon Schoenenberger Simon Wenner Sintinium - SK smbe19 smo1704 soft circles @@ -547,7 +581,7 @@ generous deed immortalized in the next stable release of Godot Engine. Stefano Caronia Steve Cloete Svenne Krap - Taylor Fahlman + Tannen Helmers Terry tezuvholovdr TheVoiceInMyHead @@ -558,26 +592,30 @@ generous deed immortalized in the next stable release of Godot Engine. Tim Drumheller Tim Erskine Timothy B. MacDonald - Title Plinsut Tobbun Tobias Bradtke - Tom Glenn Toni Duran + Tony Zhao Torgeir Lilleskog Torsten Crass Travis O'Brien Trent Skinner + Triptych + Triumph263 . Troy Bonneau Tryggve Sollid Turgut Temucin Tyler Compton Tyler Stafos UltyX + Uther Valentí Gàmez Vaughan Ling Victor Vigilant Watch + Viktor Ismagilov Vincent Cloutier + Vitor Balbio Vladimir Savin waka nya Wayne Haak @@ -587,9 +625,9 @@ generous deed immortalized in the next stable release of Godot Engine. Wyatt Goodin Yegor Smirnov YiYin Gu - Yuri LaPointe Yuri Sizov Zgegnesh Hemomancer + ΒΑΣΙΛΗΣ ΓΕΩΡΓΑΚΟΠΟΥΛΟΣ 郝晨煜 ## Bronze donors diff --git a/core/core_builders.py b/core/core_builders.py index d03874608e..004475faa7 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -117,14 +117,18 @@ def make_donors_header(target, source, env): sections = [ "Platinum sponsors", "Gold sponsors", + "Silver sponsors", + "Bronze sponsors", "Mini sponsors", "Gold donors", "Silver donors", "Bronze donors", ] sections_id = [ - "DONORS_SPONSOR_PLAT", + "DONORS_SPONSOR_PLATINUM", "DONORS_SPONSOR_GOLD", + "DONORS_SPONSOR_SILVER", + "DONORS_SPONSOR_BRONZE", "DONORS_SPONSOR_MINI", "DONORS_GOLD", "DONORS_SILVER", diff --git a/core/engine.cpp b/core/engine.cpp index c8bfffd020..d08cf92ecb 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -158,8 +158,10 @@ Array Engine::get_copyright_info() const { Dictionary Engine::get_donor_info() const { Dictionary donors; - donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLAT); + donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLATINUM); donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD); + donors["silver_sponsors"] = array_from_info(DONORS_SPONSOR_SILVER); + donors["bronze_sponsors"] = array_from_info(DONORS_SPONSOR_BRONZE); donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI); donors["gold_donors"] = array_from_info(DONORS_GOLD); donors["silver_donors"] = array_from_info(DONORS_SILVER); diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 22ab83f358..c154d57a13 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -278,7 +278,7 @@ Vector2 CameraMatrix::get_viewport_half_extents() const { return Vector2(res.x, res.y); } -void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { +Vector2 CameraMatrix::get_far_plane_half_extents() const { const real_t *matrix = (const real_t *)this->matrix; ///////--- Far Plane ---/////// Plane far_plane = Plane(matrix[3] - matrix[2], @@ -303,8 +303,7 @@ void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { Vector3 res; far_plane.intersect_3(right_plane, top_plane, &res); - r_width = res.x; - r_height = res.y; + return Vector2(res.x, res.y); } bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 49fdecae02..c5cdd98377 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -74,7 +74,7 @@ struct CameraMatrix { bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const; Vector2 get_viewport_half_extents() const; - void get_far_plane_size(real_t &r_width, real_t &r_height) const; + Vector2 get_far_plane_half_extents() const; void invert(); CameraMatrix inverse() const; diff --git a/doc/classes/DynamicFont.xml b/doc/classes/DynamicFont.xml index 24df056c6b..5d8ae29175 100644 --- a/doc/classes/DynamicFont.xml +++ b/doc/classes/DynamicFont.xml @@ -12,7 +12,7 @@ dynamic_font.size = 64 $"Label".set("custom_fonts/font", dynamic_font) [/codeblock] - [b]Note:[/b] DynamicFont doesn't support features such as right-to-left typesetting, ligatures, text shaping, variable fonts and optional font features yet. If you wish to "bake" an optional font feature into a TTF font file, you can use [url=https://fontforge.org/]FontForge[/url] to do so. In FontForge, use [b]File > Generate Fonts[/b], click [b]Options[/b], choose the desired features then generate the font. + [b]Note:[/b] DynamicFont doesn't support features such as kerning, right-to-left typesetting, ligatures, text shaping, variable fonts and optional font features yet. If you wish to "bake" an optional font feature into a TTF font file, you can use [url=https://fontforge.org/]FontForge[/url] to do so. In FontForge, use [b]File > Generate Fonts[/b], click [b]Options[/b], choose the desired features then generate the font. </description> <tutorials> </tutorials> diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 12701d8688..297cccaaac 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -34,7 +34,7 @@ </return> <description> Returns a Dictionary of Arrays of donor names. - {[code]platinum_sponsors[/code], [code]gold_sponsors[/code], [code]mini_sponsors[/code], [code]gold_donors[/code], [code]silver_donors[/code], [code]bronze_donors[/code]} + {[code]platinum_sponsors[/code], [code]gold_sponsors[/code], [code]silver_sponsors[/code], [code]bronze_sponsors[/code], [code]mini_sponsors[/code], [code]gold_donors[/code], [code]silver_donors[/code], [code]bronze_donors[/code]} </description> </method> <method name="get_frames_drawn"> diff --git a/doc/classes/HSlider.xml b/doc/classes/HSlider.xml index afe9d10d2e..0cbb4fd455 100644 --- a/doc/classes/HSlider.xml +++ b/doc/classes/HSlider.xml @@ -5,6 +5,7 @@ </brief_description> <description> Horizontal slider. See [Slider]. This one goes from left (min) to right (max). + [b]Note:[/b] The [signal Range.changed] and [signal Range.value_changed] signals are part of the [Range] class which this class inherits from. </description> <tutorials> </tutorials> diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml index 68776df603..f18b2ce39f 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -5,6 +5,7 @@ </brief_description> <description> Base class for GUI sliders. + [b]Note:[/b] The [signal Range.changed] and [signal Range.value_changed] signals are part of the [Range] class which this class inherits from. </description> <tutorials> </tutorials> diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml index 9394d6b430..5830c9eaf3 100644 --- a/doc/classes/VSlider.xml +++ b/doc/classes/VSlider.xml @@ -5,6 +5,7 @@ </brief_description> <description> Vertical slider. See [Slider]. This one goes from bottom (min) to top (max). + [b]Note:[/b] The [signal Range.changed] and [signal Range.value_changed] signals are part of the [Range] class which this class inherits from. </description> <tutorials> </tutorials> diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index facd57418d..d3dff3f375 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -477,11 +477,6 @@ ConnectDialog::ConnectDialog() { advanced->set_text(TTR("Advanced")); advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed)); - // Add spacing so the tree and inspector are the same size. - Control *spacing = memnew(Control); - spacing->set_custom_minimum_size(Size2(0, 4) * EDSCALE); - vbc_right->add_child(spacing); - deferred = memnew(CheckBox); deferred->set_h_size_flags(0); deferred->set_text(TTR("Deferred")); @@ -528,6 +523,10 @@ struct _ConnectionsDockMethodInfoSort { } }; +void ConnectionsDock::_filter_changed(const String &p_text) { + update_tree(); +} + /* * Post-ConnectDialog callback for creating/editing connections. * Creates or edits connections based on state of the ConnectDialog when "Connect" is pressed. @@ -903,6 +902,7 @@ void ConnectionsDock::update_tree() { String name; if (!did_script) { + // Get script signals (including signals from any base scripts). Ref<Script> scr = selectedNode->get_script(); if (scr.is_valid()) { scr->get_script_signal_list(&node_signals2); @@ -928,15 +928,16 @@ void ConnectionsDock::update_tree() { icon = get_theme_icon("Object", "EditorIcons"); } - TreeItem *pitem = nullptr; + TreeItem *section_item = nullptr; + // Create subsections. if (node_signals2.size()) { - pitem = tree->create_item(root); - pitem->set_text(0, name); - pitem->set_icon(0, icon); - pitem->set_selectable(0, false); - pitem->set_editable(0, false); - pitem->set_custom_bg_color(0, get_theme_color("prop_subsection", "Editor")); + section_item = tree->create_item(root); + section_item->set_text(0, name); + section_item->set_icon(0, icon); + section_item->set_selectable(0, false); + section_item->set_editable(0, false); + section_item->set_custom_bg_color(0, get_theme_color("prop_subsection", "Editor")); node_signals2.sort(); } @@ -946,6 +947,12 @@ void ConnectionsDock::update_tree() { StringName signal_name = mi.name; String signaldesc = "("; PackedStringArray argnames; + + String filter_text = search_box->get_text(); + if (!filter_text.is_subsequence_ofi(signal_name)) { + continue; + } + if (mi.arguments.size()) { for (int i = 0; i < mi.arguments.size(); i++) { PropertyInfo &pi = mi.arguments[i]; @@ -965,13 +972,14 @@ void ConnectionsDock::update_tree() { } signaldesc += ")"; - TreeItem *item = tree->create_item(pitem); - item->set_text(0, String(signal_name) + signaldesc); + // Create the children of the subsection - the actual list of signals. + TreeItem *signal_item = tree->create_item(section_item); + signal_item->set_text(0, String(signal_name) + signaldesc); Dictionary sinfo; sinfo["name"] = signal_name; sinfo["args"] = argnames; - item->set_metadata(0, sinfo); - item->set_icon(0, get_theme_icon("Signal", "EditorIcons")); + signal_item->set_metadata(0, sinfo); + signal_item->set_icon(0, get_theme_icon("Signal", "EditorIcons")); // Set tooltip with the signal's documentation. { @@ -1007,7 +1015,7 @@ void ConnectionsDock::update_tree() { } // "::" separators used in make_custom_tooltip for formatting. - item->set_tooltip(0, String(signal_name) + "::" + signaldesc + "::" + descr); + signal_item->set_tooltip(0, String(signal_name) + "::" + signaldesc + "::" + descr); } // List existing connections @@ -1044,11 +1052,11 @@ void ConnectionsDock::update_tree() { path += ")"; } - TreeItem *item2 = tree->create_item(item); - item2->set_text(0, path); + TreeItem *connection_item = tree->create_item(signal_item); + connection_item->set_text(0, path); Connection cd = c; - item2->set_metadata(0, cd); - item2->set_icon(0, get_theme_icon("Slot", "EditorIcons")); + connection_item->set_metadata(0, cd); + connection_item->set_icon(0, get_theme_icon("Slot", "EditorIcons")); } } @@ -1069,6 +1077,14 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) { VBoxContainer *vbc = this; + search_box = memnew(LineEdit); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_box->set_placeholder(TTR("Filter signals")); + search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + search_box->connect("text_changed", callable_mp(this, &ConnectionsDock::_filter_changed)); + vbc->add_child(search_box); + tree = memnew(ConnectionsDockTree); tree->set_columns(1); tree->set_select_mode(Tree::SELECT_ROW); diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 9da9a8fb2c..48fdb91f5a 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -169,9 +169,12 @@ class ConnectionsDock : public VBoxContainer { PopupMenu *signal_menu; PopupMenu *slot_menu; UndoRedo *undo_redo; + LineEdit *search_box; Map<StringName, Map<StringName, String>> descr_cache; + void _filter_changed(const String &p_text); + void _make_or_edit_connection(); void _connect(ConnectDialog::ConnectionData cToMake); void _disconnect(TreeItem &item); diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index d99726c57c..aae476ccf4 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -155,12 +155,15 @@ EditorAbout::EditorAbout() { List<String> donor_sections; donor_sections.push_back(TTR("Platinum Sponsors")); donor_sections.push_back(TTR("Gold Sponsors")); + donor_sections.push_back(TTR("Silver Sponsors")); + donor_sections.push_back(TTR("Bronze Sponsors")); donor_sections.push_back(TTR("Mini Sponsors")); donor_sections.push_back(TTR("Gold Donors")); donor_sections.push_back(TTR("Silver Donors")); donor_sections.push_back(TTR("Bronze Donors")); - const char *const *donor_src[] = { DONORS_SPONSOR_PLAT, DONORS_SPONSOR_GOLD, - DONORS_SPONSOR_MINI, DONORS_GOLD, DONORS_SILVER, DONORS_BRONZE }; + const char *const *donor_src[] = { DONORS_SPONSOR_PLATINUM, DONORS_SPONSOR_GOLD, + DONORS_SPONSOR_SILVER, DONORS_SPONSOR_BRONZE, DONORS_SPONSOR_MINI, + DONORS_GOLD, DONORS_SILVER, DONORS_BRONZE }; tc->add_child(_populate_list(TTR("Donors"), donor_sections, donor_src, 3)); // License diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index dabee67033..381ff88890 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -498,6 +498,10 @@ void EditorNode::_notification(int p_what) { RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/quality/gi_probes/quality"))); RS::get_singleton()->gi_probe_set_quality(gi_probe_quality); + RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/volumetric_fog/volume_size"), GLOBAL_GET("rendering/volumetric_fog/volume_depth")); + RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/volumetric_fog/use_filter"))); + RS::get_singleton()->environment_set_volumetric_fog_directional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/directional_shadow_shrink")); + RS::get_singleton()->environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink")); } ResourceImporterTexture::get_singleton()->update_imports(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index dea76ac997..34d553b5f9 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -946,14 +946,11 @@ void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) { } float val = get_edited_object()->get(get_edited_property()); - if (val == 0) { - return; - } bool sg = val < 0; val = Math::absf(val); val = Math::log(val) / Math::log((float)2.0); - //logspace + // Logarithmic space. val += rel * 0.05; val = Math::pow(2.0f, val); @@ -961,6 +958,16 @@ void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) { val = -val; } + // 0 is a singularity, but both positive and negative values + // are otherwise allowed. Enforce 0+ as workaround. + if (Math::is_zero_approx(val)) { + val = 0.00001; + } + + // Limit to a reasonable value to prevent the curve going into infinity, + // which can cause crashes and other issues. + val = CLAMP(val, -1'000'000, 1'000'000); + emit_changed(get_edited_property(), val); easing_draw->update(); } @@ -1003,7 +1010,18 @@ void EditorPropertyEasing::_draw_easing() { } easing_draw->draw_multiline(lines, line_color, 1.0); - f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), font_color); + // Draw more decimals for small numbers since higher precision is usually required for fine adjustments. + int decimals; + if (Math::abs(exp) < 0.1 - CMP_EPSILON) { + decimals = 4; + } else if (Math::abs(exp) < 1 - CMP_EPSILON) { + decimals = 3; + } else if (Math::abs(exp) < 10 - CMP_EPSILON) { + decimals = 2; + } else { + decimals = 1; + } + f->draw(ci, Point2(10, 10 + f->get_ascent()), rtos(exp).pad_decimals(decimals), font_color); } void EditorPropertyEasing::update_property() { @@ -1035,6 +1053,11 @@ void EditorPropertyEasing::_spin_value_changed(double p_value) { if (Math::is_zero_approx(p_value)) { p_value = 0.00001; } + + // Limit to a reasonable value to prevent the curve going into infinity, + // which can cause crashes and other issues. + p_value = CLAMP(p_value, -1'000'000, 1'000'000); + emit_changed(get_edited_property(), p_value); _spin_focus_exited(); } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 4f37fcf39c..31903c89be 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2430,11 +2430,31 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { } } -void FileSystemDock::_update_import_dock() { - if (!import_dock_needs_update) { +void FileSystemDock::_get_imported_files(const String &p_path, Vector<String> &files) const { + if (!p_path.ends_with("/")) { + if (FileAccess::exists(p_path + ".import")) { + files.push_back(p_path); + } return; } + DirAccess *da = DirAccess::open(p_path); + da->list_dir_begin(); + String n = da->get_next(); + while (n != String()) { + if (n != "." && n != ".." && !n.ends_with(".import")) { + String npath = p_path + n + (da->current_is_dir() ? "/" : ""); + _get_imported_files(npath, files); + } + n = da->get_next(); + } + da->list_dir_end(); +} + +void FileSystemDock::_update_import_dock() { + if (!import_dock_needs_update) + return; + // List selected. Vector<String> selected; if (display_mode == DISPLAY_MODE_TREE_ONLY) { @@ -2444,29 +2464,24 @@ void FileSystemDock::_update_import_dock() { } else { // Use the file list. for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) { + if (!files->is_selected(i)) continue; - } selected.push_back(files->get_item_metadata(i)); } } + // Expand directory selection + Vector<String> efiles; + for (int i = 0; i < selected.size(); i++) { + _get_imported_files(selected[i], efiles); + } + // Check import. Vector<String> imports; String import_type; - for (int i = 0; i < selected.size(); i++) { - String fpath = selected[i]; - - if (fpath.ends_with("/")) { - imports.clear(); - break; - } - - if (!FileAccess::exists(fpath + ".import")) { - imports.clear(); - break; - } + for (int i = 0; i < efiles.size(); i++) { + String fpath = efiles[i]; Ref<ConfigFile> cf; cf.instance(); Error err = cf->load(fpath + ".import"); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index b0118f11aa..ec2a075834 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -195,6 +195,7 @@ private: void _file_multi_selected(int p_index, bool p_selected); void _tree_multi_selected(Object *p_item, int p_column, bool p_selected); + void _get_imported_files(const String &p_path, Vector<String> &files) const; void _update_import_dock(); void _get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const; diff --git a/editor/icons/GuiToggleOff.svg b/editor/icons/GuiToggleOff.svg index 928b55b201..9644ef176c 100644 --- a/editor/icons/GuiToggleOff.svg +++ b/editor/icons/GuiToggleOff.svg @@ -1 +1 @@ -<svg height="26" viewBox="0 0 42 25.999998" width="42" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><rect fill-opacity=".188235" height="16" rx="9" stroke-width="55.8958" width="38" x="2" y="5"/><circle cx="10" cy="13" r="5" stroke-width="97.3613"/></g></svg> +<svg height="16" viewBox="0 0 38 15.999999" width="38" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><rect fill-opacity=".188235" height="14" rx="7" stroke-width="55.8958" width="36" x="1" y="1"/><circle cx="8" cy="8" r="5" stroke-width="97.3613"/></g></svg> diff --git a/editor/icons/GuiToggleOn.svg b/editor/icons/GuiToggleOn.svg index a79a8290b1..8ab0998f71 100644 --- a/editor/icons/GuiToggleOn.svg +++ b/editor/icons/GuiToggleOn.svg @@ -1 +1 @@ -<svg height="26" viewBox="0 0 42 25.999998" width="42" xmlns="http://www.w3.org/2000/svg"><path d="m11 5c-4.986 0-9 3.568-9 8s4.014 8 9 8h20c4.986 0 9-3.568 9-8s-4.014-8-9-8zm21 3a5 5 0 0 1 5 5 5 5 0 0 1 -5 5 5 5 0 0 1 -5-5 5 5 0 0 1 5-5z" fill="#e0e0e0" stroke-width="55.8958"/></svg> +<svg height="16" viewBox="0 0 38 15.999999" width="38" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.878 0-7 3.122-7 7s3.122 7 7 7h22c3.878 0 7-3.122 7-7s-3.122-7-7-7zm22 2a5 5 0 0 1 5 5 5 5 0 0 1 -5 5 5 5 0 0 1 -5-5 5 5 0 0 1 5-5z" fill="#e0e0e0" stroke-width="55.8958"/></svg> diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index a613174ed9..274c64263f 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -342,11 +342,13 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { left_container->add_child(tileset_toolbar_container); tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE] = memnew(Button); + tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_flat(true); tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tileset_toolbar_button_pressed), varray(TOOL_TILESET_ADD_TEXTURE)); tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]); tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_tooltip(TTR("Add Texture(s) to TileSet.")); tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE] = memnew(Button); + tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_flat(true); tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tileset_toolbar_button_pressed), varray(TOOL_TILESET_REMOVE_TEXTURE)); tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]); tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_tooltip(TTR("Remove selected Texture from TileSet.")); @@ -405,12 +407,14 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tools[SELECT_NEXT] = memnew(Button); tool_hb->add_child(tools[SELECT_NEXT]); tool_hb->move_child(tools[SELECT_NEXT], WORKSPACE_CREATE_SINGLE); + tools[SELECT_NEXT]->set_flat(true); tools[SELECT_NEXT]->set_shortcut(ED_SHORTCUT("tileset_editor/next_shape", TTR("Next Coordinate"), KEY_PAGEDOWN)); tools[SELECT_NEXT]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SELECT_NEXT)); tools[SELECT_NEXT]->set_tooltip(TTR("Select the next shape, subtile, or Tile.")); tools[SELECT_PREVIOUS] = memnew(Button); tool_hb->add_child(tools[SELECT_PREVIOUS]); tool_hb->move_child(tools[SELECT_PREVIOUS], WORKSPACE_CREATE_SINGLE); + tools[SELECT_PREVIOUS]->set_flat(true); tools[SELECT_PREVIOUS]->set_shortcut(ED_SHORTCUT("tileset_editor/previous_shape", TTR("Previous Coordinate"), KEY_PAGEUP)); tools[SELECT_PREVIOUS]->set_tooltip(TTR("Select the previous shape, subtile, or Tile.")); tools[SELECT_PREVIOUS]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SELECT_PREVIOUS)); @@ -467,6 +471,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tools[TOOL_SELECT] = memnew(Button); toolbar->add_child(tools[TOOL_SELECT]); + tools[TOOL_SELECT]->set_flat(true); tools[TOOL_SELECT]->set_toggle_mode(true); tools[TOOL_SELECT]->set_button_group(tg); tools[TOOL_SELECT]->set_pressed(true); @@ -475,20 +480,24 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { separator_bitmask = memnew(VSeparator); toolbar->add_child(separator_bitmask); tools[BITMASK_COPY] = memnew(Button); + tools[BITMASK_COPY]->set_flat(true); tools[BITMASK_COPY]->set_tooltip(TTR("Copy bitmask.")); tools[BITMASK_COPY]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(BITMASK_COPY)); toolbar->add_child(tools[BITMASK_COPY]); tools[BITMASK_PASTE] = memnew(Button); + tools[BITMASK_PASTE]->set_flat(true); tools[BITMASK_PASTE]->set_tooltip(TTR("Paste bitmask.")); tools[BITMASK_PASTE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(BITMASK_PASTE)); toolbar->add_child(tools[BITMASK_PASTE]); tools[BITMASK_CLEAR] = memnew(Button); + tools[BITMASK_CLEAR]->set_flat(true); tools[BITMASK_CLEAR]->set_tooltip(TTR("Erase bitmask.")); tools[BITMASK_CLEAR]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(BITMASK_CLEAR)); toolbar->add_child(tools[BITMASK_CLEAR]); tools[SHAPE_NEW_RECTANGLE] = memnew(Button); toolbar->add_child(tools[SHAPE_NEW_RECTANGLE]); + tools[SHAPE_NEW_RECTANGLE]->set_flat(true); tools[SHAPE_NEW_RECTANGLE]->set_toggle_mode(true); tools[SHAPE_NEW_RECTANGLE]->set_button_group(tg); tools[SHAPE_NEW_RECTANGLE]->set_tooltip(TTR("Create a new rectangle.")); @@ -496,6 +505,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tools[SHAPE_NEW_POLYGON] = memnew(Button); toolbar->add_child(tools[SHAPE_NEW_POLYGON]); + tools[SHAPE_NEW_POLYGON]->set_flat(true); tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true); tools[SHAPE_NEW_POLYGON]->set_button_group(tg); tools[SHAPE_NEW_POLYGON]->set_tooltip(TTR("Create a new polygon.")); @@ -504,12 +514,14 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { separator_shape_toggle = memnew(VSeparator); toolbar->add_child(separator_shape_toggle); tools[SHAPE_TOGGLE_TYPE] = memnew(Button); + tools[SHAPE_TOGGLE_TYPE]->set_flat(true); tools[SHAPE_TOGGLE_TYPE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SHAPE_TOGGLE_TYPE)); toolbar->add_child(tools[SHAPE_TOGGLE_TYPE]); separator_delete = memnew(VSeparator); toolbar->add_child(separator_delete); tools[SHAPE_DELETE] = memnew(Button); + tools[SHAPE_DELETE]->set_flat(true); tools[SHAPE_DELETE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SHAPE_DELETE)); toolbar->add_child(tools[SHAPE_DELETE]); @@ -534,11 +546,13 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { separator_grid = memnew(VSeparator); toolbar->add_child(separator_grid); tools[SHAPE_KEEP_INSIDE_TILE] = memnew(Button); + tools[SHAPE_KEEP_INSIDE_TILE]->set_flat(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_tooltip(TTR("Keep polygon inside region Rect.")); toolbar->add_child(tools[SHAPE_KEEP_INSIDE_TILE]); tools[TOOL_GRID_SNAP] = memnew(Button); + tools[TOOL_GRID_SNAP]->set_flat(true); tools[TOOL_GRID_SNAP]->set_toggle_mode(true); tools[TOOL_GRID_SNAP]->set_tooltip(TTR("Enable snap and show grid (configurable via the Inspector).")); tools[TOOL_GRID_SNAP]->connect("toggled", callable_mp(this, &TileSetEditor::_on_grid_snap_toggled)); @@ -549,19 +563,23 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { toolbar->add_child(separator); tools[ZOOM_OUT] = memnew(Button); + tools[ZOOM_OUT]->set_flat(true); tools[ZOOM_OUT]->connect("pressed", callable_mp(this, &TileSetEditor::_zoom_out)); toolbar->add_child(tools[ZOOM_OUT]); tools[ZOOM_OUT]->set_tooltip(TTR("Zoom Out")); tools[ZOOM_1] = memnew(Button); + tools[ZOOM_1]->set_flat(true); tools[ZOOM_1]->connect("pressed", callable_mp(this, &TileSetEditor::_zoom_reset)); toolbar->add_child(tools[ZOOM_1]); tools[ZOOM_1]->set_tooltip(TTR("Zoom Reset")); tools[ZOOM_IN] = memnew(Button); + tools[ZOOM_IN]->set_flat(true); tools[ZOOM_IN]->connect("pressed", callable_mp(this, &TileSetEditor::_zoom_in)); toolbar->add_child(tools[ZOOM_IN]); tools[ZOOM_IN]->set_tooltip(TTR("Zoom In")); tools[VISIBLE_INFO] = memnew(Button); + tools[VISIBLE_INFO]->set_flat(true); tools[VISIBLE_INFO]->set_toggle_mode(true); tools[VISIBLE_INFO]->set_tooltip(TTR("Display Tile Names (Hold Alt Key)")); toolbar->add_child(tools[VISIBLE_INFO]); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 9be1a7c2fe..89feb436d8 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -171,7 +171,7 @@ void ProjectSettingsEditor::_update_advanced_bar() { } } - disable_add = !bad_category; + disable_add = bad_category; if (!property_text.is_valid_identifier()) { disable_add = true; @@ -327,11 +327,9 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { header->add_child(search_bar); search_box = memnew(LineEdit); - search_box->set_custom_minimum_size(Size2(300, 0)); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); search_bar->add_child(search_box); - search_bar->add_spacer(); - advanced = memnew(CheckButton); advanced->set_text(TTR("Advanced")); advanced->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_advanced_pressed)); @@ -345,12 +343,14 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { advanced_bar->hide(); header->add_child(advanced_bar); + advanced_bar->add_child(memnew(HSeparator)); + HBoxContainer *hbc = memnew(HBoxContainer); hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - advanced_bar->add_margin_child(TTR("Add or remove custom project settings."), hbc, true); + advanced_bar->add_margin_child(TTR("Add or Remove Custom Project Settings:"), hbc, true); category_box = memnew(LineEdit); - category_box->set_custom_minimum_size(Size2(140, 0) * EDSCALE); + category_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); category_box->connect("text_changed", callable_mp(this, &ProjectSettingsEditor::_text_field_changed)); category_box->set_placeholder(TTR("Category")); hbc->add_child(category_box); @@ -370,7 +370,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { hbc->add_child(l); type = memnew(OptionButton); - type->set_custom_minimum_size(Size2(70, 0) * EDSCALE); + type->set_custom_minimum_size(Size2(100, 0) * EDSCALE); hbc->add_child(type); // Start at 1 to avoid adding "Nil" as an option @@ -383,17 +383,17 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { hbc->add_child(l); feature_override = memnew(OptionButton); - feature_override->set_custom_minimum_size(Size2(70, 0) * EDSCALE); + feature_override->set_custom_minimum_size(Size2(100, 0) * EDSCALE); feature_override->connect("item_selected", callable_mp(this, &ProjectSettingsEditor::_feature_selected)); hbc->add_child(feature_override); - hbc->add_spacer(); - add_button = memnew(Button); + add_button->set_flat(true); add_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_add_setting)); hbc->add_child(add_button); del_button = memnew(Button); + del_button->set_flat(true); del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting)); hbc->add_child(del_button); @@ -401,8 +401,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { advanced_bar->add_child(error_label); } - header->add_child(memnew(HSeparator)); - inspector = memnew(SectionedInspector); inspector->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 211e365454..23990bca07 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -61,18 +61,16 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und // ---- 1st & 2nd row Label *lbl_search = memnew(Label); - lbl_search->set_text(TTR("Search")); + lbl_search->set_text(TTR("Search:")); lne_search = memnew(LineEdit); - lne_search->set_placeholder(TTR("Search")); lne_search->set_name("lne_search"); lne_search->set_h_size_flags(Control::SIZE_EXPAND_FILL); Label *lbl_replace = memnew(Label); - lbl_replace->set_text(TTR("Replace")); + lbl_replace->set_text(TTR("Replace:")); lne_replace = memnew(LineEdit); - lne_replace->set_placeholder(TTR("Replace")); lne_replace->set_name("lne_replace"); lne_replace->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -84,18 +82,16 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und // ---- 3rd & 4th row Label *lbl_prefix = memnew(Label); - lbl_prefix->set_text(TTR("Prefix")); + lbl_prefix->set_text(TTR("Prefix:")); lne_prefix = memnew(LineEdit); - lne_prefix->set_placeholder(TTR("Prefix")); lne_prefix->set_name("lne_prefix"); lne_prefix->set_h_size_flags(Control::SIZE_EXPAND_FILL); Label *lbl_suffix = memnew(Label); - lbl_suffix->set_text(TTR("Suffix")); + lbl_suffix->set_text(TTR("Suffix:")); lne_suffix = memnew(LineEdit); - lne_suffix->set_placeholder(TTR("Suffix")); lne_suffix->set_name("lne_suffix"); lne_suffix->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -106,8 +102,6 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und // -- Feature Tabs - const int feature_min_height = 160 * EDSCALE; - cbut_regex = memnew(CheckButton); cbut_regex->set_text(TTR("Use Regular Expressions")); vbc->add_child(cbut_regex); @@ -118,13 +112,13 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und tabc_features = memnew(TabContainer); tabc_features->set_tab_align(TabContainer::ALIGN_LEFT); + tabc_features->set_use_hidden_tabs_for_min_size(true); vbc->add_child(tabc_features); // ---- Tab Substitute VBoxContainer *vbc_substitute = memnew(VBoxContainer); vbc_substitute->set_h_size_flags(Control::SIZE_EXPAND_FILL); - vbc_substitute->set_custom_minimum_size(Size2(0, feature_min_height)); vbc_substitute->set_name(TTR("Substitute")); tabc_features->add_child(vbc_substitute); @@ -199,7 +193,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und chk_per_level_counter = memnew(CheckBox); chk_per_level_counter->set_text(TTR("Per-level Counter")); - chk_per_level_counter->set_tooltip(TTR("If set the counter restarts for each group of child nodes.")); + chk_per_level_counter->set_tooltip(TTR("If set, the counter restarts for each group of child nodes.")); vbc_substitute->add_child(chk_per_level_counter); HBoxContainer *hbc_count_options = memnew(HBoxContainer); @@ -241,7 +235,6 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und VBoxContainer *vbc_process = memnew(VBoxContainer); vbc_process->set_h_size_flags(Control::SIZE_EXPAND_FILL); vbc_process->set_name(TTR("Post-Process")); - vbc_process->set_custom_minimum_size(Size2(0, feature_min_height)); tabc_features->add_child(vbc_process); cbut_process = memnew(CheckBox); @@ -285,18 +278,14 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und vbc->add_child(sep_preview); lbl_preview_title = memnew(Label); - lbl_preview_title->set_text(TTR("Preview")); vbc->add_child(lbl_preview_title); lbl_preview = memnew(Label); - lbl_preview->set_text(""); - lbl_preview->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color("error_color", "Editor")); vbc->add_child(lbl_preview); // ---- Dialog related set_min_size(Size2(383, 0)); - //set_as_toplevel(true); get_ok()->set_text(TTR("Rename")); Button *but_reset = add_button(TTR("Reset")); @@ -307,7 +296,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und cbut_collapse_features->connect("toggled", callable_mp(this, &RenameDialog::_features_toggled)); - // Substitite Buttons + // Substitute Buttons lne_search->connect("focus_entered", callable_mp(this, &RenameDialog::_update_substitute)); lne_search->connect("focus_exited", callable_mp(this, &RenameDialog::_update_substitute)); @@ -391,7 +380,7 @@ void RenameDialog::_update_preview(String new_text) { String new_name = _apply_rename(preview_node, spn_count_start->get_value()); if (!has_errors) { - lbl_preview_title->set_text(TTR("Preview")); + lbl_preview_title->set_text(TTR("Preview:")); lbl_preview->set_text(new_name); if (new_name == preview_node->get_name()) { @@ -482,7 +471,7 @@ void RenameDialog::_error_handler(void *p_self, const char *p_func, const char * } self->has_errors = true; - self->lbl_preview_title->set_text(TTR("Regular Expression Error")); + self->lbl_preview_title->set_text(TTR("Regular Expression Error:")); self->lbl_preview->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color("error_color", "Editor")); self->lbl_preview->set_text(vformat(TTR("At character %s"), err_str)); } diff --git a/glsl_builders.py b/glsl_builders.py index 9391fa1b65..af9afcae70 100644 --- a/glsl_builders.py +++ b/glsl_builders.py @@ -14,6 +14,7 @@ class RDHeaderStruct: self.vertex_included_files = [] self.fragment_included_files = [] + self.compute_included_files = [] self.reading = "" self.line_offset = 0 diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index b270e04c63..47982d519a 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -154,6 +154,14 @@ inline bool is_point_in_triangle(const Vector3 &p_point, const Vector3 p_vertice return true; } +inline static bool is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_vertex_snap2) { + real_t det = p_vertices[0].x * p_vertices[1].y - p_vertices[0].x * p_vertices[2].y + + p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x + + p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x; + + return det < p_vertex_snap2; +} + inline static bool are_segements_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) { Vector2 segment1 = p_segment1_points[1] - p_segment1_points[0]; Vector2 segment2 = p_segment2_points[1] - p_segment2_points[0]; @@ -1117,6 +1125,11 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { face_vertices[2].uv }; + // Skip degenerate triangles. + if (is_triangle_degenerate(points, vertex_snap2)) { + continue; + } + // Check if point is existing face vertex. for (int i = 0; i < 3; ++i) { if ((p_point - face_vertices[i].point).length_squared() < vertex_snap2) { @@ -1198,11 +1211,8 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { // The new vertex is the last vertex. for (int i = 0; i < 3; ++i) { // Don't create degenerate triangles. - Vector2 edge[2] = { points[i], points[(i + 1) % 3] }; - Vector2 new_edge1[2] = { vertices[new_vertex_idx].point, points[i] }; - Vector2 new_edge2[2] = { vertices[new_vertex_idx].point, points[(i + 1) % 3] }; - if (are_segements_parallel(edge, new_edge1, vertex_snap2) && - are_segements_parallel(edge, new_edge2, vertex_snap2)) { + Vector2 new_points[3] = { points[i], points[(i + 1) % 3], vertices[new_vertex_idx].point }; + if (is_triangle_degenerate(new_points, vertex_snap2)) { continue; } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index cc1622722e..2eb12d10fa 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -270,6 +270,7 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0.1,8,0.01"), "set_param", "get_param", PARAM_SHADOW_BLUR); ADD_GROUP("Editor", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); @@ -292,6 +293,7 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS); BIND_ENUM_CONSTANT(PARAM_SHADOW_PANCAKE_SIZE); BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR); + BIND_ENUM_CONSTANT(PARAM_SHADOW_VOLUMETRIC_FOG_FADE); BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS); BIND_ENUM_CONSTANT(PARAM_MAX); @@ -345,6 +347,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_BIAS, 0.02); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); + set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 1.0); set_param(PARAM_SHADOW_FADE_START, 1); set_disable_scale(true); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index f106151472..69c0478b86 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -58,6 +58,7 @@ public: PARAM_SHADOW_BIAS = RS::LIGHT_PARAM_SHADOW_BIAS, PARAM_SHADOW_PANCAKE_SIZE = RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE, PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR, + PARAM_SHADOW_VOLUMETRIC_FOG_FADE = RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS, PARAM_MAX = RS::LIGHT_PARAM_MAX }; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 5afc1f438e..14167531a5 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -578,7 +578,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (handled) { accept_event(); - } else if (!k->get_command()) { + } else if (!k->get_command() || (k->get_command() && k->get_alt())) { if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) { if (editable) { selection_delete(); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 3670f13705..ae2f99e91d 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -62,8 +62,8 @@ void SpinBox::_text_entered(const String &p_string) { Variant value = expr->execute(Array(), nullptr, false); if (value.get_type() != Variant::NIL) { set_value(value); - _value_changed(0); } + _value_changed(0); } LineEdit *SpinBox::get_line_edit() { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index e17085cafc..d6d8e74748 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1872,6 +1872,9 @@ void TextEdit::indent_right() { for (int i = start_line; i <= end_line; i++) { String line_text = get_line(i); + if (line_text.size() == 0 && is_selection_active()) { + continue; + } if (indent_using_spaces) { // We don't really care where selection is - we just need to know indentation level at the beginning of the line. int left = _find_first_non_whitespace_column_of_line(line_text); @@ -3604,7 +3607,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (!keycode_handled && !k->get_command()) { // For German keyboards. + if (!keycode_handled && (!k->get_command() || (k->get_command() && k->get_alt()))) { // For German keyboards. if (k->get_unicode() >= 32) { if (readonly) { diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 94629a77b9..a174c3eebe 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -694,150 +694,127 @@ bool Environment::is_fog_enabled() const { return fog_enabled; } -void Environment::set_fog_color(const Color &p_color) { - fog_color = p_color; +void Environment::set_fog_light_color(const Color &p_light_color) { + fog_light_color = p_light_color; _update_fog(); } - -Color Environment::get_fog_color() const { - return fog_color; +Color Environment::get_fog_light_color() const { + return fog_light_color; } - -void Environment::set_fog_sun_color(const Color &p_color) { - fog_sun_color = p_color; +void Environment::set_fog_light_energy(float p_amount) { + fog_light_energy = p_amount; _update_fog(); } - -Color Environment::get_fog_sun_color() const { - return fog_sun_color; +float Environment::get_fog_light_energy() const { + return fog_light_energy; } - -void Environment::set_fog_sun_amount(float p_amount) { - fog_sun_amount = p_amount; +void Environment::set_fog_sun_scatter(float p_amount) { + fog_sun_scatter = p_amount; _update_fog(); } - -float Environment::get_fog_sun_amount() const { - return fog_sun_amount; +float Environment::get_fog_sun_scatter() const { + return fog_sun_scatter; } - -void Environment::_update_fog() { - RS::get_singleton()->environment_set_fog( - environment, - fog_enabled, - fog_color, - fog_sun_color, - fog_sun_amount); +void Environment::set_fog_density(float p_amount) { + fog_density = p_amount; + _update_fog(); } - -void Environment::set_fog_depth_enabled(bool p_enabled) { - fog_depth_enabled = p_enabled; - _update_fog_depth(); +float Environment::get_fog_density() const { + return fog_density; } - -bool Environment::is_fog_depth_enabled() const { - return fog_depth_enabled; +void Environment::set_fog_height(float p_amount) { + fog_height = p_amount; + _update_fog(); } - -void Environment::set_fog_depth_begin(float p_distance) { - fog_depth_begin = p_distance; - _update_fog_depth(); +float Environment::get_fog_height() const { + return fog_height; } - -float Environment::get_fog_depth_begin() const { - return fog_depth_begin; +void Environment::set_fog_height_density(float p_amount) { + fog_height_density = p_amount; + _update_fog(); } - -void Environment::set_fog_depth_end(float p_distance) { - fog_depth_end = p_distance; - _update_fog_depth(); +float Environment::get_fog_height_density() const { + return fog_height_density; } -float Environment::get_fog_depth_end() const { - return fog_depth_end; +void Environment::_update_fog() { + RS::get_singleton()->environment_set_fog( + environment, + fog_enabled, + fog_light_color, + fog_light_energy, + fog_sun_scatter, + fog_density, + fog_height, + fog_height_density); } -void Environment::set_fog_depth_curve(float p_curve) { - fog_depth_curve = p_curve; - _update_fog_depth(); -} +// Volumetric Fog -float Environment::get_fog_depth_curve() const { - return fog_depth_curve; +void Environment::_update_volumetric_fog() { + RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, RS::EnvVolumetricFogShadowFilter(volumetric_fog_shadow_filter)); } -void Environment::set_fog_transmit_enabled(bool p_enabled) { - fog_transmit_enabled = p_enabled; - _update_fog_depth(); +void Environment::set_volumetric_fog_enabled(bool p_enable) { + volumetric_fog_enabled = p_enable; + _update_volumetric_fog(); } -bool Environment::is_fog_transmit_enabled() const { - return fog_transmit_enabled; +bool Environment::is_volumetric_fog_enabled() const { + return volumetric_fog_enabled; } - -void Environment::set_fog_transmit_curve(float p_curve) { - fog_transmit_curve = p_curve; - _update_fog_depth(); +void Environment::set_volumetric_fog_density(float p_density) { + p_density = CLAMP(p_density, 0.0000001, 1.0); + volumetric_fog_density = p_density; + _update_volumetric_fog(); } - -float Environment::get_fog_transmit_curve() const { - return fog_transmit_curve; +float Environment::get_volumetric_fog_density() const { + return volumetric_fog_density; } - -void Environment::_update_fog_depth() { - RS::get_singleton()->environment_set_fog_depth( - environment, - fog_depth_enabled, - fog_depth_begin, - fog_depth_end, - fog_depth_curve, - fog_transmit_enabled, - fog_transmit_curve); +void Environment::set_volumetric_fog_light(Color p_color) { + volumetric_fog_light = p_color; + _update_volumetric_fog(); } - -void Environment::set_fog_height_enabled(bool p_enabled) { - fog_height_enabled = p_enabled; - _update_fog_height(); +Color Environment::get_volumetric_fog_light() const { + return volumetric_fog_light; } - -bool Environment::is_fog_height_enabled() const { - return fog_height_enabled; +void Environment::set_volumetric_fog_light_energy(float p_begin) { + volumetric_fog_light_energy = p_begin; + _update_volumetric_fog(); } - -void Environment::set_fog_height_min(float p_distance) { - fog_height_min = p_distance; - _update_fog_height(); +float Environment::get_volumetric_fog_light_energy() const { + return volumetric_fog_light_energy; } - -float Environment::get_fog_height_min() const { - return fog_height_min; +void Environment::set_volumetric_fog_length(float p_length) { + volumetric_fog_length = p_length; + _update_volumetric_fog(); } - -void Environment::set_fog_height_max(float p_distance) { - fog_height_max = p_distance; - _update_fog_height(); +float Environment::get_volumetric_fog_length() const { + return volumetric_fog_length; } - -float Environment::get_fog_height_max() const { - return fog_height_max; +void Environment::set_volumetric_fog_detail_spread(float p_detail_spread) { + volumetric_fog_detail_spread = p_detail_spread; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_detail_spread() const { + return volumetric_fog_detail_spread; } -void Environment::set_fog_height_curve(float p_distance) { - fog_height_curve = p_distance; - _update_fog_height(); +void Environment::set_volumetric_fog_gi_inject(float p_gi_inject) { + volumetric_fog_gi_inject = p_gi_inject; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_gi_inject() const { + return volumetric_fog_gi_inject; } -float Environment::get_fog_height_curve() const { - return fog_height_curve; +void Environment::set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter) { + volumetric_fog_shadow_filter = p_filter; + _update_volumetric_fog(); } -void Environment::_update_fog_height() { - RS::get_singleton()->environment_set_fog_height( - environment, - fog_height_enabled, - fog_height_min, - fog_height_max, - fog_height_curve); +Environment::VolumetricFogShadowFilter Environment::get_volumetric_fog_shadow_filter() const { + return volumetric_fog_shadow_filter; } // Adjustment @@ -935,6 +912,7 @@ void Environment::_validate_property(PropertyInfo &property) const { static const char *hide_prefixes[] = { "fog_", + "volumetric_fog_", "auto_exposure_", "ss_reflections_", "ssao_", @@ -1222,52 +1200,58 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fog_enabled", "enabled"), &Environment::set_fog_enabled); ClassDB::bind_method(D_METHOD("is_fog_enabled"), &Environment::is_fog_enabled); - ClassDB::bind_method(D_METHOD("set_fog_color", "color"), &Environment::set_fog_color); - ClassDB::bind_method(D_METHOD("get_fog_color"), &Environment::get_fog_color); - ClassDB::bind_method(D_METHOD("set_fog_sun_color", "color"), &Environment::set_fog_sun_color); - ClassDB::bind_method(D_METHOD("get_fog_sun_color"), &Environment::get_fog_sun_color); - ClassDB::bind_method(D_METHOD("set_fog_sun_amount", "amount"), &Environment::set_fog_sun_amount); - ClassDB::bind_method(D_METHOD("get_fog_sun_amount"), &Environment::get_fog_sun_amount); - - ClassDB::bind_method(D_METHOD("set_fog_depth_enabled", "enabled"), &Environment::set_fog_depth_enabled); - ClassDB::bind_method(D_METHOD("is_fog_depth_enabled"), &Environment::is_fog_depth_enabled); - ClassDB::bind_method(D_METHOD("set_fog_depth_begin", "distance"), &Environment::set_fog_depth_begin); - ClassDB::bind_method(D_METHOD("get_fog_depth_begin"), &Environment::get_fog_depth_begin); - ClassDB::bind_method(D_METHOD("set_fog_depth_end", "distance"), &Environment::set_fog_depth_end); - ClassDB::bind_method(D_METHOD("get_fog_depth_end"), &Environment::get_fog_depth_end); - ClassDB::bind_method(D_METHOD("set_fog_depth_curve", "curve"), &Environment::set_fog_depth_curve); - ClassDB::bind_method(D_METHOD("get_fog_depth_curve"), &Environment::get_fog_depth_curve); - ClassDB::bind_method(D_METHOD("set_fog_transmit_enabled", "enabled"), &Environment::set_fog_transmit_enabled); - ClassDB::bind_method(D_METHOD("is_fog_transmit_enabled"), &Environment::is_fog_transmit_enabled); - ClassDB::bind_method(D_METHOD("set_fog_transmit_curve", "curve"), &Environment::set_fog_transmit_curve); - ClassDB::bind_method(D_METHOD("get_fog_transmit_curve"), &Environment::get_fog_transmit_curve); - - ClassDB::bind_method(D_METHOD("set_fog_height_enabled", "enabled"), &Environment::set_fog_height_enabled); - ClassDB::bind_method(D_METHOD("is_fog_height_enabled"), &Environment::is_fog_height_enabled); - ClassDB::bind_method(D_METHOD("set_fog_height_min", "height"), &Environment::set_fog_height_min); - ClassDB::bind_method(D_METHOD("get_fog_height_min"), &Environment::get_fog_height_min); - ClassDB::bind_method(D_METHOD("set_fog_height_max", "height"), &Environment::set_fog_height_max); - ClassDB::bind_method(D_METHOD("get_fog_height_max"), &Environment::get_fog_height_max); - ClassDB::bind_method(D_METHOD("set_fog_height_curve", "curve"), &Environment::set_fog_height_curve); - ClassDB::bind_method(D_METHOD("get_fog_height_curve"), &Environment::get_fog_height_curve); + ClassDB::bind_method(D_METHOD("set_fog_light_color", "light_color"), &Environment::set_fog_light_color); + ClassDB::bind_method(D_METHOD("get_fog_light_color"), &Environment::get_fog_light_color); + ClassDB::bind_method(D_METHOD("set_fog_light_energy", "light_energy"), &Environment::set_fog_light_energy); + ClassDB::bind_method(D_METHOD("get_fog_light_energy"), &Environment::get_fog_light_energy); + ClassDB::bind_method(D_METHOD("set_fog_sun_scatter", "sun_scatter"), &Environment::set_fog_sun_scatter); + ClassDB::bind_method(D_METHOD("get_fog_sun_scatter"), &Environment::get_fog_sun_scatter); + + ClassDB::bind_method(D_METHOD("set_fog_density", "density"), &Environment::set_fog_density); + ClassDB::bind_method(D_METHOD("get_fog_density"), &Environment::get_fog_density); + + ClassDB::bind_method(D_METHOD("set_fog_height", "height"), &Environment::set_fog_height); + ClassDB::bind_method(D_METHOD("get_fog_height"), &Environment::get_fog_height); + + ClassDB::bind_method(D_METHOD("set_fog_height_density", "height_density"), &Environment::set_fog_height_density); + ClassDB::bind_method(D_METHOD("get_fog_height_density"), &Environment::get_fog_height_density); ADD_GROUP("Fog", "fog_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_color"), "set_fog_color", "get_fog_color"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_sun_color"), "set_fog_sun_color", "get_fog_sun_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_fog_sun_amount", "get_fog_sun_amount"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_depth_enabled"), "set_fog_depth_enabled", "is_fog_depth_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_begin", PROPERTY_HINT_RANGE, "0,4000,0.1"), "set_fog_depth_begin", "get_fog_depth_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_end", PROPERTY_HINT_RANGE, "0,4000,0.1,or_greater"), "set_fog_depth_end", "get_fog_depth_end"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_curve", PROPERTY_HINT_EXP_EASING), "set_fog_depth_curve", "get_fog_depth_curve"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_transmit_enabled"), "set_fog_transmit_enabled", "is_fog_transmit_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_transmit_curve", PROPERTY_HINT_EXP_EASING), "set_fog_transmit_curve", "get_fog_transmit_curve"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_height_enabled"), "set_fog_height_enabled", "is_fog_height_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_min", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_min", "get_fog_height_min"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_max", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_max", "get_fog_height_max"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_curve", PROPERTY_HINT_EXP_EASING), "set_fog_height_curve", "get_fog_height_curve"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_fog_light_energy", "get_fog_light_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter"); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density"); + + ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); + ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_light", "color"), &Environment::set_volumetric_fog_light); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_light"), &Environment::get_volumetric_fog_light); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_density", "density"), &Environment::set_volumetric_fog_density); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_density"), &Environment::get_volumetric_fog_density); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_light_energy", "begin"), &Environment::set_volumetric_fog_light_energy); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_light_energy"), &Environment::get_volumetric_fog_light_energy); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_length", "length"), &Environment::set_volumetric_fog_length); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_length"), &Environment::get_volumetric_fog_length); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_detail_spread", "detail_spread"), &Environment::set_volumetric_fog_detail_spread); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_detail_spread"), &Environment::get_volumetric_fog_detail_spread); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_gi_inject", "gi_inject"), &Environment::set_volumetric_fog_gi_inject); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_shadow_filter", "shadow_filter"), &Environment::set_volumetric_fog_shadow_filter); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_shadow_filter"), &Environment::get_volumetric_fog_shadow_filter); + + ADD_GROUP("Volumetric Fog", "volumetric_fog_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_enabled"), "set_volumetric_fog_enabled", "is_volumetric_fog_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_EXP_RANGE, "0.00,16,0.01"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "volumetric_fog_shadow_filter", PROPERTY_HINT_ENUM, "Disabled,Low,Medium,High"), "set_volumetric_fog_shadow_filter", "get_volumetric_fog_shadow_filter"); // Adjustment @@ -1331,6 +1315,11 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT); + + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED); + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_LOW); + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM); + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_HIGH); } Environment::Environment() { @@ -1344,10 +1333,8 @@ Environment::Environment() { _update_sdfgi(); _update_glow(); _update_fog(); - _update_fog_depth(); - _update_fog_height(); _update_adjustment(); - + _update_volumetric_fog(); _change_notify(); } diff --git a/scene/resources/environment.h b/scene/resources/environment.h index f334d22115..d4d84f31aa 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -97,6 +97,13 @@ public: GLOW_BLEND_MODE_MIX, }; + enum VolumetricFogShadowFilter { + VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED, + VOLUMETRIC_FOG_SHADOW_FILTER_LOW, + VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM, + VOLUMETRIC_FOG_SHADOW_FILTER_HIGH, + }; + private: RID environment; @@ -177,24 +184,25 @@ private: // Fog bool fog_enabled = false; - Color fog_color = Color(0.5, 0.6, 0.7); - Color fog_sun_color = Color(1.0, 0.9, 0.7); - float fog_sun_amount = 0.0; - void _update_fog(); + Color fog_light_color = Color(0.5, 0.6, 0.7); + float fog_light_energy = 1.0; + float fog_sun_scatter = 0.0; + float fog_density = 0.001; + float fog_height = 0.0; + float fog_height_density = 0.0; //can be negative to invert effect - bool fog_depth_enabled = true; - float fog_depth_begin = 10.0; - float fog_depth_end = 100.0; - float fog_depth_curve = 1.0; - bool fog_transmit_enabled = false; - float fog_transmit_curve = 1.0; - void _update_fog_depth(); + void _update_fog(); - bool fog_height_enabled = false; - float fog_height_min = 10.0; - float fog_height_max = 0.0; - float fog_height_curve = 1.0; - void _update_fog_height(); + // Volumetric Fog + bool volumetric_fog_enabled = false; + float volumetric_fog_density = 0.01; + Color volumetric_fog_light = Color(0.0, 0.0, 0.0); + float volumetric_fog_light_energy = 1.0; + float volumetric_fog_length = 64.0; + float volumetric_fog_detail_spread = 2.0; + VolumetricFogShadowFilter volumetric_fog_shadow_filter = VOLUMETRIC_FOG_SHADOW_FILTER_LOW; + float volumetric_fog_gi_inject = 0.0; + void _update_volumetric_fog(); // Adjustment bool adjustment_enabled = false; @@ -344,36 +352,40 @@ public: float get_glow_hdr_luminance_cap() const; // Fog + void set_fog_enabled(bool p_enabled); bool is_fog_enabled() const; - void set_fog_color(const Color &p_color); - Color get_fog_color() const; - void set_fog_sun_color(const Color &p_color); - Color get_fog_sun_color() const; - void set_fog_sun_amount(float p_amount); - float get_fog_sun_amount() const; - - void set_fog_depth_enabled(bool p_enabled); - bool is_fog_depth_enabled() const; - void set_fog_depth_begin(float p_distance); - float get_fog_depth_begin() const; - void set_fog_depth_end(float p_distance); - float get_fog_depth_end() const; - void set_fog_depth_curve(float p_curve); - float get_fog_depth_curve() const; - void set_fog_transmit_enabled(bool p_enabled); - bool is_fog_transmit_enabled() const; - void set_fog_transmit_curve(float p_curve); - float get_fog_transmit_curve() const; - - void set_fog_height_enabled(bool p_enabled); - bool is_fog_height_enabled() const; - void set_fog_height_min(float p_distance); - float get_fog_height_min() const; - void set_fog_height_max(float p_distance); - float get_fog_height_max() const; - void set_fog_height_curve(float p_distance); - float get_fog_height_curve() const; + void set_fog_light_color(const Color &p_light_color); + Color get_fog_light_color() const; + void set_fog_light_energy(float p_amount); + float get_fog_light_energy() const; + void set_fog_sun_scatter(float p_amount); + float get_fog_sun_scatter() const; + + void set_fog_density(float p_amount); + float get_fog_density() const; + void set_fog_height(float p_amount); + float get_fog_height() const; + void set_fog_height_density(float p_amount); + float get_fog_height_density() const; + + // Volumetric Fog + void set_volumetric_fog_enabled(bool p_enable); + bool is_volumetric_fog_enabled() const; + void set_volumetric_fog_density(float p_density); + float get_volumetric_fog_density() const; + void set_volumetric_fog_light(Color p_color); + Color get_volumetric_fog_light() const; + void set_volumetric_fog_light_energy(float p_begin); + float get_volumetric_fog_light_energy() const; + void set_volumetric_fog_length(float p_length); + float get_volumetric_fog_length() const; + void set_volumetric_fog_detail_spread(float p_detail_spread); + float get_volumetric_fog_detail_spread() const; + void set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter); + VolumetricFogShadowFilter get_volumetric_fog_shadow_filter() const; + void set_volumetric_fog_gi_inject(float p_gi_inject); + float get_volumetric_fog_gi_inject() const; // Adjustment void set_adjustment_enabled(bool p_enabled); @@ -399,5 +411,6 @@ VARIANT_ENUM_CAST(Environment::SSAOBlur) VARIANT_ENUM_CAST(Environment::SDFGICascades) VARIANT_ENUM_CAST(Environment::SDFGIYScale) VARIANT_ENUM_CAST(Environment::GlowBlendMode) +VARIANT_ENUM_CAST(Environment::VolumetricFogShadowFilter) #endif // ENVIRONMENT_H diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 348fc423bb..e1282fdf71 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -86,7 +86,13 @@ public: virtual void environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) = 0; virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) = 0; - virtual void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) = 0; + + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter) = 0; + + virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0; + virtual void environment_set_volumetric_fog_filter_active(bool p_enable) = 0; + virtual void environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size) = 0; + virtual void environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size) = 0; virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) = 0; virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) = 0; @@ -104,9 +110,7 @@ public: virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) = 0; - virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0; - virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0; - virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0; + virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density) = 0; virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0; diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp index e620ad954d..390272b4b8 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp @@ -1229,6 +1229,50 @@ void RasterizerEffectsRD::resolve_gi(RID p_source_depth, RID p_source_normal_rou RD::get_singleton()->compute_list_end(); } +void RasterizerEffectsRD::reduce_shadow(RID p_source_shadow, RID p_dest_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, int p_shrink_limit, RD::ComputeListID compute_list) { + uint32_t push_constant[8] = { (uint32_t)p_source_size.x, (uint32_t)p_source_size.y, (uint32_t)p_source_rect.position.x, (uint32_t)p_source_rect.position.y, (uint32_t)p_shrink_limit, 0, 0, 0 }; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shadow_reduce.pipelines[SHADOW_REDUCE_REDUCE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_source_shadow, p_dest_shadow), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(uint32_t) * 8); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1); +} +void RasterizerEffectsRD::filter_shadow(RID p_shadow, RID p_backing_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, RenderingServer::EnvVolumetricFogShadowFilter p_filter, RD::ComputeListID compute_list, bool p_vertical, bool p_horizontal) { + uint32_t push_constant[8] = { (uint32_t)p_source_size.x, (uint32_t)p_source_size.y, (uint32_t)p_source_rect.position.x, (uint32_t)p_source_rect.position.y, 0, 0, 0, 0 }; + + switch (p_filter) { + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED: + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_LOW: { + push_constant[5] = 0; + } break; + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM: { + push_constant[5] = 9; + } break; + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_HIGH: { + push_constant[5] = 18; + } break; + } + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shadow_reduce.pipelines[SHADOW_REDUCE_FILTER]); + if (p_vertical) { + push_constant[6] = 1; + push_constant[7] = 0; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_shadow, p_backing_shadow), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(uint32_t) * 8); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1); + } + if (p_vertical && p_horizontal) { + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + if (p_horizontal) { + push_constant[6] = 0; + push_constant[7] = 1; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_backing_shadow, p_shadow), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(uint32_t) * 8); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1); + } +} RasterizerEffectsRD::RasterizerEffectsRD() { { // Initialize copy Vector<String> copy_modes; @@ -1560,6 +1604,20 @@ RasterizerEffectsRD::RasterizerEffectsRD() { } } + { + Vector<String> shadow_reduce_modes; + shadow_reduce_modes.push_back("\n#define MODE_REDUCE\n"); + shadow_reduce_modes.push_back("\n#define MODE_FILTER\n"); + + shadow_reduce.shader.initialize(shadow_reduce_modes); + + shadow_reduce.shader_version = shadow_reduce.shader.version_create(); + + for (int i = 0; i < SHADOW_REDUCE_MAX; i++) { + shadow_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(shadow_reduce.shader.version_get_shader(shadow_reduce.shader_version, i)); + } + } + RD::SamplerState sampler; sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; @@ -1624,4 +1682,5 @@ RasterizerEffectsRD::~RasterizerEffectsRD() { ssr_scale.shader.version_free(ssr_scale.shader_version); sss.shader.version_free(sss.shader_version); tonemap.shader.version_free(tonemap.shader_version); + shadow_reduce.shader.version_free(shadow_reduce.shader_version); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h index 80849654de..2ba9610498 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h @@ -46,6 +46,7 @@ #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_filter.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_scale.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/specular_merge.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/ssao.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl.gen.h" @@ -534,6 +535,18 @@ class RasterizerEffectsRD { RID pipelines[RESOLVE_MODE_MAX]; //3 quality levels } resolve; + enum ShadowReduceMode { + SHADOW_REDUCE_REDUCE, + SHADOW_REDUCE_FILTER, + SHADOW_REDUCE_MAX + }; + + struct ShadowReduce { + ShadowReduceShaderRD shader; + RID shader_version; + RID pipelines[2]; + } shadow_reduce; + RID default_sampler; RID default_mipmap_sampler; RID index_buffer; @@ -633,6 +646,9 @@ public: void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_giprobe, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_giprobe, Vector2i p_screen_size, int p_samples); + void reduce_shadow(RID p_source_shadow, RID p_dest_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, int p_shrink_limit, RenderingDevice::ComputeListID compute_list); + void filter_shadow(RID p_shadow, RID p_backing_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, RS::EnvVolumetricFogShadowFilter p_filter, RenderingDevice::ComputeListID compute_list, bool p_vertical = true, bool p_horizontal = true); + RasterizerEffectsRD(); ~RasterizerEffectsRD(); }; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index 8c122983da..f65c5d5de3 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -1145,12 +1145,20 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, RID p_rende scene_state.ubo.time = time; scene_state.ubo.gi_upscale_for_msaa = false; + scene_state.ubo.volumetric_fog_enabled = false; + scene_state.ubo.fog_enabled = false; if (p_render_buffers.is_valid()) { RenderBufferDataHighEnd *render_buffers = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); if (render_buffers->msaa != RS::VIEWPORT_MSAA_DISABLED) { scene_state.ubo.gi_upscale_for_msaa = true; } + + if (render_buffers_has_volumetric_fog(p_render_buffers)) { + scene_state.ubo.volumetric_fog_enabled = true; + scene_state.ubo.volumetric_fog_inv_length = 1.0 / render_buffers_get_volumetric_fog_end(p_render_buffers); + scene_state.ubo.volumetric_fog_detail_spread = 1.0 / render_buffers_get_volumetric_fog_detail_spread(p_render_buffers); //reverse lookup + } } #if 0 if (p_render_buffers.is_valid() && render_buffers_is_sdfgi_enabled(p_render_buffers)) { @@ -1265,12 +1273,29 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, RID p_rende scene_state.ubo.ssao_ao_affect = environment_get_ssao_ao_affect(p_environment); scene_state.ubo.ssao_light_affect = environment_get_ssao_light_affect(p_environment); - Color ao_color = environment_get_ao_color(p_environment); + Color ao_color = environment_get_ao_color(p_environment).to_linear(); scene_state.ubo.ao_color[0] = ao_color.r; scene_state.ubo.ao_color[1] = ao_color.g; scene_state.ubo.ao_color[2] = ao_color.b; scene_state.ubo.ao_color[3] = ao_color.a; + scene_state.ubo.fog_enabled = environment_is_fog_enabled(p_environment); + scene_state.ubo.fog_density = environment_get_fog_density(p_environment); + scene_state.ubo.fog_height = environment_get_fog_height(p_environment); + scene_state.ubo.fog_height_density = environment_get_fog_height_density(p_environment); + if (scene_state.ubo.fog_height_density >= 0.0001) { + scene_state.ubo.fog_height_density = 1.0 / scene_state.ubo.fog_height_density; + } + + Color fog_color = environment_get_fog_light_color(p_environment).to_linear(); + float fog_energy = environment_get_fog_light_energy(p_environment); + + scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy; + scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy; + scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy; + + scene_state.ubo.fog_sun_scatter = environment_get_fog_sun_scatter(p_environment); + } else { if (p_reflection_probe.is_valid() && storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_reflection_probe))) { scene_state.ubo.use_ambient_light = false; @@ -1754,6 +1779,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor RD::get_singleton()->draw_list_end(); if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { + RENDER_TIMESTAMP("Resolve Depth Pre-Pass"); if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE) { static int texture_samples[RS::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8, 16 }; storage->get_effects()->resolve_gi(render_buffer->depth_msaa, render_buffer->normal_roughness_buffer_msaa, using_giprobe ? render_buffer->giprobe_buffer_msaa : RID(), render_buffer->depth, render_buffer->normal_roughness_buffer, using_giprobe ? render_buffer->giprobe_buffer : RID(), Vector2i(render_buffer->width, render_buffer->height), texture_samples[render_buffer->msaa]); @@ -2502,7 +2528,17 @@ void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_b u.ids.push_back(render_buffers_get_gi_probe_buffer(p_render_buffers)); uniforms.push_back(u); } - + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID vfog = render_buffers_get_volumetric_fog_texture(p_render_buffers); + if (vfog.is_null()) { + vfog = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + } + u.ids.push_back(vfog); + uniforms.push_back(u); + } rb->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); } } @@ -2815,6 +2851,13 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag u.ids.push_back(render_buffers_get_default_gi_probe_buffer()); uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + uniforms.push_back(u); + } default_render_buffers_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h index a49173de98..1aad9039ff 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h @@ -359,6 +359,21 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { int32_t sdf_size[3]; uint32_t gi_upscale_for_msaa; + + uint32_t volumetric_fog_enabled; + float volumetric_fog_inv_length; + float volumetric_fog_detail_spread; + uint32_t volumetric_fog_pad; + + // Fog + + uint32_t fog_enabled; + float fog_density; + float fog_height; + float fog_height_density; + + float fog_light_color[3]; + float fog_sun_scatter; }; UBO ubo; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp index bdf9b71c56..5e39455c29 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -227,6 +227,7 @@ void RasterizerSceneRD::_sdfgi_erase(RenderBuffers *rb) { RD::get_singleton()->free(rb->sdfgi->lightprobe_data); RD::get_singleton()->free(rb->sdfgi->lightprobe_history_scroll); RD::get_singleton()->free(rb->sdfgi->occlusion_data); + RD::get_singleton()->free(rb->sdfgi->ambient_texture); RD::get_singleton()->free(rb->sdfgi->cascades_ubo); @@ -371,6 +372,16 @@ void RasterizerSceneRD::sdfgi_update(RID p_render_buffers, RID p_environment, co RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; sdfgi->lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, sdfgi->lightprobe_data); + + //texture handling ambient data, to integrate with volumetric foc + RD::TextureFormat tf_ambient = tf_probes; + tf_ambient.array_layers = sdfgi->cascades.size(); + tf_ambient.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; //pack well with RGBE + tf_ambient.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count; + tf_ambient.height = sdfgi->probe_axis_count; + tf_ambient.type = RD::TEXTURE_TYPE_2D_ARRAY; + //lightprobe texture is an octahedral texture + sdfgi->ambient_texture = RD::get_singleton()->texture_create(tf_ambient, RD::TextureView()); } sdfgi->cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES); @@ -930,6 +941,13 @@ void RasterizerSceneRD::sdfgi_update(RID p_render_buffers, RID p_environment, co u.ids.push_back(parent_average); uniforms.push_back(u); } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 14; + u.ids.push_back(sdfgi->ambient_texture); + uniforms.push_back(u); + } sdfgi->cascades[i].integrate_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 0); } @@ -1282,6 +1300,7 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm push_constant.ray_bias = rb->sdfgi->probe_bias; push_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; push_constant.image_size[1] = rb->sdfgi->probe_axis_count; + push_constant.store_ambient_texture = env->volumetric_fog_enabled; RID sky_uniform_set = sdfgi_shader.integrate_default_sky_uniform_set; push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_DISABLED; @@ -1375,6 +1394,96 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm RENDER_TIMESTAMP("<SDFGI Update Probes"); } +void RasterizerSceneRD::_setup_giprobes(RID p_render_buffers, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, uint32_t &r_gi_probes_used) { + r_gi_probes_used = 0; + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(rb == nullptr); + + RID gi_probe_buffer = render_buffers_get_gi_probe_buffer(p_render_buffers); + GI::GIProbeData gi_probe_data[RenderBuffers::MAX_GIPROBES]; + + bool giprobes_changed = false; + + Transform to_camera; + to_camera.origin = p_transform.origin; //only translation, make local + + for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { + RID texture; + if (i < p_gi_probe_cull_count) { + GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_gi_probe_cull_result[i]); + + if (gipi) { + texture = gipi->texture; + GI::GIProbeData &gipd = gi_probe_data[i]; + + RID base_probe = gipi->probe; + + Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera; + + gipd.xform[0] = to_cell.basis.elements[0][0]; + gipd.xform[1] = to_cell.basis.elements[1][0]; + gipd.xform[2] = to_cell.basis.elements[2][0]; + gipd.xform[3] = 0; + gipd.xform[4] = to_cell.basis.elements[0][1]; + gipd.xform[5] = to_cell.basis.elements[1][1]; + gipd.xform[6] = to_cell.basis.elements[2][1]; + gipd.xform[7] = 0; + gipd.xform[8] = to_cell.basis.elements[0][2]; + gipd.xform[9] = to_cell.basis.elements[1][2]; + gipd.xform[10] = to_cell.basis.elements[2][2]; + gipd.xform[11] = 0; + gipd.xform[12] = to_cell.origin.x; + gipd.xform[13] = to_cell.origin.y; + gipd.xform[14] = to_cell.origin.z; + gipd.xform[15] = 1; + + Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); + + gipd.bounds[0] = bounds.x; + gipd.bounds[1] = bounds.y; + gipd.bounds[2] = bounds.z; + + gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); + gipd.bias = storage->gi_probe_get_bias(base_probe); + gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe); + gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe); + gipd.anisotropy_strength = 0; + gipd.ao = storage->gi_probe_get_ao(base_probe); + gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); + gipd.mipmaps = gipi->mipmaps.size(); + } + + r_gi_probes_used++; + } + + if (texture == RID()) { + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + } + + if (texture != rb->giprobe_textures[i]) { + giprobes_changed = true; + rb->giprobe_textures[i] = texture; + } + } + + if (giprobes_changed) { + RD::get_singleton()->free(rb->gi_uniform_set); + rb->gi_uniform_set = RID(); + if (rb->volumetric_fog) { + if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + RD::get_singleton()->free(rb->volumetric_fog->uniform_set); + RD::get_singleton()->free(rb->volumetric_fog->uniform_set2); + } + rb->volumetric_fog->uniform_set = RID(); + rb->volumetric_fog->uniform_set2 = RID(); + } + } + + if (p_gi_probe_cull_count > 0) { + RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GI::GIProbeData) * MIN(RenderBuffers::MAX_GIPROBES, p_gi_probe_cull_count), gi_probe_data, true); + } +} + void RasterizerSceneRD::_process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_ambient_buffer, RID p_reflection_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count) { RENDER_TIMESTAMP("Render GI"); @@ -1490,81 +1599,6 @@ void RasterizerSceneRD::_process_gi(RID p_render_buffers, RID p_normal_roughness RD::get_singleton()->buffer_update(gi.sdfgi_ubo, 0, sizeof(GI::SDFGIData), &sdfgi_data, true); } - { - RID gi_probe_buffer = render_buffers_get_gi_probe_buffer(p_render_buffers); - GI::GIProbeData gi_probe_data[RenderBuffers::MAX_GIPROBES]; - - bool giprobes_changed = false; - - Transform to_camera; - to_camera.origin = p_transform.origin; //only translation, make local - - for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { - RID texture; - if (i < p_gi_probe_cull_count) { - GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_gi_probe_cull_result[i]); - - if (gipi) { - texture = gipi->texture; - GI::GIProbeData &gipd = gi_probe_data[i]; - - RID base_probe = gipi->probe; - - Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera; - - gipd.xform[0] = to_cell.basis.elements[0][0]; - gipd.xform[1] = to_cell.basis.elements[1][0]; - gipd.xform[2] = to_cell.basis.elements[2][0]; - gipd.xform[3] = 0; - gipd.xform[4] = to_cell.basis.elements[0][1]; - gipd.xform[5] = to_cell.basis.elements[1][1]; - gipd.xform[6] = to_cell.basis.elements[2][1]; - gipd.xform[7] = 0; - gipd.xform[8] = to_cell.basis.elements[0][2]; - gipd.xform[9] = to_cell.basis.elements[1][2]; - gipd.xform[10] = to_cell.basis.elements[2][2]; - gipd.xform[11] = 0; - gipd.xform[12] = to_cell.origin.x; - gipd.xform[13] = to_cell.origin.y; - gipd.xform[14] = to_cell.origin.z; - gipd.xform[15] = 1; - - Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); - - gipd.bounds[0] = bounds.x; - gipd.bounds[1] = bounds.y; - gipd.bounds[2] = bounds.z; - - gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); - gipd.bias = storage->gi_probe_get_bias(base_probe); - gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe); - gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe); - gipd.anisotropy_strength = 0; - gipd.ao = storage->gi_probe_get_ao(base_probe); - gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); - } - } - - if (texture == RID()) { - texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); - } - - if (texture != rb->giprobe_textures[i]) { - giprobes_changed = true; - rb->giprobe_textures[i] = texture; - } - } - - if (giprobes_changed) { - RD::get_singleton()->free(rb->gi_uniform_set); - rb->gi_uniform_set = RID(); - } - - if (p_gi_probe_cull_count > 0) { - RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GI::GIProbeData) * MIN(RenderBuffers::MAX_GIPROBES, p_gi_probe_cull_count), gi_probe_data, true); - } - } - if (rb->gi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) { Vector<RD::Uniform> uniforms; { @@ -2880,6 +2914,99 @@ void RasterizerSceneRD::environment_set_sdfgi(RID p_env, bool p_enable, RS::Envi env->sdfgi_y_scale = p_y_scale; } +void RasterizerSceneRD::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->fog_enabled = p_enable; + env->fog_light_color = p_light_color; + env->fog_light_energy = p_light_energy; + env->fog_sun_scatter = p_sun_scatter; + env->fog_density = p_density; + env->fog_height = p_height; + env->fog_height_density = p_height_density; +} + +bool RasterizerSceneRD::environment_is_fog_enabled(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, false); + + return env->fog_enabled; +} +Color RasterizerSceneRD::environment_get_fog_light_color(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, Color()); + return env->fog_light_color; +} +float RasterizerSceneRD::environment_get_fog_light_energy(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->fog_light_energy; +} +float RasterizerSceneRD::environment_get_fog_sun_scatter(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->fog_sun_scatter; +} +float RasterizerSceneRD::environment_get_fog_density(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->fog_density; +} +float RasterizerSceneRD::environment_get_fog_height(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + + return env->fog_height; +} +float RasterizerSceneRD::environment_get_fog_height_density(RID p_env) const { + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->fog_height_density; +} + +void RasterizerSceneRD::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RenderingServer::EnvVolumetricFogShadowFilter p_shadow_filter) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->volumetric_fog_enabled = p_enable; + env->volumetric_fog_density = p_density; + env->volumetric_fog_light = p_light; + env->volumetric_fog_light_energy = p_light_energy; + env->volumetric_fog_length = p_lenght; + env->volumetric_fog_detail_spread = p_detail_spread; + env->volumetric_fog_shadow_filter = p_shadow_filter; + env->volumetric_fog_gi_inject = p_gi_inject; +} + +void RasterizerSceneRD::environment_set_volumetric_fog_volume_size(int p_size, int p_depth) { + volumetric_fog_size = p_size; + volumetric_fog_depth = p_depth; +} + +void RasterizerSceneRD::environment_set_volumetric_fog_filter_active(bool p_enable) { + volumetric_fog_filter_active = p_enable; +} +void RasterizerSceneRD::environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size) { + p_shrink_size = nearest_power_of_2_templated(p_shrink_size); + if (volumetric_fog_directional_shadow_shrink == (uint32_t)p_shrink_size) { + return; + } + + _clear_shadow_shrink_stages(directional_shadow.shrink_stages); +} +void RasterizerSceneRD::environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size) { + p_shrink_size = nearest_power_of_2_templated(p_shrink_size); + if (volumetric_fog_positional_shadow_shrink == (uint32_t)p_shrink_size) { + return; + } + + for (uint32_t i = 0; i < shadow_atlas_owner.get_rid_count(); i++) { + ShadowAtlas *sa = shadow_atlas_owner.get_ptr_by_index(i); + _clear_shadow_shrink_stages(sa->shrink_stages); + } +} + void RasterizerSceneRD::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { sdfgi_ray_count = p_ray_count; } @@ -3286,6 +3413,7 @@ void RasterizerSceneRD::shadow_atlas_set_size(RID p_atlas, int p_size) { if (shadow_atlas->depth.is_valid()) { RD::get_singleton()->free(shadow_atlas->depth); shadow_atlas->depth = RID(); + _clear_shadow_shrink_stages(shadow_atlas->shrink_stages); } for (int i = 0; i < 4; i++) { //clear subdivisions @@ -3579,6 +3707,7 @@ void RasterizerSceneRD::directional_shadow_atlas_set_size(int p_size) { if (directional_shadow.depth.is_valid()) { RD::get_singleton()->free(directional_shadow.depth); + _clear_shadow_shrink_stages(directional_shadow.shrink_stages); directional_shadow.depth = RID(); } @@ -4951,6 +5080,8 @@ void RasterizerSceneRD::_process_ssao(RID p_render_buffers, RID p_environment, R Environment *env = environment_owner.getornull(p_environment); ERR_FAIL_COND(!env); + RENDER_TIMESTAMP("Process SSAO"); + if (rb->ssao.ao[0].is_valid() && rb->ssao.ao_full.is_valid() != ssao_half_size) { RD::get_singleton()->free(rb->ssao.depth); RD::get_singleton()->free(rb->ssao.ao[0]); @@ -5463,6 +5594,30 @@ RID RasterizerSceneRD::render_buffers_get_sdfgi_occlusion_texture(RID p_render_b return rb->sdfgi->occlusion_texture; } +bool RasterizerSceneRD::render_buffers_has_volumetric_fog(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, false); + + return rb->volumetric_fog != nullptr; +} +RID RasterizerSceneRD::render_buffers_get_volumetric_fog_texture(RID p_render_buffers) { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, RID()); + + return rb->volumetric_fog->fog_map; +} + +float RasterizerSceneRD::render_buffers_get_volumetric_fog_end(RID p_render_buffers) { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, 0); + return rb->volumetric_fog->length; +} +float RasterizerSceneRD::render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers) { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, 0); + return rb->volumetric_fog->spread; +} + void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); rb->width = p_width; @@ -5679,9 +5834,10 @@ void RasterizerSceneRD::_setup_reflections(RID *p_reflection_probe_cull_result, } } -void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count) { +void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count) { uint32_t light_count = 0; r_directional_light_count = 0; + r_positional_light_count = 0; sky_scene_state.directional_light_count = 0; for (int i = 0; i < p_light_cull_count; i++) { @@ -5797,7 +5953,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull light_data.shadow_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) * bias_scale; light_data.shadow_normal_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * light_instance_get_directional_shadow_texel_size(li, j); light_data.shadow_transmittance_bias[j] = storage->light_get_transmittance_bias(base) * bias_scale; - light_data.shadow_transmittance_z_scale[j] = light_instance_get_shadow_range(li, j); + light_data.shadow_z_range[j] = light_instance_get_shadow_range(li, j); light_data.shadow_range_begin[j] = light_instance_get_shadow_range_begin(li, j); RasterizerStorageRD::store_camera(shadow_mtx, light_data.shadow_matrices[j]); @@ -5826,6 +5982,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull float fade_start = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_FADE_START); light_data.fade_from = -light_data.shadow_split_offsets[3] * MIN(fade_start, 0.999); //using 1.0 would break smoothstep light_data.fade_to = -light_data.shadow_split_offsets[3]; + light_data.shadow_volumetric_fog_fade = 1.0 / storage->light_get_shadow_volumetric_fog_fade(base); light_data.soft_shadow_scale = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR); light_data.softshadow_angle = angular_diameter; @@ -5867,6 +6024,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull Transform light_transform = light_instance_get_base_transform(li); Cluster::LightData &light_data = cluster.lights[light_count]; + cluster.lights_instances[light_count] = li; float sign = storage->light_is_negative(base) ? -1 : 1; Color linear_col = storage->light_get_color(base).to_linear(); @@ -5965,6 +6123,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull light_data.atlas_rect[3] = rect.size.height; light_data.soft_shadow_scale = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR); + light_data.shadow_volumetric_fog_fade = 1.0 / storage->light_get_shadow_volumetric_fog_fade(base); if (type == RS::LIGHT_OMNI) { light_data.atlas_rect[3] *= 0.5; //one paraboloid on top of another @@ -6005,6 +6164,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull cluster.builder.add_light(type == RS::LIGHT_SPOT ? LightClusterBuilder::LIGHT_TYPE_SPOT : LightClusterBuilder::LIGHT_TYPE_OMNI, light_transform, radius, spot_angle); light_count++; + r_positional_light_count++; } break; } @@ -6152,6 +6312,526 @@ void RasterizerSceneRD::_setup_decals(const RID *p_decal_instances, int p_decal_ } } +void RasterizerSceneRD::_volumetric_fog_erase(RenderBuffers *rb) { + ERR_FAIL_COND(!rb->volumetric_fog); + + RD::get_singleton()->free(rb->volumetric_fog->light_density_map); + RD::get_singleton()->free(rb->volumetric_fog->fog_map); + memdelete(rb->volumetric_fog); + rb->volumetric_fog = nullptr; +} + +void RasterizerSceneRD::_allocate_shadow_shrink_stages(RID p_base, int p_base_size, Vector<ShadowShrinkStage> &shrink_stages, uint32_t p_target_size) { + //create fog mipmaps + uint32_t fog_texture_size = p_target_size; + uint32_t base_texture_size = p_base_size; + + ShadowShrinkStage first; + first.size = base_texture_size; + first.texture = p_base; + shrink_stages.push_back(first); //put depth first in case we dont find smaller ones + + while (fog_texture_size < base_texture_size) { + base_texture_size = MAX(base_texture_size / 8, fog_texture_size); + + ShadowShrinkStage s; + s.size = base_texture_size; + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = base_texture_size; + tf.height = base_texture_size; + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + if (base_texture_size == fog_texture_size) { + s.filter_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + } + + s.texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + shrink_stages.push_back(s); + } +} + +void RasterizerSceneRD::_clear_shadow_shrink_stages(Vector<ShadowShrinkStage> &shrink_stages) { + for (int i = 1; i < shrink_stages.size(); i++) { + RD::get_singleton()->free(shrink_stages[i].texture); + if (shrink_stages[i].filter_texture.is_valid()) { + RD::get_singleton()->free(shrink_stages[i].filter_texture); + } + } + shrink_stages.clear(); +} + +void RasterizerSceneRD::_update_volumetric_fog(RID p_render_buffers, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_gi_probe_count) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + Environment *env = environment_owner.getornull(p_environment); + + float ratio = float(rb->width) / float((rb->width + rb->height) / 2); + uint32_t target_width = uint32_t(float(volumetric_fog_size) * ratio); + uint32_t target_height = uint32_t(float(volumetric_fog_size) / ratio); + + if (rb->volumetric_fog) { + //validate + if (!env || !env->volumetric_fog_enabled || rb->volumetric_fog->width != target_width || rb->volumetric_fog->height != target_height || rb->volumetric_fog->depth != volumetric_fog_depth) { + _volumetric_fog_erase(rb); + _render_buffers_uniform_set_changed(p_render_buffers); + } + } + + if (!env || !env->volumetric_fog_enabled) { + //no reason to enable or update, bye + return; + } + + if (env && env->volumetric_fog_enabled && !rb->volumetric_fog) { + //required volumetric fog but not existing, create + rb->volumetric_fog = memnew(VolumetricFog); + rb->volumetric_fog->width = target_width; + rb->volumetric_fog->height = target_height; + rb->volumetric_fog->depth = volumetric_fog_depth; + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = target_width; + tf.height = target_height; + tf.depth = volumetric_fog_depth; + tf.type = RD::TEXTURE_TYPE_3D; + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + rb->volumetric_fog->light_density_map = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + + rb->volumetric_fog->fog_map = RD::get_singleton()->texture_create(tf, RD::TextureView()); + _render_buffers_uniform_set_changed(p_render_buffers); + } + + //update directional shadow + + if (p_use_directional_shadows) { + if (directional_shadow.shrink_stages.empty()) { + if (rb->volumetric_fog->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + //invalidate uniform set, we will need a new one + RD::get_singleton()->free(rb->volumetric_fog->uniform_set); + rb->volumetric_fog->uniform_set = RID(); + } + _allocate_shadow_shrink_stages(directional_shadow.depth, directional_shadow.size, directional_shadow.shrink_stages, volumetric_fog_directional_shadow_shrink); + } + + if (directional_shadow.shrink_stages.size() > 1) { + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + for (int i = 1; i < directional_shadow.shrink_stages.size(); i++) { + int32_t src_size = directional_shadow.shrink_stages[i - 1].size; + int32_t dst_size = directional_shadow.shrink_stages[i].size; + Rect2i r(0, 0, src_size, src_size); + int32_t shrink_limit = 8 / (src_size / dst_size); + + storage->get_effects()->reduce_shadow(directional_shadow.shrink_stages[i - 1].texture, directional_shadow.shrink_stages[i].texture, Size2i(src_size, src_size), r, shrink_limit, compute_list); + RD::get_singleton()->compute_list_add_barrier(compute_list); + if (env->volumetric_fog_shadow_filter != RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED && directional_shadow.shrink_stages[i].filter_texture.is_valid()) { + Rect2i rf(0, 0, dst_size, dst_size); + storage->get_effects()->filter_shadow(directional_shadow.shrink_stages[i].texture, directional_shadow.shrink_stages[i].filter_texture, Size2i(dst_size, dst_size), rf, env->volumetric_fog_shadow_filter, compute_list); + } + } + RD::get_singleton()->compute_list_end(); + } + } + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + + if (shadow_atlas) { + //shrink shadows that need to be shrunk + + bool force_shrink_shadows = false; + + if (shadow_atlas->shrink_stages.empty()) { + if (rb->volumetric_fog->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + //invalidate uniform set, we will need a new one + RD::get_singleton()->free(rb->volumetric_fog->uniform_set); + rb->volumetric_fog->uniform_set = RID(); + } + _allocate_shadow_shrink_stages(shadow_atlas->depth, shadow_atlas->size, shadow_atlas->shrink_stages, volumetric_fog_positional_shadow_shrink); + force_shrink_shadows = true; + } + + if (rb->volumetric_fog->last_shadow_filter != env->volumetric_fog_shadow_filter) { + //if shadow filter changed, invalidate caches + rb->volumetric_fog->last_shadow_filter = env->volumetric_fog_shadow_filter; + force_shrink_shadows = true; + } + + cluster.lights_shadow_rect_cache_count = 0; + + for (int i = 0; i < p_positional_light_count; i++) { + if (cluster.lights[i].shadow_color_enabled[3] > 127) { + RID li = cluster.lights_instances[i]; + + ERR_CONTINUE(!shadow_atlas->shadow_owners.has(li)); + + uint32_t key = shadow_atlas->shadow_owners[li]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_CONTINUE((int)shadow >= shadow_atlas->quadrants[quadrant].shadows.size()); + + ShadowAtlas::Quadrant::Shadow &s = shadow_atlas->quadrants[quadrant].shadows.write[shadow]; + + if (!force_shrink_shadows && s.fog_version == s.version) { + continue; //do not update, no need + } + + s.fog_version = s.version; + + uint32_t quadrant_size = shadow_atlas->size >> 1; + + Rect2i atlas_rect; + + atlas_rect.position.x = (quadrant & 1) * quadrant_size; + atlas_rect.position.y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + atlas_rect.position.x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + atlas_rect.position.y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + atlas_rect.size.x = shadow_size; + atlas_rect.size.y = shadow_size; + + cluster.lights_shadow_rect_cache[cluster.lights_shadow_rect_cache_count] = atlas_rect; + + cluster.lights_shadow_rect_cache_count++; + + if (cluster.lights_shadow_rect_cache_count == cluster.max_lights) { + break; //light limit reached + } + } + } + + if (cluster.lights_shadow_rect_cache_count > 0) { + //there are shadows to be shrunk, try to do them in parallel + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + for (int i = 1; i < shadow_atlas->shrink_stages.size(); i++) { + int32_t base_size = shadow_atlas->shrink_stages[0].size; + int32_t src_size = shadow_atlas->shrink_stages[i - 1].size; + int32_t dst_size = shadow_atlas->shrink_stages[i].size; + + uint32_t rect_divisor = base_size / src_size; + + int32_t shrink_limit = 8 / (src_size / dst_size); + + //shrink in parallel for more performance + for (uint32_t j = 0; j < cluster.lights_shadow_rect_cache_count; j++) { + Rect2i src_rect = cluster.lights_shadow_rect_cache[j]; + + src_rect.position /= rect_divisor; + src_rect.size /= rect_divisor; + + storage->get_effects()->reduce_shadow(shadow_atlas->shrink_stages[i - 1].texture, shadow_atlas->shrink_stages[i].texture, Size2i(src_size, src_size), src_rect, shrink_limit, compute_list); + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (env->volumetric_fog_shadow_filter != RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED && shadow_atlas->shrink_stages[i].filter_texture.is_valid()) { + uint32_t filter_divisor = base_size / dst_size; + + //filter in parallel for more performance + for (uint32_t j = 0; j < cluster.lights_shadow_rect_cache_count; j++) { + Rect2i dst_rect = cluster.lights_shadow_rect_cache[j]; + + dst_rect.position /= filter_divisor; + dst_rect.size /= filter_divisor; + + storage->get_effects()->filter_shadow(shadow_atlas->shrink_stages[i].texture, shadow_atlas->shrink_stages[i].filter_texture, Size2i(dst_size, dst_size), dst_rect, env->volumetric_fog_shadow_filter, compute_list, true, false); + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + for (uint32_t j = 0; j < cluster.lights_shadow_rect_cache_count; j++) { + Rect2i dst_rect = cluster.lights_shadow_rect_cache[j]; + + dst_rect.position /= filter_divisor; + dst_rect.size /= filter_divisor; + + storage->get_effects()->filter_shadow(shadow_atlas->shrink_stages[i].texture, shadow_atlas->shrink_stages[i].filter_texture, Size2i(dst_size, dst_size), dst_rect, env->volumetric_fog_shadow_filter, compute_list, false, true); + } + } + } + + RD::get_singleton()->compute_list_end(); + } + } + + //update volumetric fog + + if (rb->volumetric_fog->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + //re create uniform set if needed + + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + if (shadow_atlas == nullptr || shadow_atlas->shrink_stages.size() == 0) { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + } else { + u.ids.push_back(shadow_atlas->shrink_stages[shadow_atlas->shrink_stages.size() - 1].texture); + } + + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + if (directional_shadow.shrink_stages.size() == 0) { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + } else { + u.ids.push_back(directional_shadow.shrink_stages[directional_shadow.shrink_stages.size() - 1].texture); + } + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 3; + u.ids.push_back(get_positional_light_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 4; + u.ids.push_back(get_directional_light_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 5; + u.ids.push_back(get_cluster_builder_texture()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 6; + u.ids.push_back(get_cluster_builder_indices_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 7; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 8; + u.ids.push_back(rb->volumetric_fog->light_density_map); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 9; + u.ids.push_back(rb->volumetric_fog->fog_map); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 10; + u.ids.push_back(shadow_sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 11; + u.ids.push_back(render_buffers_get_gi_probe_buffer(p_render_buffers)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 12; + for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { + u.ids.push_back(rb->giprobe_textures[i]); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 13; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + rb->volumetric_fog->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, 0), 0); + + SWAP(uniforms.write[7].ids.write[0], uniforms.write[8].ids.write[0]); + + rb->volumetric_fog->uniform_set2 = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, 0), 0); + } + + bool using_sdfgi = env->volumetric_fog_gi_inject > 0.0001 && env->sdfgi_enabled && (rb->sdfgi != nullptr); + + if (using_sdfgi) { + if (rb->volumetric_fog->sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->sdfgi_uniform_set)) { + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 0; + u.ids.push_back(gi.sdfgi_ubo); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + u.ids.push_back(rb->sdfgi->ambient_texture); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + u.ids.push_back(rb->sdfgi->occlusion_texture); + uniforms.push_back(u); + } + + rb->volumetric_fog->sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, VOLUMETRIC_FOG_SHADER_DENSITY_WITH_SDFGI), 1); + } + } + + rb->volumetric_fog->length = env->volumetric_fog_length; + rb->volumetric_fog->spread = env->volumetric_fog_detail_spread; + + VolumetricFogShader::PushConstant push_constant; + + Vector2 frustum_near_size = p_cam_projection.get_viewport_half_extents(); + Vector2 frustum_far_size = p_cam_projection.get_far_plane_half_extents(); + float z_near = p_cam_projection.get_z_near(); + float z_far = p_cam_projection.get_z_far(); + float fog_end = env->volumetric_fog_length; + + Vector2 fog_far_size = frustum_near_size.lerp(frustum_far_size, (fog_end - z_near) / (z_far - z_near)); + Vector2 fog_near_size; + if (p_cam_projection.is_orthogonal()) { + fog_near_size = fog_far_size; + } else { + fog_near_size = Vector2(); + } + + push_constant.fog_frustum_size_begin[0] = fog_near_size.x; + push_constant.fog_frustum_size_begin[1] = fog_near_size.y; + + push_constant.fog_frustum_size_end[0] = fog_far_size.x; + push_constant.fog_frustum_size_end[1] = fog_far_size.y; + + push_constant.z_near = z_near; + push_constant.z_far = z_far; + + push_constant.fog_frustum_end = fog_end; + + push_constant.fog_volume_size[0] = rb->volumetric_fog->width; + push_constant.fog_volume_size[1] = rb->volumetric_fog->height; + push_constant.fog_volume_size[2] = rb->volumetric_fog->depth; + + push_constant.directional_light_count = p_directional_light_count; + + Color light = env->volumetric_fog_light.to_linear(); + push_constant.light_energy[0] = light.r * env->volumetric_fog_light_energy; + push_constant.light_energy[1] = light.g * env->volumetric_fog_light_energy; + push_constant.light_energy[2] = light.b * env->volumetric_fog_light_energy; + push_constant.base_density = env->volumetric_fog_density; + + push_constant.detail_spread = env->volumetric_fog_detail_spread; + push_constant.gi_inject = env->volumetric_fog_gi_inject; + + push_constant.cam_rotation[0] = p_cam_transform.basis[0][0]; + push_constant.cam_rotation[1] = p_cam_transform.basis[1][0]; + push_constant.cam_rotation[2] = p_cam_transform.basis[2][0]; + push_constant.cam_rotation[3] = 0; + push_constant.cam_rotation[4] = p_cam_transform.basis[0][1]; + push_constant.cam_rotation[5] = p_cam_transform.basis[1][1]; + push_constant.cam_rotation[6] = p_cam_transform.basis[2][1]; + push_constant.cam_rotation[7] = 0; + push_constant.cam_rotation[8] = p_cam_transform.basis[0][2]; + push_constant.cam_rotation[9] = p_cam_transform.basis[1][2]; + push_constant.cam_rotation[10] = p_cam_transform.basis[2][2]; + push_constant.cam_rotation[11] = 0; + push_constant.filter_axis = 0; + push_constant.max_gi_probes = env->volumetric_fog_gi_inject > 0.001 ? p_gi_probe_count : 0; + + /* Vector2 dssize = directional_shadow_get_size(); + push_constant.directional_shadow_pixel_size[0] = 1.0 / dssize.x; + push_constant.directional_shadow_pixel_size[1] = 1.0 / dssize.y; +*/ + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + bool use_filter = volumetric_fog_filter_active; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.pipelines[using_sdfgi ? VOLUMETRIC_FOG_SHADER_DENSITY_WITH_SDFGI : VOLUMETRIC_FOG_SHADER_DENSITY]); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set, 0); + if (using_sdfgi) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->sdfgi_uniform_set, 1); + } + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, rb->volumetric_fog->depth, 4, 4, 4); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (use_filter) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.pipelines[VOLUMETRIC_FOG_SHADER_FILTER]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set, 0); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, rb->volumetric_fog->depth, 8, 8, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + push_constant.filter_axis = 1; + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set2, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, rb->volumetric_fog->depth, 8, 8, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.pipelines[VOLUMETRIC_FOG_SHADER_FOG]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, 1, 8, 8, 1); + + RD::get_singleton()->compute_list_end(); +} + void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { Color clear_color; if (p_render_buffers.is_valid()) { @@ -6190,10 +6870,25 @@ void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_ca } uint32_t directional_light_count = 0; - _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows, directional_light_count); + uint32_t positional_light_count = 0; + _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows, directional_light_count, positional_light_count); _setup_decals(p_decal_cull_result, p_decal_cull_count, p_cam_transform.affine_inverse()); cluster.builder.bake_cluster(); //bake to cluster + uint32_t gi_probe_count = 0; + _setup_giprobes(p_render_buffers, p_cam_transform, p_gi_probe_cull_result, p_gi_probe_cull_count, gi_probe_count); + + if (p_render_buffers.is_valid()) { + bool directional_shadows = false; + for (uint32_t i = 0; i < directional_light_count; i++) { + if (cluster.directional_lights[i].shadow_enabled) { + directional_shadows = true; + break; + } + } + _update_volumetric_fog(p_render_buffers, p_environment, p_cam_projection, p_cam_transform, p_shadow_atlas, directional_light_count, directional_shadows, positional_light_count, gi_probe_count); + } + _render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, directional_light_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_lightmap_cull_result, p_lightmap_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color); if (p_render_buffers.is_valid()) { @@ -6481,6 +7176,7 @@ void RasterizerSceneRD::render_sdfgi(RID p_render_buffers, int p_region, Instanc ipush_constant.sky_color[1] = 0; ipush_constant.sky_color[2] = 0; ipush_constant.y_mult = rb->sdfgi->y_mult; + ipush_constant.store_ambient_texture = false; ipush_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; ipush_constant.image_size[1] = rb->sdfgi->probe_axis_count; @@ -6836,6 +7532,9 @@ bool RasterizerSceneRD::free(RID p_rid) { if (rb->sdfgi) { _sdfgi_erase(rb); } + if (rb->volumetric_fog) { + _volumetric_fog_erase(rb); + } render_buffers_owner.free(p_rid); } else if (environment_owner.owns(p_rid)) { //not much to delete, just free it @@ -7406,6 +8105,8 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { cluster.lights = memnew_arr(Cluster::LightData, cluster.max_lights); cluster.light_buffer = RD::get_singleton()->storage_buffer_create(light_buffer_size); //defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(cluster.max_lights) + "\n"; + cluster.lights_instances = memnew_arr(RID, cluster.max_lights); + cluster.lights_shadow_rect_cache = memnew_arr(Rect2i, cluster.max_lights); cluster.max_directional_lights = 8; uint32_t directional_light_buffer_size = cluster.max_directional_lights * sizeof(Cluster::DirectionalLightData); @@ -7422,8 +8123,30 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { cluster.builder.setup(16, 8, 24); + { + String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(cluster.max_directional_lights) + "\n"; + Vector<String> volumetric_fog_modes; + volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n"); + volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n#define ENABLE_SDFGI\n"); + volumetric_fog_modes.push_back("\n#define MODE_FILTER\n"); + volumetric_fog_modes.push_back("\n#define MODE_FOG\n"); + volumetric_fog.shader.initialize(volumetric_fog_modes, defines); + volumetric_fog.shader_version = volumetric_fog.shader.version_create(); + for (int i = 0; i < VOLUMETRIC_FOG_SHADER_MAX; i++) { + volumetric_fog.pipelines[i] = RD::get_singleton()->compute_pipeline_create(volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, i)); + } + } default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES); + { + RD::SamplerState sampler; + sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.enable_compare = true; + sampler.compare_op = RD::COMPARE_OP_LESS; + shadow_sampler = RD::get_singleton()->sampler_create(sampler); + } + camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_shape")))); camera_effects_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter")); environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); @@ -7441,6 +8164,11 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { soft_shadow_kernel = memnew_arr(float, 128); shadows_quality_set(RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/shadows/soft_shadow_quality")))); directional_shadow_quality_set(RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/directional_shadow/soft_shadow_quality")))); + + environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/volumetric_fog/volume_size"), GLOBAL_GET("rendering/volumetric_fog/volume_depth")); + environment_set_volumetric_fog_filter_active(GLOBAL_GET("rendering/volumetric_fog/use_filter")); + environment_set_volumetric_fog_directional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/directional_shadow_shrink")); + environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink")); } RasterizerSceneRD::~RasterizerSceneRD() { @@ -7491,7 +8219,13 @@ RasterizerSceneRD::~RasterizerSceneRD() { RD::get_singleton()->free(cluster.decal_buffer); memdelete_arr(cluster.directional_lights); memdelete_arr(cluster.lights); + memdelete_arr(cluster.lights_shadow_rect_cache); + memdelete_arr(cluster.lights_instances); memdelete_arr(cluster.reflections); memdelete_arr(cluster.decals); } + + RD::get_singleton()->free(shadow_sampler); + + directional_shadow_atlas_set_size(0); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index 27eec44ec3..4c48960587 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -45,6 +45,7 @@ #include "servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/sky.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl.gen.h" #include "servers/rendering/rendering_device.h" class RasterizerSceneRD : public RasterizerScene { @@ -78,9 +79,10 @@ protected: }; virtual RenderBufferData *_create_render_buffer_data() = 0; - void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count); + void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count); void _setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform); void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment); + void _setup_giprobes(RID p_render_buffers, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, uint32_t &r_gi_probes_used); virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, int p_directional_light_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0; virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip, bool p_use_pancake) = 0; @@ -490,6 +492,12 @@ private: /* SHADOW ATLAS */ + struct ShadowShrinkStage { + RID texture; + RID filter_texture; + uint32_t size; + }; + struct ShadowAtlas { enum { QUADRANT_SHIFT = 27, @@ -503,10 +511,12 @@ private: struct Shadow { RID owner; uint64_t version; + uint64_t fog_version; // used for fog uint64_t alloc_tick; Shadow() { version = 0; + fog_version = 0; alloc_tick = 0; } }; @@ -528,6 +538,8 @@ private: RID fb; //for copying Map<RID, uint32_t> shadow_owners; + + Vector<ShadowShrinkStage> shrink_stages; }; RID_Owner<ShadowAtlas> shadow_atlas_owner; @@ -556,8 +568,14 @@ private: int light_count = 0; int size = 0; int current_light = 0; + + Vector<ShadowShrinkStage> shrink_stages; + } directional_shadow; + void _allocate_shadow_shrink_stages(RID p_base, int p_base_size, Vector<ShadowShrinkStage> &shrink_stages, uint32_t p_target_size); + void _clear_shadow_shrink_stages(Vector<ShadowShrinkStage> &shrink_stages); + /* SHADOW CUBEMAPS */ struct ShadowCubemap { @@ -656,6 +674,26 @@ private: float auto_exp_scale = 0.5; uint64_t auto_exposure_version = 0; + // Fog + bool fog_enabled = false; + Color fog_light_color = Color(0.5, 0.6, 0.7); + float fog_light_energy = 1.0; + float fog_sun_scatter = 0.0; + float fog_density = 0.001; + float fog_height = 0.0; + float fog_height_density = 0.0; //can be negative to invert effect + + /// Volumetric Fog + /// + bool volumetric_fog_enabled = false; + float volumetric_fog_density = 0.01; + Color volumetric_fog_light = Color(0, 0, 0); + float volumetric_fog_light_energy = 0.0; + float volumetric_fog_length = 64.0; + float volumetric_fog_detail_spread = 2.0; + RS::EnvVolumetricFogShadowFilter volumetric_fog_shadow_filter = RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_LOW; + float volumetric_fog_gi_inject = 0.0; + /// Glow bool glow_enabled = false; @@ -739,6 +777,7 @@ private: /* RENDER BUFFERS */ struct SDFGI; + struct VolumetricFog; struct RenderBuffers { enum { @@ -759,6 +798,7 @@ private: RID gi_uniform_set; SDFGI *sdfgi = nullptr; + VolumetricFog *volumetric_fog = nullptr; //built-in textures used for ping pong image processing and blurring struct Blur { @@ -885,6 +925,7 @@ private: RID lightprobe_data; RID occlusion_texture; RID occlusion_data; + RID ambient_texture; //integrates with volumetric fog RID lightprobe_history_scroll; //used for scrolling lightprobes RID lightprobe_average_scroll; //used for scrolling lightprobes @@ -1077,6 +1118,9 @@ private: float sky_color[3]; float y_mult; + + uint32_t store_ambient_texture; + uint32_t pad[3]; }; SdfgiIntegrateShaderRD integrate; @@ -1141,7 +1185,7 @@ private: float anisotropy_strength; float ao; float ao_size; - uint32_t pad[1]; + uint32_t mipmaps; }; struct PushConstant { @@ -1219,7 +1263,8 @@ private: float soft_shadow_size; float soft_shadow_scale; uint32_t mask; - uint32_t pad[2]; + float shadow_volumetric_fog_fade; + uint32_t pad; float projector_rect[4]; }; @@ -1236,10 +1281,12 @@ private: uint32_t shadow_enabled; float fade_from; float fade_to; + uint32_t pad[3]; + float shadow_volumetric_fog_fade; float shadow_bias[4]; float shadow_normal_bias[4]; float shadow_transmittance_bias[4]; - float shadow_transmittance_z_scale[4]; + float shadow_z_range[4]; float shadow_range_begin[4]; float shadow_split_offsets[4]; float shadow_matrices[4][16]; @@ -1283,6 +1330,9 @@ private: LightData *lights; uint32_t max_lights; RID light_buffer; + RID *lights_instances; + Rect2i *lights_shadow_rect_cache; + uint32_t lights_shadow_rect_cache_count = 0; DirectionalLightData *directional_lights; uint32_t max_directional_lights; @@ -1292,6 +1342,73 @@ private: } cluster; + struct VolumetricFog { + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 0; + + float length; + float spread; + + RID light_density_map; + RID fog_map; + RID uniform_set; + RID uniform_set2; + RID sdfgi_uniform_set; + + int last_shadow_filter = -1; + }; + + enum { + VOLUMETRIC_FOG_SHADER_DENSITY, + VOLUMETRIC_FOG_SHADER_DENSITY_WITH_SDFGI, + VOLUMETRIC_FOG_SHADER_FILTER, + VOLUMETRIC_FOG_SHADER_FOG, + VOLUMETRIC_FOG_SHADER_MAX, + }; + + struct VolumetricFogShader { + struct PushConstant { + float fog_frustum_size_begin[2]; + float fog_frustum_size_end[2]; + + float fog_frustum_end; + float z_near; + float z_far; + uint32_t filter_axis; + + int32_t fog_volume_size[3]; + uint32_t directional_light_count; + + float light_energy[3]; + float base_density; + + float detail_spread; + float gi_inject; + uint32_t max_gi_probes; + uint32_t pad; + + float cam_rotation[12]; + }; + + VolumetricFogShaderRD shader; + + RID shader_version; + RID pipelines[VOLUMETRIC_FOG_SHADER_MAX]; + + } volumetric_fog; + + uint32_t volumetric_fog_depth = 128; + uint32_t volumetric_fog_size = 128; + bool volumetric_fog_filter_active = false; + uint32_t volumetric_fog_directional_shadow_shrink = 512; + uint32_t volumetric_fog_positional_shadow_shrink = 512; + + void _volumetric_fog_erase(RenderBuffers *rb); + void _update_volumetric_fog(RID p_render_buffers, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_gi_probe_count); + + RID shadow_sampler; + uint64_t scene_pass = 0; uint64_t shadow_atlas_realloc_tolerance_msec = 500; @@ -1390,7 +1507,21 @@ public: void environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap); void environment_glow_set_use_bicubic_upscale(bool p_enable); - void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {} + void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density); + bool environment_is_fog_enabled(RID p_env) const; + Color environment_get_fog_light_color(RID p_env) const; + float environment_get_fog_light_energy(RID p_env) const; + float environment_get_fog_sun_scatter(RID p_env) const; + float environment_get_fog_density(RID p_env) const; + float environment_get_fog_height(RID p_env) const; + float environment_get_fog_height_density(RID p_env) const; + + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter); + + virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth); + virtual void environment_set_volumetric_fog_filter_active(bool p_enable); + virtual void environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size); + virtual void environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size); void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance); void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_bias, float p_light_affect, float p_ao_channel_affect, RS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness); @@ -1411,10 +1542,6 @@ public: void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) {} - void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) {} - void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {} - void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {} - virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size); virtual RID camera_effects_create(); @@ -1708,6 +1835,11 @@ public: float render_buffers_get_sdfgi_energy(RID p_render_buffers) const; RID render_buffers_get_sdfgi_occlusion_texture(RID p_render_buffers) const; + bool render_buffers_has_volumetric_fog(RID p_render_buffers) const; + RID render_buffers_get_volumetric_fog_texture(RID p_render_buffers); + float render_buffers_get_volumetric_fog_end(RID p_render_buffers); + float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); + void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass); void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index 102e0e2eed..f3ba57e733 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -3290,6 +3290,7 @@ RID RasterizerStorageRD::light_create(RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; + light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 1.0; return light_owner.make_rid(light); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index b7aedf8717..6e5923953b 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -1184,6 +1184,13 @@ public: return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } + _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const { + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0.0); + + return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE]; + } + RS::LightBakeMode light_get_bake_mode(RID p_light); uint32_t light_get_max_sdfgi_cascade(RID p_light); uint64_t light_get_version(RID p_light) const; diff --git a/servers/rendering/rasterizer_rd/shaders/SCsub b/servers/rendering/rasterizer_rd/shaders/SCsub index 67f4edc626..3aa863be98 100644 --- a/servers/rendering/rasterizer_rd/shaders/SCsub +++ b/servers/rendering/rasterizer_rd/shaders/SCsub @@ -35,3 +35,5 @@ if "RD_GLSL" in env["BUILDERS"]: env.RD_GLSL("sdfgi_direct_light.glsl") env.RD_GLSL("sdfgi_debug.glsl") env.RD_GLSL("sdfgi_debug_probes.glsl") + env.RD_GLSL("volumetric_fog.glsl") + env.RD_GLSL("shadow_reduce.glsl") diff --git a/servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl b/servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl new file mode 100644 index 0000000000..e723468dd8 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl @@ -0,0 +1,95 @@ + +#define CLUSTER_COUNTER_SHIFT 20 +#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1) +#define CLUSTER_COUNTER_MASK 0xfff + +struct LightData { //this structure needs to be as packed as possible + vec3 position; + float inv_radius; + vec3 direction; + float size; + uint attenuation_energy; //attenuation + uint color_specular; //rgb color, a specular (8 bit unorm) + uint cone_attenuation_angle; // attenuation and angle, (16bit float) + uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm) + vec4 atlas_rect; // rect in the shadow atlas + mat4 shadow_matrix; + float shadow_bias; + float shadow_normal_bias; + float transmittance_bias; + float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle + float soft_shadow_scale; // scales the shadow kernel for blurrier shadows + uint mask; + float shadow_volumetric_fog_fade; + uint pad; + vec4 projector_rect; //projector rect in srgb decal atlas +}; + +#define REFLECTION_AMBIENT_DISABLED 0 +#define REFLECTION_AMBIENT_ENVIRONMENT 1 +#define REFLECTION_AMBIENT_COLOR 2 + +struct ReflectionData { + vec3 box_extents; + float index; + vec3 box_offset; + uint mask; + vec4 params; // intensity, 0, interior , boxproject + vec3 ambient; // ambient color + uint ambient_mode; + mat4 local_matrix; // up to here for spot and omni, rest is for directional + // notes: for ambientblend, use distance to edge to blend between already existing global environment +}; + +struct DirectionalLightData { + vec3 direction; + float energy; + vec3 color; + float size; + float specular; + uint mask; + float softshadow_angle; + float soft_shadow_scale; + bool blend_splits; + bool shadow_enabled; + float fade_from; + float fade_to; + uvec3 pad; + float shadow_volumetric_fog_fade; + vec4 shadow_bias; + vec4 shadow_normal_bias; + vec4 shadow_transmittance_bias; + vec4 shadow_z_range; + vec4 shadow_range_begin; + vec4 shadow_split_offsets; + mat4 shadow_matrix1; + mat4 shadow_matrix2; + mat4 shadow_matrix3; + mat4 shadow_matrix4; + vec4 shadow_color1; + vec4 shadow_color2; + vec4 shadow_color3; + vec4 shadow_color4; + vec2 uv_scale1; + vec2 uv_scale2; + vec2 uv_scale3; + vec2 uv_scale4; +}; + +struct DecalData { + mat4 xform; //to decal transform + vec3 inv_extents; + float albedo_mix; + vec4 albedo_rect; + vec4 normal_rect; + vec4 orm_rect; + vec4 emission_rect; + vec4 modulate; + float emission_energy; + uint mask; + float upper_fade; + float lower_fade; + mat3x4 normal_xform; + vec3 normal; + float normal_fade; +}; diff --git a/servers/rendering/rasterizer_rd/shaders/gi.glsl b/servers/rendering/rasterizer_rd/shaders/gi.glsl index a1939f75ad..8011dadc72 100644 --- a/servers/rendering/rasterizer_rd/shaders/gi.glsl +++ b/servers/rendering/rasterizer_rd/shaders/gi.glsl @@ -80,7 +80,7 @@ struct GIProbeData { float anisotropy_strength; float ambient_occlusion; float ambient_occlusion_size; - uint pad2; + uint mipmaps; }; layout(set = 0, binding = 16, std140) uniform GIProbes { diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl index 792a1aa05f..5993e68317 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl @@ -1237,7 +1237,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r; //reconstruct depth - shadow_z / lights.data[idx].inv_radius; + shadow_z /= lights.data[idx].inv_radius; //distance to light plane float z = dot(spot_dir, -light_rel_vec); transmittance_z = z - shadow_z; @@ -1601,6 +1601,51 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef MODE_RENDER_DEPTH + +vec4 volumetric_fog_process(vec2 screen_uv, float z) { + vec3 fog_pos = vec3(screen_uv, z * scene_data.volumetric_fog_inv_length); + if (fog_pos.z < 0.0) { + return vec4(0.0); + } else if (fog_pos.z < 1.0) { + fog_pos.z = pow(fog_pos.z, scene_data.volumetric_fog_detail_spread); + } + + return texture(sampler3D(volumetric_fog_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), fog_pos); +} + +vec4 fog_process(vec3 vertex) { + vec3 fog_color = scene_data.fog_light_color; + + if (scene_data.fog_sun_scatter > 0.001) { + vec4 sun_scatter = vec4(0.0); + float sun_total = 0.0; + vec3 view = normalize(vertex); + + for (uint i = 0; i < scene_data.directional_light_count; i++) { + vec3 light_color = directional_lights.data[i].color * directional_lights.data[i].energy; + float light_amount = pow(max(dot(view, directional_lights.data[i].direction), 0.0), 8.0); + fog_color += light_color * light_amount * scene_data.fog_sun_scatter; + } + } + + float fog_amount = 1.0 - exp(vertex.z * scene_data.fog_density); + + if (abs(scene_data.fog_height_density) > 0.001) { + float y = (scene_data.camera_matrix * vec4(vertex, 1.0)).y; + + float y_dist = scene_data.fog_height - y; + + float vfog_amount = clamp(exp(y_dist * scene_data.fog_height_density), 0.0, 1.0); + + fog_amount = max(vfog_amount, fog_amount); + } + + return vec4(fog_color, fog_amount); +} + +#endif + void main() { #ifdef MODE_DUAL_PARABOLOID @@ -2187,8 +2232,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.x; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.x; + shadow_z *= directional_lights.data[i].shadow_z_range.x; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.x; transmittance_z = z - shadow_z; } @@ -2219,8 +2264,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.y; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.y; + shadow_z *= directional_lights.data[i].shadow_z_range.y; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.y; transmittance_z = z - shadow_z; } @@ -2251,8 +2296,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.z; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.z; + shadow_z *= directional_lights.data[i].shadow_z_range.z; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.z; transmittance_z = z - shadow_z; } @@ -2285,8 +2330,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.w; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.w; + shadow_z *= directional_lights.data[i].shadow_z_range.w; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.w; transmittance_z = z - shadow_z; } @@ -2662,8 +2707,6 @@ FRAGMENT_SHADER_CODE diffuse_light *= 1.0 - metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point ambient_light *= 1.0 - metallic; - //fog - #ifdef MODE_MULTIPLE_RENDER_TARGETS #ifdef MODE_UNSHADED @@ -2679,16 +2722,37 @@ FRAGMENT_SHADER_CODE specular_buffer = vec4(specular_light, metallic); #endif + if (scene_data.volumetric_fog_enabled) { + vec4 fog = volumetric_fog_process(screen_uv, -vertex.z); + diffuse_buffer.rgb = mix(diffuse_buffer.rgb, fog.rgb, fog.a); + specular_buffer.rgb = mix(specular_buffer.rgb, vec3(0.0), fog.a); + } + + if (scene_data.fog_enabled) { + vec4 fog = fog_process(vertex); + diffuse_buffer.rgb = mix(diffuse_buffer.rgb, fog.rgb, fog.a); + specular_buffer.rgb = mix(specular_buffer.rgb, vec3(0.0), fog.a); + } + #else //MODE_MULTIPLE_RENDER_TARGETS #ifdef MODE_UNSHADED frag_color = vec4(albedo, alpha); #else frag_color = vec4(emission + ambient_light + diffuse_light + specular_light, alpha); - //frag_color = vec4(1.0);;; - + //frag_color = vec4(1.0); #endif //USE_NO_SHADING + if (scene_data.volumetric_fog_enabled) { + vec4 fog = volumetric_fog_process(screen_uv, -vertex.z); + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); + } + + if (scene_data.fog_enabled) { + vec4 fog = fog_process(vertex); + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); + } + #endif //MODE_MULTIPLE_RENDER_TARGETS #endif //MODE_RENDER_DEPTH diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl index c4dc7bd675..66bfefbe89 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl @@ -3,6 +3,8 @@ #define MAX_GI_PROBES 8 +#include "cluster_data_inc.glsl" + layout(push_constant, binding = 0, std430) uniform DrawCall { uint instance_index; uint pad; //16 bits minimum size @@ -94,40 +96,18 @@ layout(set = 0, binding = 3, std140) uniform SceneData { ivec3 sdf_size; bool gi_upscale_for_msaa; -#if 0 - vec4 ambient_light_color; - vec4 bg_color; - - vec4 fog_color_enabled; - vec4 fog_sun_color_amount; - - float ambient_energy; - float bg_energy; -#endif - -#if 0 - vec2 shadow_atlas_pixel_size; - vec2 directional_shadow_pixel_size; + bool volumetric_fog_enabled; + float volumetric_fog_inv_length; + float volumetric_fog_detail_spread; + uint volumetric_fog_pad; - float z_far; - - float subsurface_scatter_width; - float ambient_occlusion_affect_light; - float ambient_occlusion_affect_ao_channel; - float opaque_prepass_threshold; - - bool fog_depth_enabled; - float fog_depth_begin; - float fog_depth_end; + bool fog_enabled; float fog_density; - float fog_depth_curve; - bool fog_transmit_enabled; - float fog_transmit_curve; - bool fog_height_enabled; - float fog_height_min; - float fog_height_max; - float fog_height_curve; -#endif + float fog_height; + float fog_height_density; + + vec3 fog_light_color; + float fog_sun_scatter; } scene_data; @@ -163,86 +143,16 @@ layout(set = 0, binding = 4, std430) restrict readonly buffer Instances { } instances; -struct LightData { //this structure needs to be as packed as possible - vec3 position; - float inv_radius; - vec3 direction; - float size; - uint attenuation_energy; //attenuation - uint color_specular; //rgb color, a specular (8 bit unorm) - uint cone_attenuation_angle; // attenuation and angle, (16bit float) - uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm) - vec4 atlas_rect; // rect in the shadow atlas - mat4 shadow_matrix; - float shadow_bias; - float shadow_normal_bias; - float transmittance_bias; - float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle - float soft_shadow_scale; // scales the shadow kernel for blurrier shadows - uint mask; - uint pad[2]; - vec4 projector_rect; //projector rect in srgb decal atlas -}; - layout(set = 0, binding = 5, std430) restrict readonly buffer Lights { LightData data[]; } lights; -#define REFLECTION_AMBIENT_DISABLED 0 -#define REFLECTION_AMBIENT_ENVIRONMENT 1 -#define REFLECTION_AMBIENT_COLOR 2 - -struct ReflectionData { - vec3 box_extents; - float index; - vec3 box_offset; - uint mask; - vec4 params; // intensity, 0, interior , boxproject - vec3 ambient; // ambient color - uint ambient_mode; - mat4 local_matrix; // up to here for spot and omni, rest is for directional - // notes: for ambientblend, use distance to edge to blend between already existing global environment -}; - layout(set = 0, binding = 6) buffer restrict readonly ReflectionProbeData { ReflectionData data[]; } reflections; -struct DirectionalLightData { - vec3 direction; - float energy; - vec3 color; - float size; - float specular; - uint mask; - float softshadow_angle; - float soft_shadow_scale; - bool blend_splits; - bool shadow_enabled; - float fade_from; - float fade_to; - vec4 shadow_bias; - vec4 shadow_normal_bias; - vec4 shadow_transmittance_bias; - vec4 shadow_transmittance_z_scale; - vec4 shadow_range_begin; - vec4 shadow_split_offsets; - mat4 shadow_matrix1; - mat4 shadow_matrix2; - mat4 shadow_matrix3; - mat4 shadow_matrix4; - vec4 shadow_color1; - vec4 shadow_color2; - vec4 shadow_color3; - vec4 shadow_color4; - vec2 uv_scale1; - vec2 uv_scale2; - vec2 uv_scale3; - vec2 uv_scale4; -}; - layout(set = 0, binding = 7, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } @@ -271,31 +181,9 @@ layout(set = 0, binding = 12, std140) restrict readonly buffer LightmapCaptures } lightmap_captures; -#define CLUSTER_COUNTER_SHIFT 20 -#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1) -#define CLUSTER_COUNTER_MASK 0xfff - layout(set = 0, binding = 13) uniform texture2D decal_atlas; layout(set = 0, binding = 14) uniform texture2D decal_atlas_srgb; -struct DecalData { - mat4 xform; //to decal transform - vec3 inv_extents; - float albedo_mix; - vec4 albedo_rect; - vec4 normal_rect; - vec4 orm_rect; - vec4 emission_rect; - vec4 modulate; - float emission_energy; - uint mask; - float upper_fade; - float lower_fade; - mat3x4 normal_xform; - vec3 normal; - float normal_fade; -}; - layout(set = 0, binding = 15, std430) restrict readonly buffer Decals { DecalData data[]; } @@ -394,9 +282,7 @@ layout(set = 3, binding = 2) uniform texture2D normal_roughness_buffer; layout(set = 3, binding = 4) uniform texture2D ao_buffer; layout(set = 3, binding = 5) uniform texture2D ambient_buffer; layout(set = 3, binding = 6) uniform texture2D reflection_buffer; - layout(set = 3, binding = 7) uniform texture2DArray sdfgi_lightprobe_texture; - layout(set = 3, binding = 8) uniform texture3D sdfgi_occlusion_cascades; struct GIProbeData { @@ -412,7 +298,7 @@ struct GIProbeData { float anisotropy_strength; float ambient_occlusion; float ambient_occlusion_size; - uint pad2; + uint mipmaps; }; layout(set = 3, binding = 9, std140) uniform GIProbes { @@ -420,6 +306,8 @@ layout(set = 3, binding = 9, std140) uniform GIProbes { } gi_probes; +layout(set = 3, binding = 10) uniform texture3D volumetric_fog_texture; + #endif /* Set 4 Skeleton & Instancing (Multimesh) */ diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl index e4779aafaf..1ec471d204 100644 --- a/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl +++ b/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl @@ -37,6 +37,8 @@ layout(rgba32i, set = 0, binding = 12) uniform restrict iimage2D lightprobe_aver layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_average_parent_texture; +layout(rgba16f, set = 0, binding = 14) uniform restrict writeonly image2DArray lightprobe_ambient_texture; + layout(set = 1, binding = 0) uniform textureCube sky_irradiance; layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps; @@ -68,6 +70,9 @@ layout(push_constant, binding = 0, std430) uniform Params { vec3 sky_color; float y_mult; + + bool store_ambient_texture; + uint pad[3]; } params; @@ -319,6 +324,13 @@ void main() { imageStore(lightprobe_history_texture, prev_pos, ivalue); imageStore(lightprobe_average_texture, average_pos, average); + + if (params.store_ambient_texture && i == 0) { + ivec3 ambient_pos = ivec3(pos, int(params.cascade)); + vec4 ambient_light = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS); + ambient_light *= 0.88622; // SHL0 + imageStore(lightprobe_ambient_texture, ambient_pos, ambient_light); + } } #endif // MODE PROCESS diff --git a/servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl b/servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl new file mode 100644 index 0000000000..29443ae7db --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl @@ -0,0 +1,105 @@ +#[compute] + +#version 450 + +VERSION_DEFINES + +#define BLOCK_SIZE 8 + +layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in; + +#ifdef MODE_REDUCE + +shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE]; +const uint swizzle_table[BLOCK_SIZE] = uint[](0, 4, 2, 6, 1, 5, 3, 7); +const uint unswizzle_table[BLOCK_SIZE] = uint[](0, 0, 0, 1, 0, 2, 1, 3); + +#endif + +layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_depth; +layout(r32f, set = 0, binding = 1) uniform restrict writeonly image2D dst_depth; + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 source_size; + ivec2 source_offset; + uint min_size; + uint gaussian_kernel_version; + ivec2 filter_dir; +} +params; + +void main() { +#ifdef MODE_REDUCE + + uvec2 pos = gl_LocalInvocationID.xy; + + ivec2 image_offset = params.source_offset; + ivec2 image_pos = image_offset + ivec2(gl_GlobalInvocationID.xy); + uint dst_t = swizzle_table[pos.y] * BLOCK_SIZE + swizzle_table[pos.x]; + tmp_data[dst_t] = imageLoad(source_depth, min(image_pos, params.source_size - ivec2(1))).r; + ivec2 image_size = params.source_size; + + uint t = pos.y * BLOCK_SIZE + pos.x; + + //neighbours + uint size = BLOCK_SIZE; + + do { + groupMemoryBarrier(); + barrier(); + + size >>= 1; + image_size >>= 1; + image_offset >>= 1; + + if (all(lessThan(pos, uvec2(size)))) { + uint nx = t + size; + uint ny = t + (BLOCK_SIZE * size); + uint nxy = ny + size; + + tmp_data[t] += tmp_data[nx]; + tmp_data[t] += tmp_data[ny]; + tmp_data[t] += tmp_data[nxy]; + tmp_data[t] /= 4.0; + } + + } while (size > params.min_size); + + if (all(lessThan(pos, uvec2(size)))) { + image_pos = ivec2(unswizzle_table[size + pos.x], unswizzle_table[size + pos.y]); + image_pos += image_offset + ivec2(gl_WorkGroupID.xy) * int(size); + + image_size = max(ivec2(1), image_size); //in case image size became 0 + + if (all(lessThan(image_pos, uvec2(image_size)))) { + imageStore(dst_depth, image_pos, vec4(tmp_data[t])); + } + } +#endif + +#ifdef MODE_FILTER + + ivec2 image_pos = params.source_offset + ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(image_pos, params.source_size))) { + return; + } + + ivec2 clamp_min = ivec2(params.source_offset); + ivec2 clamp_max = ivec2(params.source_size) - 1; + + //gaussian kernel, size 9, sigma 4 + const int kernel_size = 9; + const float gaussian_kernel[kernel_size * 3] = float[]( + 0.000229, 0.005977, 0.060598, 0.241732, 0.382928, 0.241732, 0.060598, 0.005977, 0.000229, + 0.028532, 0.067234, 0.124009, 0.179044, 0.20236, 0.179044, 0.124009, 0.067234, 0.028532, + 0.081812, 0.101701, 0.118804, 0.130417, 0.134535, 0.130417, 0.118804, 0.101701, 0.081812); + float accum = 0.0; + for (int i = 0; i < kernel_size; i++) { + ivec2 ofs = clamp(image_pos + params.filter_dir * (i - kernel_size / 2), clamp_min, clamp_max); + accum += imageLoad(source_depth, ofs).r * gaussian_kernel[params.gaussian_kernel_version + i]; + } + + imageStore(dst_depth, image_pos, vec4(accum)); + +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl b/servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl new file mode 100644 index 0000000000..cb19fb0b69 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl @@ -0,0 +1,530 @@ +#[compute] + +#version 450 + +VERSION_DEFINES + +#if defined(MODE_FOG) || defined(MODE_FILTER) + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#endif + +#if defined(MODE_DENSITY) + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#endif + +#include "cluster_data_inc.glsl" + +#define M_PI 3.14159265359 + +layout(set = 0, binding = 1) uniform texture2D shadow_atlas; +layout(set = 0, binding = 2) uniform texture2D directional_shadow_atlas; + +layout(set = 0, binding = 3, std430) restrict readonly buffer Lights { + LightData data[]; +} +lights; + +layout(set = 0, binding = 4, std140) uniform DirectionalLights { + DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +} +directional_lights; + +layout(set = 0, binding = 5) uniform utexture3D cluster_texture; + +layout(set = 0, binding = 6, std430) restrict readonly buffer ClusterData { + uint indices[]; +} +cluster_data; + +layout(set = 0, binding = 7) uniform sampler linear_sampler; + +#ifdef MODE_DENSITY +layout(rgba16f, set = 0, binding = 8) uniform restrict writeonly image3D density_map; +layout(rgba16f, set = 0, binding = 9) uniform restrict readonly image3D fog_map; //unused +#endif + +#ifdef MODE_FOG +layout(rgba16f, set = 0, binding = 8) uniform restrict readonly image3D density_map; +layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image3D fog_map; +#endif + +#ifdef MODE_FILTER +layout(rgba16f, set = 0, binding = 8) uniform restrict readonly image3D source_map; +layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image3D dest_map; +#endif + +layout(set = 0, binding = 10) uniform sampler shadow_sampler; + +#define MAX_GI_PROBES 8 + +struct GIProbeData { + mat4 xform; + vec3 bounds; + float dynamic_range; + + float bias; + float normal_bias; + bool blend_ambient; + uint texture_slot; + + float anisotropy_strength; + float ambient_occlusion; + float ambient_occlusion_size; + uint mipmaps; +}; + +layout(set = 0, binding = 11, std140) uniform GIProbes { + GIProbeData data[MAX_GI_PROBES]; +} +gi_probes; + +layout(set = 0, binding = 12) uniform texture3D gi_probe_textures[MAX_GI_PROBES]; + +layout(set = 0, binding = 13) uniform sampler linear_sampler_with_mipmaps; + +#ifdef ENABLE_SDFGI + +// SDFGI Integration on set 1 +#define SDFGI_MAX_CASCADES 8 + +struct SDFGIProbeCascadeData { + vec3 position; + float to_probe; + ivec3 probe_world_offset; + float to_cell; // 1/bounds * grid_size +}; + +layout(set = 1, binding = 0, std140) uniform SDFGI { + vec3 grid_size; + uint max_cascades; + + bool use_occlusion; + int probe_axis_size; + float probe_to_uvw; + float normal_bias; + + vec3 lightprobe_tex_pixel_size; + float energy; + + vec3 lightprobe_uv_offset; + float y_mult; + + vec3 occlusion_clamp; + uint pad3; + + vec3 occlusion_renormalize; + uint pad4; + + vec3 cascade_probe_size; + uint pad5; + + SDFGIProbeCascadeData cascades[SDFGI_MAX_CASCADES]; +} +sdfgi; + +layout(set = 1, binding = 1) uniform texture2DArray sdfgi_ambient_texture; + +layout(set = 1, binding = 2) uniform texture3D sdfgi_occlusion_texture; + +#endif //SDFGI + +layout(push_constant, binding = 0, std430) uniform Params { + vec2 fog_frustum_size_begin; + vec2 fog_frustum_size_end; + + float fog_frustum_end; + float z_near; + float z_far; + int filter_axis; + + ivec3 fog_volume_size; + uint directional_light_count; + + vec3 light_color; + float base_density; + + float detail_spread; + float gi_inject; + uint max_gi_probes; + uint pad; + + mat3x4 cam_rotation; +} +params; + +float get_depth_at_pos(float cell_depth_size, int z) { + float d = float(z) * cell_depth_size + cell_depth_size * 0.5; //center of voxels + d = pow(d, params.detail_spread); + return params.fog_frustum_end * d; +} + +vec3 hash3f(uvec3 x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return vec3(x & 0xFFFFF) / vec3(float(0xFFFFF)); +} + +void main() { + vec3 fog_cell_size = 1.0 / vec3(params.fog_volume_size); + +#ifdef MODE_DENSITY + + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + if (any(greaterThanEqual(pos, params.fog_volume_size))) { + return; //do not compute + } + + vec3 posf = vec3(pos); + + //posf += mix(vec3(0.0),vec3(1.0),0.3) * hash3f(uvec3(pos)) * 2.0 - 1.0; + + vec3 fog_unit_pos = posf * fog_cell_size + fog_cell_size * 0.5; //center of voxels + fog_unit_pos.z = pow(fog_unit_pos.z, params.detail_spread); + + vec3 view_pos; + view_pos.xy = (fog_unit_pos.xy * 2.0 - 1.0) * mix(params.fog_frustum_size_begin, params.fog_frustum_size_end, vec2(fog_unit_pos.z)); + view_pos.z = -params.fog_frustum_end * fog_unit_pos.z; + view_pos.y = -view_pos.y; + + vec3 total_light = params.light_color; + + float total_density = params.base_density; + float cell_depth_size = abs(view_pos.z - get_depth_at_pos(fog_cell_size.z, pos.z + 1)); + //compute directional lights + + for (uint i = 0; i < params.directional_light_count; i++) { + vec3 shadow_attenuation = vec3(1.0); + + if (directional_lights.data[i].shadow_enabled) { + float depth_z = -view_pos.z; + + vec4 pssm_coord; + vec3 shadow_color = directional_lights.data[i].shadow_color1.rgb; + vec3 light_dir = directional_lights.data[i].direction; + vec4 v = vec4(view_pos, 1.0); + float z_range; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.x; + + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.y; + + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.z; + + } else { + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.w; + } + + float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r; + float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * directional_lights.data[i].shadow_volumetric_fog_fade); + + /* + //float shadow = textureProj(sampler2DShadow(directional_shadow_atlas,shadow_sampler),pssm_coord); + float shadow = 0.0; + for(float xi=-1;xi<=1;xi++) { + for(float yi=-1;yi<=1;yi++) { + vec2 ofs = vec2(xi,yi) * 1.5 * params.directional_shadow_pixel_size; + shadow += textureProj(sampler2DShadow(directional_shadow_atlas,shadow_sampler),pssm_coord + vec4(ofs,0.0,0.0)); + } + + } + + shadow /= 3.0 * 3.0; + +*/ + shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance + + shadow_attenuation = mix(shadow_color, vec3(1.0), shadow); + } + + total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy / M_PI; + } + + //compute lights from cluster + + vec3 cluster_pos; + cluster_pos.xy = fog_unit_pos.xy; + cluster_pos.z = clamp((abs(view_pos.z) - params.z_near) / (params.z_far - params.z_near), 0.0, 1.0); + + uvec4 cluster_cell = texture(usampler3D(cluster_texture, linear_sampler), cluster_pos); + + uint omni_light_count = cluster_cell.x >> CLUSTER_COUNTER_SHIFT; + uint omni_light_pointer = cluster_cell.x & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < omni_light_count; i++) { + uint light_index = cluster_data.indices[omni_light_pointer + i]; + + vec3 light_pos = lights.data[i].position; + float d = distance(lights.data[i].position, view_pos) * lights.data[i].inv_radius; + vec3 shadow_attenuation = vec3(1.0); + + if (d < 1.0) { + vec2 attenuation_energy = unpackHalf2x16(lights.data[i].attenuation_energy); + vec4 color_specular = unpackUnorm4x8(lights.data[i].color_specular); + + float attenuation = pow(max(1.0 - d, 0.0), attenuation_energy.x); + + vec3 light = attenuation_energy.y * color_specular.rgb / M_PI; + + vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[i].shadow_color_enabled); + + if (shadow_color_enabled.a > 0.5) { + //has shadow + vec4 v = vec4(view_pos, 1.0); + + vec4 splane = (lights.data[i].shadow_matrix * v); + float shadow_len = length(splane.xyz); //need to remember shadow len from here + + splane.xyz = normalize(splane.xyz); + vec4 clamp_rect = lights.data[i].atlas_rect; + + if (splane.z >= 0.0) { + splane.z += 1.0; + + clamp_rect.y += clamp_rect.w; + + } else { + splane.z = 1.0 - splane.z; + } + + splane.xy /= splane.z; + + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len * lights.data[i].inv_radius; + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; + splane.w = 1.0; //needed? i think it should be 1 already + + float depth = texture(sampler2D(shadow_atlas, linear_sampler), splane.xy).r; + float shadow = exp(min(0.0, (depth - splane.z)) / lights.data[i].inv_radius * lights.data[i].shadow_volumetric_fog_fade); + + shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + } + total_light += light * attenuation * shadow_attenuation; + } + } + + uint spot_light_count = cluster_cell.y >> CLUSTER_COUNTER_SHIFT; + uint spot_light_pointer = cluster_cell.y & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < spot_light_count; i++) { + uint light_index = cluster_data.indices[spot_light_pointer + i]; + + vec3 light_pos = lights.data[i].position; + vec3 light_rel_vec = lights.data[i].position - view_pos; + float d = length(light_rel_vec) * lights.data[i].inv_radius; + vec3 shadow_attenuation = vec3(1.0); + + if (d < 1.0) { + vec2 attenuation_energy = unpackHalf2x16(lights.data[i].attenuation_energy); + vec4 color_specular = unpackUnorm4x8(lights.data[i].color_specular); + + float attenuation = pow(max(1.0 - d, 0.0), attenuation_energy.x); + + vec3 spot_dir = lights.data[i].direction; + vec2 spot_att_angle = unpackHalf2x16(lights.data[i].cone_attenuation_angle); + float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_att_angle.y); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_att_angle.y)); + attenuation *= 1.0 - pow(spot_rim, spot_att_angle.x); + + vec3 light = attenuation_energy.y * color_specular.rgb / M_PI; + + vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[i].shadow_color_enabled); + + if (shadow_color_enabled.a > 0.5) { + //has shadow + vec4 v = vec4(view_pos, 1.0); + + vec4 splane = (lights.data[i].shadow_matrix * v); + splane /= splane.w; + + float depth = texture(sampler2D(shadow_atlas, linear_sampler), splane.xy).r; + float shadow = exp(min(0.0, (depth - splane.z)) / lights.data[i].inv_radius * lights.data[i].shadow_volumetric_fog_fade); + + shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + } + + total_light += light * attenuation * shadow_attenuation; + } + } + + vec3 world_pos = mat3(params.cam_rotation) * view_pos; + + for (uint i = 0; i < params.max_gi_probes; i++) { + vec3 position = (gi_probes.data[i].xform * vec4(world_pos, 1.0)).xyz; + + //this causes corrupted pixels, i have no idea why.. + if (all(bvec2(all(greaterThanEqual(position, vec3(0.0))), all(lessThan(position, gi_probes.data[i].bounds))))) { + position /= gi_probes.data[i].bounds; + + vec4 light = vec4(0.0); + for (uint j = 0; j < gi_probes.data[i].mipmaps; j++) { + vec4 slight = textureLod(sampler3D(gi_probe_textures[i], linear_sampler_with_mipmaps), position, float(j)); + float a = (1.0 - light.a); + light += a * slight; + } + + light.rgb *= gi_probes.data[i].dynamic_range * params.gi_inject; + + total_light += light.rgb; + } + } + + //sdfgi +#ifdef ENABLE_SDFGI + + { + float blend = -1.0; + vec3 ambient_total = vec3(0.0); + + for (uint i = 0; i < sdfgi.max_cascades; i++) { + vec3 cascade_pos = (world_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe; + + if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) { + continue; //skip cascade + } + + vec3 base_pos = floor(cascade_pos); + ivec3 probe_base_pos = ivec3(base_pos); + + vec4 ambient_accum = vec4(0.0); + + ivec3 tex_pos = ivec3(probe_base_pos.xy, int(i)); + tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size; + + for (uint j = 0; j < 8; j++) { + ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1); + ivec3 probe_posi = probe_base_pos; + probe_posi += offset; + + // Compute weight + + vec3 probe_pos = vec3(probe_posi); + vec3 probe_to_pos = cascade_pos - probe_pos; + + vec3 trilinear = vec3(1.0) - abs(probe_to_pos); + float weight = trilinear.x * trilinear.y * trilinear.z; + + // Compute lightprobe occlusion + + if (sdfgi.use_occlusion) { + ivec3 occ_indexv = abs((sdfgi.cascades[i].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4); + vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3))); + + vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw; + occ_pos.z += float(i); + if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures + occ_pos.x += 1.0; + } + + occ_pos *= sdfgi.occlusion_renormalize; + float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask); + + weight *= max(occlusion, 0.01); + } + + // Compute ambient texture position + + ivec3 uvw = tex_pos; + uvw.xy += offset.xy; + uvw.x += offset.z * sdfgi.probe_axis_size; + + vec3 ambient = texelFetch(sampler2DArray(sdfgi_ambient_texture, linear_sampler), uvw, 0).rgb; + + ambient_accum.rgb += ambient * weight; + ambient_accum.a += weight; + } + + if (ambient_accum.a > 0) { + ambient_accum.rgb /= ambient_accum.a; + } + ambient_total = ambient_accum.rgb; + break; + } + + total_light += ambient_total * params.gi_inject; + } + +#endif + + imageStore(density_map, pos, vec4(total_light, total_density)); +#endif + +#ifdef MODE_FOG + + ivec3 pos = ivec3(gl_GlobalInvocationID.xy, 0); + + if (any(greaterThanEqual(pos, params.fog_volume_size))) { + return; //do not compute + } + + vec4 fog_accum = vec4(0.0); + float prev_z = 0.0; + + float t = 1.0; + + for (int i = 0; i < params.fog_volume_size.z; i++) { + //compute fog position + ivec3 fog_pos = pos + ivec3(0, 0, i); + //get fog value + vec4 fog = imageLoad(density_map, fog_pos); + + //get depth at cell pos + float z = get_depth_at_pos(fog_cell_size.z, i); + //get distance from previos pos + float d = abs(prev_z - z); + //compute exinction based on beer's + float extinction = t * exp(-d * fog.a); + //compute alpha based on different of extinctions + float alpha = t - extinction; + //update extinction + t = extinction; + + fog_accum += vec4(fog.rgb * alpha, alpha); + prev_z = z; + + vec4 fog_value; + + if (fog_accum.a > 0.0) { + fog_value = vec4(fog_accum.rgb / fog_accum.a, 1.0 - t); + } else { + fog_value = vec4(0.0); + } + + imageStore(fog_map, fog_pos, fog_value); + } + +#endif + +#ifdef MODE_FILTER + + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + + const float gauss[7] = float[](0.071303, 0.131514, 0.189879, 0.214607, 0.189879, 0.131514, 0.071303); + + const ivec3 filter_dir[3] = ivec3[](ivec3(1, 0, 0), ivec3(0, 1, 0), ivec3(0, 0, 1)); + ivec3 offset = filter_dir[params.filter_axis]; + + vec4 accum = vec4(0.0); + for (int i = -3; i <= 3; i++) { + accum += imageLoad(source_map, clamp(pos + offset * i, ivec3(0), params.fog_volume_size - ivec3(1))) * gauss[i + 3]; + } + + imageStore(dest_map, pos, accum); + +#endif +} diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 706912b353..b4eac8cd76 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -562,9 +562,13 @@ public: BIND6(environment_set_adjustment, RID, bool, float, float, float, RID) - BIND5(environment_set_fog, RID, bool, const Color &, const Color &, float) - BIND7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) - BIND5(environment_set_fog_height, RID, bool, float, float, float) + BIND8(environment_set_fog, RID, bool, const Color &, float, float, float, float, float) + BIND9(environment_set_volumetric_fog, RID, bool, float, const Color &, float, float, float, float, EnvVolumetricFogShadowFilter) + + BIND2(environment_set_volumetric_fog_volume_size, int, int) + BIND1(environment_set_volumetric_fog_filter_active, bool) + BIND1(environment_set_volumetric_fog_directional_shadow_shrink_size, int) + BIND1(environment_set_volumetric_fog_positional_shadow_shrink_size, int) BIND11(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, bool, bool, float, float, float) BIND1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount) diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 60a694eed5..fb5c161c8a 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -479,9 +479,14 @@ public: FUNC6(environment_set_adjustment, RID, bool, float, float, float, RID) - FUNC5(environment_set_fog, RID, bool, const Color &, const Color &, float) - FUNC7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) - FUNC5(environment_set_fog_height, RID, bool, float, float, float) + FUNC8(environment_set_fog, RID, bool, const Color &, float, float, float, float, float) + + FUNC9(environment_set_volumetric_fog, RID, bool, float, const Color &, float, float, float, float, EnvVolumetricFogShadowFilter) + + FUNC2(environment_set_volumetric_fog_volume_size, int, int) + FUNC1(environment_set_volumetric_fog_filter_active, bool) + FUNC1(environment_set_volumetric_fog_directional_shadow_shrink_size, int) + FUNC1(environment_set_volumetric_fog_positional_shadow_shrink_size, int) FUNC3R(Ref<Image>, environment_bake_panorama, RID, bool, const Size2i &) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index c156313c15..3b034da3aa 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1747,11 +1747,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "ramp"), &RenderingServer::environment_set_adjustment); ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr); ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "bias", "light_affect", "ao_channel_affect", "blur", "bilateral_sharpness"), &RenderingServer::environment_set_ssao); - ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "color", "sun_color", "sun_amount"), &RenderingServer::environment_set_fog); - - ClassDB::bind_method(D_METHOD("environment_set_fog_depth", "env", "enable", "depth_begin", "depth_end", "depth_curve", "transmit", "transmit_curve"), &RenderingServer::environment_set_fog_depth); - - ClassDB::bind_method(D_METHOD("environment_set_fog_height", "env", "enable", "min_height", "max_height", "height_curve"), &RenderingServer::environment_set_fog_height); + ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density"), &RenderingServer::environment_set_fog); ClassDB::bind_method(D_METHOD("scenario_create"), &RenderingServer::scenario_create); ClassDB::bind_method(D_METHOD("scenario_set_debug", "scenario", "debug_mode"), &RenderingServer::scenario_set_debug); @@ -2411,6 +2407,17 @@ RenderingServer::RenderingServer() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/probe_ray_count", PropertyInfo(Variant::INT, "rendering/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)")); GLOBAL_DEF("rendering/sdfgi/frames_to_converge", 1); ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/frames_to_converge", PropertyInfo(Variant::INT, "rendering/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)")); + + GLOBAL_DEF("rendering/volumetric_fog/volume_size", 64); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/volume_size", PropertyInfo(Variant::INT, "rendering/volumetric_fog/volume_size", PROPERTY_HINT_RANGE, "16,512,1")); + GLOBAL_DEF("rendering/volumetric_fog/volume_depth", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/volume_depth", PropertyInfo(Variant::INT, "rendering/volumetric_fog/volume_depth", PROPERTY_HINT_RANGE, "16,512,1")); + GLOBAL_DEF("rendering/volumetric_fog/use_filter", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/use_filter", PropertyInfo(Variant::INT, "rendering/volumetric_fog/use_filter", PROPERTY_HINT_ENUM, "No (Faster),Yes (Higher Quality)")); + GLOBAL_DEF("rendering/volumetric_fog/directional_shadow_shrink", 512); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/directional_shadow_shrink", PropertyInfo(Variant::INT, "rendering/volumetric_fog/directional_shadow_shrink", PROPERTY_HINT_RANGE, "32,2048,1")); + GLOBAL_DEF("rendering/volumetric_fog/positional_shadow_shrink", 512); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/positional_shadow_shrink", PropertyInfo(Variant::INT, "rendering/volumetric_fog/positional_shadow_shrink", PROPERTY_HINT_RANGE, "32,2048,1")); } RenderingServer::~RenderingServer() { diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 109e9e53c5..a13fd4954b 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -390,6 +390,7 @@ public: LIGHT_PARAM_SHADOW_BIAS, LIGHT_PARAM_SHADOW_PANCAKE_SIZE, LIGHT_PARAM_SHADOW_BLUR, + LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, LIGHT_PARAM_TRANSMITTANCE_BIAS, LIGHT_PARAM_MAX }; @@ -861,9 +862,20 @@ public: virtual void environment_set_sdfgi_frames_to_converge(EnvironmentSDFGIFramesToConverge p_frames) = 0; - virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0; - virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0; - virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0; + virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density) = 0; + + enum EnvVolumetricFogShadowFilter { + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED, + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_LOW, + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM, + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_HIGH, + }; + + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, EnvVolumetricFogShadowFilter p_shadow_filter) = 0; + virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0; + virtual void environment_set_volumetric_fog_filter_active(bool p_enable) = 0; + virtual void environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size) = 0; + virtual void environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size) = 0; virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0; diff --git a/tests/test_ordered_hash_map.cpp b/tests/test_ordered_hash_map.cpp deleted file mode 100644 index d18a3784be..0000000000 --- a/tests/test_ordered_hash_map.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************/ -/* test_ordered_hash_map.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#include "test_ordered_hash_map.h" - -#include "core/ordered_hash_map.h" -#include "core/os/os.h" -#include "core/pair.h" -#include "core/vector.h" - -namespace TestOrderedHashMap { - -bool test_insert() { - OrderedHashMap<int, int> map; - OrderedHashMap<int, int>::Element e = map.insert(42, 84); - - return e && e.key() == 42 && e.get() == 84 && e.value() == 84 && map[42] == 84 && map.has(42) && map.find(42); -} - -bool test_insert_overwrite() { - OrderedHashMap<int, int> map; - map.insert(42, 84); - map.insert(42, 1234); - - return map[42] == 1234; -} - -bool test_erase_via_element() { - OrderedHashMap<int, int> map; - OrderedHashMap<int, int>::Element e = map.insert(42, 84); - - map.erase(e); - return !e && !map.has(42) && !map.find(42); -} - -bool test_erase_via_key() { - OrderedHashMap<int, int> map; - map.insert(42, 84); - map.erase(42); - return !map.has(42) && !map.find(42); -} - -bool test_size() { - OrderedHashMap<int, int> map; - map.insert(42, 84); - map.insert(123, 84); - map.insert(123, 84); - map.insert(0, 84); - map.insert(123485, 84); - - return map.size() == 4; -} - -bool test_iteration() { - OrderedHashMap<int, int> map; - map.insert(42, 84); - map.insert(123, 12385); - map.insert(0, 12934); - map.insert(123485, 1238888); - map.insert(123, 111111); - - Vector<Pair<int, int>> expected; - expected.push_back(Pair<int, int>(42, 84)); - expected.push_back(Pair<int, int>(123, 111111)); - expected.push_back(Pair<int, int>(0, 12934)); - expected.push_back(Pair<int, int>(123485, 1238888)); - - int idx = 0; - for (OrderedHashMap<int, int>::Element E = map.front(); E; E = E.next()) { - if (expected[idx] != Pair<int, int>(E.key(), E.value())) { - return false; - } - ++idx; - } - return true; -} - -bool test_const_iteration(const OrderedHashMap<int, int> &map) { - Vector<Pair<int, int>> expected; - expected.push_back(Pair<int, int>(42, 84)); - expected.push_back(Pair<int, int>(123, 111111)); - expected.push_back(Pair<int, int>(0, 12934)); - expected.push_back(Pair<int, int>(123485, 1238888)); - - int idx = 0; - for (OrderedHashMap<int, int>::ConstElement E = map.front(); E; E = E.next()) { - if (expected[idx] != Pair<int, int>(E.key(), E.value())) { - return false; - } - ++idx; - } - return true; -} - -bool test_const_iteration() { - OrderedHashMap<int, int> map; - map.insert(42, 84); - map.insert(123, 12385); - map.insert(0, 12934); - map.insert(123485, 1238888); - map.insert(123, 111111); - - return test_const_iteration(map); -} - -typedef bool (*TestFunc)(); - -TestFunc test_funcs[] = { - - test_insert, - test_insert_overwrite, - test_erase_via_element, - test_erase_via_key, - test_size, - test_iteration, - test_const_iteration, - nullptr - -}; - -MainLoop *test() { - int count = 0; - int passed = 0; - - while (true) { - if (!test_funcs[count]) { - break; - } - bool pass = test_funcs[count](); - if (pass) { - passed++; - } - OS::get_singleton()->print("\t%s\n", pass ? "PASS" : "FAILED"); - - count++; - } - - OS::get_singleton()->print("\n\n\n"); - OS::get_singleton()->print("*************\n"); - OS::get_singleton()->print("***TOTALS!***\n"); - OS::get_singleton()->print("*************\n"); - - OS::get_singleton()->print("Passed %i of %i tests\n", passed, count); - - return nullptr; -} - -} // namespace TestOrderedHashMap diff --git a/tests/test_ordered_hash_map.h b/tests/test_ordered_hash_map.h index f251da0ba2..3182c391cb 100644 --- a/tests/test_ordered_hash_map.h +++ b/tests/test_ordered_hash_map.h @@ -31,11 +31,109 @@ #ifndef TEST_ORDERED_HASH_MAP_H #define TEST_ORDERED_HASH_MAP_H -#include "core/os/main_loop.h" +#include "core/ordered_hash_map.h" +#include "core/os/os.h" +#include "core/pair.h" +#include "core/vector.h" + +#include "tests/test_macros.h" namespace TestOrderedHashMap { -MainLoop *test(); +TEST_CASE("[OrderedHashMap] Insert element") { + OrderedHashMap<int, int> map; + OrderedHashMap<int, int>::Element e = map.insert(42, 84); + + CHECK(e); + CHECK(e.key() == 42); + CHECK(e.get() == 84); + CHECK(e.value() == 84); + CHECK(map[42] == 84); + CHECK(map.has(42)); + CHECK(map.find(42)); +} + +TEST_CASE("[OrderedHashMap] Overwrite element") { + OrderedHashMap<int, int> map; + map.insert(42, 84); + map.insert(42, 1234); + + CHECK(map[42] == 1234); +} + +TEST_CASE("[OrderedHashMap] Erase via element") { + OrderedHashMap<int, int> map; + OrderedHashMap<int, int>::Element e = map.insert(42, 84); + + map.erase(e); + CHECK(!e); + CHECK(!map.has(42)); + CHECK(!map.find(42)); +} + +TEST_CASE("[OrderedHashMap] Erase via key") { + OrderedHashMap<int, int> map; + map.insert(42, 84); + map.erase(42); + CHECK(!map.has(42)); + CHECK(!map.find(42)); } +TEST_CASE("[OrderedHashMap] Size") { + OrderedHashMap<int, int> map; + map.insert(42, 84); + map.insert(123, 84); + map.insert(123, 84); + map.insert(0, 84); + map.insert(123485, 84); + + CHECK(map.size() == 4); +} + +TEST_CASE("[OrderedHashMap] Iteration") { + OrderedHashMap<int, int> map; + map.insert(42, 84); + map.insert(123, 12385); + map.insert(0, 12934); + map.insert(123485, 1238888); + map.insert(123, 111111); + + Vector<Pair<int, int>> expected; + expected.push_back(Pair<int, int>(42, 84)); + expected.push_back(Pair<int, int>(123, 111111)); + expected.push_back(Pair<int, int>(0, 12934)); + expected.push_back(Pair<int, int>(123485, 1238888)); + + int idx = 0; + for (OrderedHashMap<int, int>::Element E = map.front(); E; E = E.next()) { + CHECK(expected[idx] == Pair<int, int>(E.key(), E.value())); + ++idx; + } +} + +TEST_CASE("[OrderedHashMap] Const iteration") { + OrderedHashMap<int, int> map; + map.insert(42, 84); + map.insert(123, 12385); + map.insert(0, 12934); + map.insert(123485, 1238888); + map.insert(123, 111111); + + const OrderedHashMap<int, int> const_map = map; + + Vector<Pair<int, int>> expected; + expected.push_back(Pair<int, int>(42, 84)); + expected.push_back(Pair<int, int>(123, 111111)); + expected.push_back(Pair<int, int>(0, 12934)); + expected.push_back(Pair<int, int>(123485, 1238888)); + + int idx = 0; + for (OrderedHashMap<int, int>::ConstElement E = const_map.front(); E; E = E.next()) { + CHECK(expected[idx] == Pair<int, int>(E.key(), E.value())); + ++idx; + } +} + +} // namespace TestOrderedHashMap + #endif // TEST_ORDERED_HASH_MAP_H |