summaryrefslogtreecommitdiff
path: root/servers
diff options
context:
space:
mode:
Diffstat (limited to 'servers')
-rw-r--r--servers/SCsub1
-rw-r--r--servers/audio/audio_driver_dummy.cpp67
-rw-r--r--servers/audio/audio_driver_dummy.h22
-rw-r--r--servers/audio/effects/audio_effect_record.cpp4
-rw-r--r--servers/audio/effects/audio_effect_record.h1
-rw-r--r--servers/movie_writer/SCsub5
-rw-r--r--servers/movie_writer/movie_writer.cpp306
-rw-r--r--servers/movie_writer/movie_writer.h123
-rw-r--r--servers/movie_writer/movie_writer_mjpeg.cpp263
-rw-r--r--servers/movie_writer/movie_writer_mjpeg.h73
-rw-r--r--servers/navigation_server_2d.cpp10
-rw-r--r--servers/navigation_server_2d.h6
-rw-r--r--servers/navigation_server_3d.cpp6
-rw-r--r--servers/navigation_server_3d.h6
-rw-r--r--servers/physics_2d/godot_area_2d.h6
-rw-r--r--servers/physics_3d/godot_area_3d.h6
-rw-r--r--servers/register_server_types.cpp15
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp5
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp5
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h1
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/tonemap.glsl9
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl3
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl13
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl1
-rw-r--r--servers/rendering/renderer_rd/uniform_set_cache_rd.h18
-rw-r--r--servers/rendering/renderer_viewport.cpp14
-rw-r--r--servers/rendering/renderer_viewport.h2
-rw-r--r--servers/rendering/rendering_server_default.cpp7
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering_server.cpp8
-rw-r--r--servers/rendering_server.h2
-rw-r--r--servers/text_server.cpp24
-rw-r--r--servers/text_server.h28
39 files changed, 1011 insertions, 76 deletions
diff --git a/servers/SCsub b/servers/SCsub
index 66a1b9b26f..2ce90e970b 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -14,6 +14,7 @@ SConscript("audio/SCsub")
SConscript("text/SCsub")
SConscript("debugger/SCsub")
SConscript("extensions/SCsub")
+SConscript("movie_writer/SCsub")
lib = env.add_library("servers", env.servers_sources)
diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
index 635e4601c7..60eb657923 100644
--- a/servers/audio/audio_driver_dummy.cpp
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -33,22 +33,24 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
+AudioDriverDummy *AudioDriverDummy::singleton = nullptr;
+
Error AudioDriverDummy::init() {
active = false;
thread_exited = false;
exit_thread = false;
samples_in = nullptr;
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
- speaker_mode = SPEAKER_MODE_STEREO;
- channels = 2;
-
- int latency = GLOBAL_GET("audio/driver/output_latency");
- buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
+ if (mix_rate == -1) {
+ mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ }
+ channels = get_channels();
samples_in = memnew_arr(int32_t, (size_t)buffer_frames * channels);
- thread.start(AudioDriverDummy::thread_func, this);
+ if (use_threads) {
+ thread.start(AudioDriverDummy::thread_func, this);
+ }
return OK;
};
@@ -93,11 +95,56 @@ void AudioDriverDummy::unlock() {
mutex.unlock();
};
+void AudioDriverDummy::set_use_threads(bool p_use_threads) {
+ use_threads = p_use_threads;
+}
+
+void AudioDriverDummy::set_speaker_mode(SpeakerMode p_mode) {
+ speaker_mode = p_mode;
+}
+
+void AudioDriverDummy::set_mix_rate(int p_rate) {
+ mix_rate = p_rate;
+}
+
+uint32_t AudioDriverDummy::get_channels() const {
+ static const int channels_for_mode[4] = { 2, 4, 8, 16 };
+ return channels_for_mode[speaker_mode];
+}
+
+void AudioDriverDummy::mix_audio(int p_frames, int32_t *p_buffer) {
+ ERR_FAIL_COND(!active); // If not active, should not mix.
+ ERR_FAIL_COND(use_threads == true); // If using threads, this will not work well.
+
+ uint32_t todo = p_frames;
+ while (todo) {
+ uint32_t to_mix = MIN(buffer_frames, todo);
+ lock();
+ audio_server_process(to_mix, samples_in);
+ unlock();
+
+ uint32_t total_samples = to_mix * channels;
+
+ for (uint32_t i = 0; i < total_samples; i++) {
+ p_buffer[i] = samples_in[i];
+ }
+
+ todo -= to_mix;
+ p_buffer += total_samples;
+ }
+}
+
void AudioDriverDummy::finish() {
- exit_thread = true;
- thread.wait_to_finish();
+ if (use_threads) {
+ exit_thread = true;
+ thread.wait_to_finish();
+ }
if (samples_in) {
memdelete_arr(samples_in);
};
-};
+}
+
+AudioDriverDummy::AudioDriverDummy() {
+ singleton = this;
+}
diff --git a/servers/audio/audio_driver_dummy.h b/servers/audio/audio_driver_dummy.h
index 68b523e086..232a8d5e1f 100644
--- a/servers/audio/audio_driver_dummy.h
+++ b/servers/audio/audio_driver_dummy.h
@@ -44,9 +44,9 @@ class AudioDriverDummy : public AudioDriver {
static void thread_func(void *p_udata);
- unsigned int buffer_frames;
- unsigned int mix_rate;
- SpeakerMode speaker_mode;
+ uint32_t buffer_frames = 4096;
+ int32_t mix_rate = -1;
+ SpeakerMode speaker_mode = SPEAKER_MODE_STEREO;
int channels;
@@ -54,6 +54,10 @@ class AudioDriverDummy : public AudioDriver {
bool thread_exited;
mutable bool exit_thread;
+ bool use_threads = true;
+
+ static AudioDriverDummy *singleton;
+
public:
const char *get_name() const {
return "Dummy";
@@ -67,7 +71,17 @@ public:
virtual void unlock();
virtual void finish();
- AudioDriverDummy() {}
+ void set_use_threads(bool p_use_threads);
+ void set_speaker_mode(SpeakerMode p_mode);
+ void set_mix_rate(int p_rate);
+
+ uint32_t get_channels() const;
+
+ void mix_audio(int p_frames, int32_t *p_buffer);
+
+ static AudioDriverDummy *get_dummy_singleton() { return singleton; }
+
+ AudioDriverDummy();
~AudioDriverDummy() {}
};
diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp
index d68522e5b9..a6553e1431 100644
--- a/servers/audio/effects/audio_effect_record.cpp
+++ b/servers/audio/effects/audio_effect_record.cpp
@@ -71,8 +71,6 @@ bool AudioEffectRecordInstance::process_silence() const {
}
void AudioEffectRecordInstance::_io_thread_process() {
- thread_active = true;
-
while (is_recording) {
//Check: The current recording has been requested to stop
if (!base->recording_active) {
@@ -86,8 +84,6 @@ void AudioEffectRecordInstance::_io_thread_process() {
OS::get_singleton()->delay_usec(500);
}
}
-
- thread_active = false;
}
void AudioEffectRecordInstance::_io_store_buffer() {
diff --git a/servers/audio/effects/audio_effect_record.h b/servers/audio/effects/audio_effect_record.h
index 305484d1cb..8a6247e27a 100644
--- a/servers/audio/effects/audio_effect_record.h
+++ b/servers/audio/effects/audio_effect_record.h
@@ -48,7 +48,6 @@ class AudioEffectRecordInstance : public AudioEffectInstance {
bool is_recording;
Thread io_thread;
- bool thread_active = false;
Vector<AudioFrame> ring_buffer;
Vector<float> recording_data;
diff --git a/servers/movie_writer/SCsub b/servers/movie_writer/SCsub
new file mode 100644
index 0000000000..86681f9c74
--- /dev/null
+++ b/servers/movie_writer/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.servers_sources, "*.cpp")
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
new file mode 100644
index 0000000000..ac60dc3b9a
--- /dev/null
+++ b/servers/movie_writer/movie_writer.cpp
@@ -0,0 +1,306 @@
+/*************************************************************************/
+/* movie_writer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 "movie_writer.h"
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+
+MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
+uint32_t MovieWriter::writer_count = 0;
+
+void MovieWriter::add_writer(MovieWriter *p_writer) {
+ ERR_FAIL_COND(writer_count == MAX_WRITERS);
+ writers[writer_count++] = p_writer;
+}
+
+MovieWriter *MovieWriter::find_writer_for_file(const String &p_file) {
+ for (int32_t i = writer_count - 1; i >= 0; i--) { // More recent last, to have override ability.
+ if (writers[i]->handles_file(p_file)) {
+ return writers[i];
+ }
+ }
+ return nullptr;
+}
+
+uint32_t MovieWriter::get_audio_mix_rate() const {
+ uint32_t ret = 0;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_audio_mix_rate, ret)) {
+ return ret;
+ }
+ return 48000;
+}
+AudioServer::SpeakerMode MovieWriter::get_audio_speaker_mode() const {
+ AudioServer::SpeakerMode ret = AudioServer::SPEAKER_MODE_STEREO;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_audio_speaker_mode, ret)) {
+ return ret;
+ }
+ return AudioServer::SPEAKER_MODE_STEREO;
+}
+
+Error MovieWriter::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ Error ret = OK;
+ if (GDVIRTUAL_REQUIRED_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret)) {
+ return ret;
+ }
+ return ERR_UNCONFIGURED;
+}
+
+Error MovieWriter::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
+ Error ret = OK;
+ if (GDVIRTUAL_REQUIRED_CALL(_write_frame, p_image, p_audio_data, ret)) {
+ return ret;
+ }
+ return ERR_UNCONFIGURED;
+}
+
+void MovieWriter::write_end() {
+ GDVIRTUAL_REQUIRED_CALL(_write_end);
+}
+
+bool MovieWriter::handles_file(const String &p_path) const {
+ bool ret = false;
+ if (GDVIRTUAL_REQUIRED_CALL(_handles_file, p_path, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
+ Vector<String> exts;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_supported_extensions, exts)) {
+ for (int i = 0; i < exts.size(); i++) {
+ r_extensions->push_back(exts[i]);
+ }
+ }
+}
+
+void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ mix_rate = get_audio_mix_rate();
+ AudioDriverDummy::get_dummy_singleton()->set_mix_rate(mix_rate);
+ AudioDriverDummy::get_dummy_singleton()->set_speaker_mode(AudioDriver::SpeakerMode(get_audio_speaker_mode()));
+ fps = p_fps;
+ if ((mix_rate % fps) != 0) {
+ WARN_PRINT("Audio mix rate (" + itos(mix_rate) + ") can not be divided by fps (" + itos(fps) + "). Audio may go out of sync over time.");
+ }
+
+ audio_channels = AudioDriverDummy::get_dummy_singleton()->get_channels();
+ audio_mix_buffer.resize(mix_rate * audio_channels / fps);
+
+ write_begin(p_movie_size, p_fps, p_base_path);
+}
+
+void MovieWriter::_bind_methods() {
+ ClassDB::bind_static_method("MovieWriter", D_METHOD("add_writer", "writer"), &MovieWriter::add_writer);
+
+ GDVIRTUAL_BIND(_get_audio_mix_rate)
+ GDVIRTUAL_BIND(_get_audio_speaker_mode)
+
+ GDVIRTUAL_BIND(_handles_file, "path")
+
+ GDVIRTUAL_BIND(_write_begin, "movie_size", "fps", "base_path")
+ GDVIRTUAL_BIND(_write_frame, "frame_image", "audio_frame_block")
+ GDVIRTUAL_BIND(_write_end)
+
+ GLOBAL_DEF("editor/movie_writer/mix_rate_hz", 48000);
+ GLOBAL_DEF("editor/movie_writer/speaker_mode", 0);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/movie_writer/speaker_mode", PropertyInfo(Variant::INT, "editor/movie_writer/speaker_mode", PROPERTY_HINT_ENUM, "Stereo,3.1,5.1,7.1"));
+ GLOBAL_DEF("editor/movie_writer/mjpeg_quality", 0.75);
+ // used by the editor
+ GLOBAL_DEF_BASIC("editor/movie_writer/movie_file", "");
+ GLOBAL_DEF_BASIC("editor/movie_writer/disable_vsync", false);
+ GLOBAL_DEF_BASIC("editor/movie_writer/fps", 60);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/movie_writer/fps", PropertyInfo(Variant::INT, "editor/movie_writer/fps", PROPERTY_HINT_RANGE, "1,300,1"));
+}
+
+void MovieWriter::set_extensions_hint() {
+ RBSet<String> found;
+ for (uint32_t i = 0; i < writer_count; i++) {
+ List<String> extensions;
+ writers[i]->get_supported_extensions(&extensions);
+ for (const String &ext : extensions) {
+ found.insert(ext);
+ }
+ }
+
+ String ext_hint;
+
+ for (const String &S : found) {
+ if (ext_hint != "") {
+ ext_hint += ",";
+ }
+ ext_hint += "*." + S;
+ }
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/movie_writer/movie_file", PropertyInfo(Variant::STRING, "editor/movie_writer/movie_file", PROPERTY_HINT_GLOBAL_SAVE_FILE, ext_hint));
+}
+
+void MovieWriter::add_frame(const Ref<Image> &p_image) {
+ AudioDriverDummy::get_dummy_singleton()->mix_audio(mix_rate / fps, audio_mix_buffer.ptr());
+ write_frame(p_image, audio_mix_buffer.ptr());
+}
+
+void MovieWriter::end() {
+ write_end();
+}
+/////////////////////////////////////////
+
+uint32_t MovieWriterPNGWAV::get_audio_mix_rate() const {
+ return mix_rate;
+}
+AudioServer::SpeakerMode MovieWriterPNGWAV::get_audio_speaker_mode() const {
+ return speaker_mode;
+}
+
+void MovieWriterPNGWAV::get_supported_extensions(List<String> *r_extensions) const {
+ r_extensions->push_back("png");
+}
+
+bool MovieWriterPNGWAV::handles_file(const String &p_path) const {
+ return p_path.get_extension().to_lower() == "png";
+}
+
+String MovieWriterPNGWAV::zeros_str(uint32_t p_index) {
+ char zeros[MAX_TRAILING_ZEROS + 1];
+ for (uint32_t i = 0; i < MAX_TRAILING_ZEROS; i++) {
+ uint32_t idx = MAX_TRAILING_ZEROS - i - 1;
+ uint32_t digit = (p_index / uint32_t(Math::pow(double(10), double(idx)))) % 10;
+ zeros[i] = '0' + digit;
+ }
+ zeros[MAX_TRAILING_ZEROS] = 0;
+ return zeros;
+}
+
+Error MovieWriterPNGWAV::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ // Quick & Dirty PNGWAV Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
+
+ base_path = p_base_path.get_basename();
+ if (base_path.is_relative_path()) {
+ base_path = "res://" + base_path;
+ }
+
+ {
+ //Remove existing files before writing anew
+ uint32_t idx = 0;
+ Ref<DirAccess> d = DirAccess::open(base_path.get_base_dir());
+ String file = base_path.get_file();
+ while (true) {
+ String path = file + zeros_str(idx) + ".png";
+ if (d->remove(path) != OK) {
+ break;
+ }
+ }
+ }
+
+ f_wav = FileAccess::open(base_path + ".wav", FileAccess::WRITE_READ);
+ ERR_FAIL_COND_V(f_wav.is_null(), ERR_CANT_OPEN);
+
+ fps = p_fps;
+
+ f_wav->store_buffer((const uint8_t *)"RIFF", 4);
+ int total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */;
+ f_wav->store_32(total_size); //will store final later
+ f_wav->store_buffer((const uint8_t *)"WAVE", 4);
+
+ /* FORMAT CHUNK */
+
+ f_wav->store_buffer((const uint8_t *)"fmt ", 4);
+
+ uint32_t channels = 2;
+ switch (speaker_mode) {
+ case AudioServer::SPEAKER_MODE_STEREO:
+ channels = 2;
+ break;
+ case AudioServer::SPEAKER_SURROUND_31:
+ channels = 4;
+ break;
+ case AudioServer::SPEAKER_SURROUND_51:
+ channels = 6;
+ break;
+ case AudioServer::SPEAKER_SURROUND_71:
+ channels = 8;
+ break;
+ }
+
+ f_wav->store_32(16); //standard format, no extra fields
+ f_wav->store_16(1); // compression code, standard PCM
+ f_wav->store_16(channels); //CHANNELS: 2
+
+ f_wav->store_32(mix_rate);
+
+ /* useless stuff the format asks for */
+
+ int bits_per_sample = 32;
+ int blockalign = bits_per_sample / 8 * channels;
+ int bytes_per_sec = mix_rate * blockalign;
+
+ audio_block_size = (mix_rate / fps) * blockalign;
+
+ f_wav->store_32(bytes_per_sec);
+ f_wav->store_16(blockalign); // block align (unused)
+ f_wav->store_16(bits_per_sample);
+
+ /* DATA CHUNK */
+
+ f_wav->store_buffer((const uint8_t *)"data", 4);
+
+ f_wav->store_32(0); //data size... wooh
+ wav_data_size_pos = f_wav->get_position();
+
+ return OK;
+}
+
+Error MovieWriterPNGWAV::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
+ ERR_FAIL_COND_V(!f_wav.is_valid(), ERR_UNCONFIGURED);
+
+ Vector<uint8_t> png_buffer = p_image->save_png_to_buffer();
+
+ Ref<FileAccess> fi = FileAccess::open(base_path + zeros_str(frame_count) + ".png", FileAccess::WRITE);
+ fi->store_buffer(png_buffer.ptr(), png_buffer.size());
+ f_wav->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
+
+ frame_count++;
+
+ return OK;
+}
+
+void MovieWriterPNGWAV::write_end() {
+ if (f_wav.is_valid()) {
+ uint32_t total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */;
+ uint32_t datasize = f_wav->get_position() - wav_data_size_pos;
+ f_wav->seek(4);
+ f_wav->store_32(total_size + datasize);
+ f_wav->seek(0x28);
+ f_wav->store_32(datasize);
+ }
+}
+
+MovieWriterPNGWAV::MovieWriterPNGWAV() {
+ mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate_hz");
+ speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
+}
diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h
new file mode 100644
index 0000000000..11e739df39
--- /dev/null
+++ b/servers/movie_writer/movie_writer.h
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* movie_writer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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. */
+/*************************************************************************/
+
+#ifndef MOVIE_WRITER_H
+#define MOVIE_WRITER_H
+
+#include "core/templates/local_vector.h"
+#include "servers/audio/audio_driver_dummy.h"
+#include "servers/audio_server.h"
+
+class MovieWriter : public Object {
+ GDCLASS(MovieWriter, Object);
+
+ uint64_t fps = 0;
+ uint64_t mix_rate = 0;
+ uint32_t audio_channels = 0;
+
+ LocalVector<int32_t> audio_mix_buffer;
+
+ enum {
+ MAX_WRITERS = 8
+ };
+ static MovieWriter *writers[];
+ static uint32_t writer_count;
+
+protected:
+ virtual uint32_t get_audio_mix_rate() const;
+ virtual AudioServer::SpeakerMode get_audio_speaker_mode() const;
+
+ virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
+ virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data);
+ virtual void write_end();
+
+ GDVIRTUAL0RC(uint32_t, _get_audio_mix_rate)
+ GDVIRTUAL0RC(AudioServer::SpeakerMode, _get_audio_speaker_mode)
+
+ GDVIRTUAL1RC(bool, _handles_file, const String &)
+ GDVIRTUAL0RC(Vector<String>, _get_supported_extensions)
+
+ GDVIRTUAL3R(Error, _write_begin, const Size2i &, uint32_t, const String &)
+ GDVIRTUAL2R(Error, _write_frame, const Ref<Image> &, GDNativeConstPtr<int32_t>)
+ GDVIRTUAL0(_write_end)
+
+ static void _bind_methods();
+
+public:
+ virtual bool handles_file(const String &p_path) const;
+ virtual void get_supported_extensions(List<String> *r_extensions) const;
+
+ static void add_writer(MovieWriter *p_writer);
+ static MovieWriter *find_writer_for_file(const String &p_file);
+
+ void begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
+ void add_frame(const Ref<Image> &p_image);
+
+ static void set_extensions_hint();
+
+ void end();
+};
+
+class MovieWriterPNGWAV : public MovieWriter {
+ GDCLASS(MovieWriterPNGWAV, MovieWriter)
+
+ enum {
+ MAX_TRAILING_ZEROS = 8 // more than 10 days at 60fps, no hard drive can put up with this anyway :)
+ };
+
+ uint32_t mix_rate = 48000;
+ AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO;
+ String base_path;
+ uint32_t frame_count = 0;
+ uint32_t fps = 0;
+
+ uint32_t audio_block_size = 0;
+
+ Ref<FileAccess> f_wav;
+ uint32_t wav_data_size_pos = 0;
+
+ String zeros_str(uint32_t p_index);
+
+protected:
+ virtual uint32_t get_audio_mix_rate() const override;
+ virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override;
+ virtual void get_supported_extensions(List<String> *r_extensions) const override;
+
+ virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override;
+ virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override;
+ virtual void write_end() override;
+
+ virtual bool handles_file(const String &p_path) const override;
+
+public:
+ MovieWriterPNGWAV();
+};
+
+#endif // VIDEO_WRITER_H
diff --git a/servers/movie_writer/movie_writer_mjpeg.cpp b/servers/movie_writer/movie_writer_mjpeg.cpp
new file mode 100644
index 0000000000..b0c65e768d
--- /dev/null
+++ b/servers/movie_writer/movie_writer_mjpeg.cpp
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* movie_writer_mjpeg.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 "movie_writer_mjpeg.h"
+#include "core/config/project_settings.h"
+
+uint32_t MovieWriterMJPEG::get_audio_mix_rate() const {
+ return mix_rate;
+}
+AudioServer::SpeakerMode MovieWriterMJPEG::get_audio_speaker_mode() const {
+ return speaker_mode;
+}
+
+bool MovieWriterMJPEG::handles_file(const String &p_path) const {
+ return p_path.get_extension().to_lower() == "avi";
+}
+
+void MovieWriterMJPEG::get_supported_extensions(List<String> *r_extensions) const {
+ r_extensions->push_back("avi");
+}
+
+Error MovieWriterMJPEG::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ // Quick & Dirty MJPEG Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
+
+ base_path = p_base_path.get_basename();
+ if (base_path.is_relative_path()) {
+ base_path = "res://" + base_path;
+ }
+
+ base_path += ".avi";
+
+ f = FileAccess::open(base_path, FileAccess::WRITE_READ);
+
+ fps = p_fps;
+
+ ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);
+
+ f->store_buffer((const uint8_t *)"RIFF", 4);
+ f->store_32(0); // Total length (update later)
+ f->store_buffer((const uint8_t *)"AVI ", 4);
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(300); // 4 + 4 + 4 + 56 + 4 + 4 + 132 + 4 + 4 + 84
+ f->store_buffer((const uint8_t *)"hdrl", 4);
+ f->store_buffer((const uint8_t *)"avih", 4);
+ f->store_32(56);
+
+ f->store_32(1000000 / p_fps); // Microsecs per frame.
+ f->store_32(7000); // Max bytes per second
+ f->store_32(0); // Padding Granularity
+ f->store_32(16);
+ total_frames_ofs = f->get_position();
+ f->store_32(0); // Total frames (update later)
+ f->store_32(0); // Initial frames
+ f->store_32(1); // Streams
+ f->store_32(0); // Suggested buffer size
+ f->store_32(p_movie_size.width); // Movie Width
+ f->store_32(p_movie_size.height); // Movie Height
+ for (uint32_t i = 0; i < 4; i++) {
+ f->store_32(0); // Reserved.
+ }
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(132); // 4 + 4 + 4 + 48 + 4 + 4 + 40 + 4 + 4 + 16
+ f->store_buffer((const uint8_t *)"strl", 4);
+ f->store_buffer((const uint8_t *)"strh", 4);
+ f->store_32(48);
+ f->store_buffer((const uint8_t *)"vids", 4);
+ f->store_buffer((const uint8_t *)"MJPG", 4);
+ f->store_32(0); // Flags
+ f->store_16(0); // Priority
+ f->store_16(0); // Language
+ f->store_32(0); // Initial Frames
+ f->store_32(1); // Scale
+ f->store_32(p_fps); // FPS
+ f->store_32(0); // Start
+ total_frames_ofs2 = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+ f->store_32(0); // Suggested Buffer Size
+ f->store_32(0); // Quality
+ f->store_32(0); // Sample Size
+
+ f->store_buffer((const uint8_t *)"strf", 4);
+ f->store_32(40); // Size.
+ f->store_32(40); // Size.
+
+ f->store_32(p_movie_size.width); // Width
+ f->store_32(p_movie_size.height); // Width
+ f->store_16(1); // Planes
+ f->store_16(24); // Bitcount
+ f->store_buffer((const uint8_t *)"MJPG", 4); // Compression
+
+ f->store_32(((p_movie_size.width * 24 / 8 + 3) & 0xFFFFFFFC) * p_movie_size.height); // SizeImage
+ f->store_32(0); // XPelsXMeter
+ f->store_32(0); // YPelsXMeter
+ f->store_32(0); // ClrUsed
+ f->store_32(0); // ClrImportant
+
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(16);
+
+ f->store_buffer((const uint8_t *)"odml", 4);
+ f->store_buffer((const uint8_t *)"dmlh", 4);
+ f->store_32(4); // sizes
+
+ total_frames_ofs3 = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+
+ // Audio //
+
+ const uint32_t bit_depth = 32;
+ uint32_t channels = 2;
+ switch (speaker_mode) {
+ case AudioServer::SPEAKER_MODE_STEREO:
+ channels = 2;
+ break;
+ case AudioServer::SPEAKER_SURROUND_31:
+ channels = 4;
+ break;
+ case AudioServer::SPEAKER_SURROUND_51:
+ channels = 6;
+ break;
+ case AudioServer::SPEAKER_SURROUND_71:
+ channels = 8;
+ break;
+ }
+ uint32_t blockalign = bit_depth / 8 * channels;
+
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(84); // 4 + 4 + 4 + 48 + 4 + 4 + 16
+ f->store_buffer((const uint8_t *)"strl", 4);
+ f->store_buffer((const uint8_t *)"strh", 4);
+ f->store_32(48);
+ f->store_buffer((const uint8_t *)"auds", 4);
+ f->store_32(0); // Handler
+ f->store_32(0); // Flags
+ f->store_16(0); // Priority
+ f->store_16(0); // Language
+ f->store_32(0); // Initial Frames
+ f->store_32(blockalign); // Scale
+ f->store_32(mix_rate * blockalign); // mix rate
+ f->store_32(0); // Start
+ total_audio_frames_ofs4 = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+ f->store_32(12288); // Suggested Buffer Size
+ f->store_32(0xFFFFFFFF); // Quality
+ f->store_32(blockalign); // Block Align to 32 bits
+
+ audio_block_size = (mix_rate / fps) * blockalign;
+
+ f->store_buffer((const uint8_t *)"strf", 4);
+ f->store_32(16); // Standard format, no extra fields
+ f->store_16(1); // Compression code, standard PCM
+ f->store_16(channels);
+ f->store_32(mix_rate); // Samples (frames) / Sec
+ f->store_32(mix_rate * blockalign); // Bytes / sec
+ f->store_16(blockalign); // Bytes / sec
+ f->store_16(bit_depth); // Bytes / sec
+
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ movi_data_ofs = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+ f->store_buffer((const uint8_t *)"movi", 4);
+
+ return OK;
+}
+
+Error MovieWriterMJPEG::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
+ ERR_FAIL_COND_V(!f.is_valid(), ERR_UNCONFIGURED);
+
+ Vector<uint8_t> jpg_buffer = p_image->save_jpg_to_buffer(quality);
+ uint32_t s = jpg_buffer.size();
+
+ f->store_buffer((const uint8_t *)"00db", 4); // Stream 0, Video
+ f->store_32(jpg_buffer.size()); // sizes
+ f->store_buffer(jpg_buffer.ptr(), jpg_buffer.size());
+ if (jpg_buffer.size() & 1) {
+ f->store_8(0);
+ s++;
+ }
+ jpg_frame_sizes.push_back(s);
+
+ f->store_buffer((const uint8_t *)"01wb", 4); // Stream 1, Audio.
+ f->store_32(audio_block_size);
+ f->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
+
+ frame_count++;
+
+ return OK;
+}
+
+void MovieWriterMJPEG::write_end() {
+ if (f.is_valid()) {
+ // Finalize the file (frame indices)
+ f->store_buffer((const uint8_t *)"idx1", 4);
+ f->store_32(8 * 4 * frame_count);
+ uint32_t ofs = 4;
+ uint32_t all_data_size = 0;
+ for (uint32_t i = 0; i < frame_count; i++) {
+ f->store_buffer((const uint8_t *)"00db", 4);
+ f->store_32(16); // AVI_KEYFRAME
+ f->store_32(ofs);
+ f->store_32(jpg_frame_sizes[i]);
+
+ ofs += jpg_frame_sizes[i] + 8;
+
+ f->store_buffer((const uint8_t *)"01wb", 4);
+ f->store_32(16); // AVI_KEYFRAME
+ f->store_32(ofs);
+ f->store_32(audio_block_size);
+
+ ofs += audio_block_size + 8;
+ all_data_size += jpg_frame_sizes[i] + audio_block_size;
+ }
+
+ uint32_t file_size = f->get_position();
+ f->seek(4);
+ f->store_32(file_size - 78);
+ f->seek(total_frames_ofs);
+ f->store_32(frame_count);
+ f->seek(total_frames_ofs2);
+ f->store_32(frame_count);
+ f->seek(total_frames_ofs3);
+ f->store_32(frame_count);
+ f->seek(total_audio_frames_ofs4);
+ f->store_32(frame_count * mix_rate / fps);
+ f->seek(movi_data_ofs);
+ f->store_32(all_data_size + 4 + 16 * frame_count);
+
+ f.unref();
+ }
+}
+
+MovieWriterMJPEG::MovieWriterMJPEG() {
+ mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate_hz");
+ speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
+ quality = GLOBAL_GET("editor/movie_writer/mjpeg_quality");
+}
diff --git a/servers/movie_writer/movie_writer_mjpeg.h b/servers/movie_writer/movie_writer_mjpeg.h
new file mode 100644
index 0000000000..822bedfedf
--- /dev/null
+++ b/servers/movie_writer/movie_writer_mjpeg.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* movie_writer_mjpeg.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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. */
+/*************************************************************************/
+
+#ifndef MOVIE_WRITER_MJPEG_H
+#define MOVIE_WRITER_MJPEG_H
+
+#include "servers/movie_writer/movie_writer.h"
+
+class MovieWriterMJPEG : public MovieWriter {
+ GDCLASS(MovieWriterMJPEG, MovieWriter)
+
+ uint32_t mix_rate = 48000;
+ AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO;
+ String base_path;
+ uint32_t frame_count = 0;
+ uint32_t fps = 0;
+ float quality = 0.75;
+
+ uint32_t audio_block_size = 0;
+
+ Vector<uint32_t> jpg_frame_sizes;
+
+ uint64_t total_frames_ofs = 0;
+ uint64_t total_frames_ofs2 = 0;
+ uint64_t total_frames_ofs3 = 0;
+ uint64_t total_audio_frames_ofs4 = 0;
+ uint64_t movi_data_ofs = 0;
+
+ Ref<FileAccess> f;
+
+protected:
+ virtual uint32_t get_audio_mix_rate() const override;
+ virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override;
+ virtual void get_supported_extensions(List<String> *r_extensions) const override;
+
+ virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override;
+ virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override;
+ virtual void write_end() override;
+
+ virtual bool handles_file(const String &p_path) const override;
+
+public:
+ MovieWriterMJPEG();
+};
+
+#endif // MOVIE_WRITER_AVIJPEG_H
diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp
index ab323e76e8..0442089503 100644
--- a/servers/navigation_server_2d.cpp
+++ b/servers/navigation_server_2d.cpp
@@ -166,7 +166,7 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer2D::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer2D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer2D::map_get_edge_connection_margin);
- ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "layers"), &NavigationServer2D::map_get_path, DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer2D::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer2D::map_get_closest_point_owner);
@@ -180,8 +180,8 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer2D::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer2D::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer2D::region_get_map);
- ClassDB::bind_method(D_METHOD("region_set_layers", "region", "layers"), &NavigationServer2D::region_set_layers);
- ClassDB::bind_method(D_METHOD("region_get_layers", "region"), &NavigationServer2D::region_get_layers);
+ ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers);
+ ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navpoly", "region", "nav_poly"), &NavigationServer2D::region_set_navpoly);
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count);
@@ -250,8 +250,8 @@ void FORWARD_2_C(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, r
real_t FORWARD_1_C(region_get_travel_cost, RID, p_region, rid_to_rid);
void FORWARD_2_C(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
-void FORWARD_2_C(region_set_layers, RID, p_region, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
-uint32_t FORWARD_1_C(region_get_layers, RID, p_region, rid_to_rid);
+void FORWARD_2_C(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
+uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid);
void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
void NavigationServer2D::region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const {
diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h
index 8e285147b1..30f553d10b 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -75,7 +75,7 @@ public:
virtual real_t map_get_edge_connection_margin(RID p_map) const;
/// Returns the navigation path to reach the destination from the origin.
- virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
+ virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const;
virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const;
@@ -99,8 +99,8 @@ public:
virtual RID region_get_map(RID p_region) const;
/// Set the region's layers
- virtual void region_set_layers(RID p_region, uint32_t p_layers) const;
- virtual uint32_t region_get_layers(RID p_region) const;
+ virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) const;
+ virtual uint32_t region_get_navigation_layers(RID p_region) const;
/// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform2D p_transform) const;
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 6d99f6dfbb..60bbcec8d4 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -42,7 +42,7 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer3D::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer3D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer3D::map_get_edge_connection_margin);
- ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "layers"), &NavigationServer3D::map_get_path, DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer3D::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point_to_segment", "map", "start", "end", "use_collision"), &NavigationServer3D::map_get_closest_point_to_segment, DEFVAL(false));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer3D::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_normal", "map", "to_point"), &NavigationServer3D::map_get_closest_point_normal);
@@ -58,8 +58,8 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer3D::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer3D::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer3D::region_get_map);
- ClassDB::bind_method(D_METHOD("region_set_layers", "region", "layers"), &NavigationServer3D::region_set_layers);
- ClassDB::bind_method(D_METHOD("region_get_layers", "region"), &NavigationServer3D::region_get_layers);
+ ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer3D::region_set_navigation_layers);
+ ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer3D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer3D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navmesh", "region", "nav_mesh"), &NavigationServer3D::region_set_navmesh);
ClassDB::bind_method(D_METHOD("region_bake_navmesh", "mesh", "node"), &NavigationServer3D::region_bake_navmesh);
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 9b7e48e8d1..9c04d68622 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -84,7 +84,7 @@ public:
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
/// Returns the navigation path to reach the destination from the origin.
- virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigable_layers = 1) const = 0;
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0;
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const = 0;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const = 0;
@@ -110,8 +110,8 @@ public:
virtual RID region_get_map(RID p_region) const = 0;
/// Set the region's layers
- virtual void region_set_layers(RID p_region, uint32_t p_layers) const = 0;
- virtual uint32_t region_get_layers(RID p_region) const = 0;
+ virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) const = 0;
+ virtual uint32_t region_get_navigation_layers(RID p_region) const = 0;
/// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform3D p_transform) const = 0;
diff --git a/servers/physics_2d/godot_area_2d.h b/servers/physics_2d/godot_area_2d.h
index 35dad9d2c3..221982cf78 100644
--- a/servers/physics_2d/godot_area_2d.h
+++ b/servers/physics_2d/godot_area_2d.h
@@ -70,9 +70,9 @@ class GodotArea2D : public GodotCollisionObject2D {
static uint32_t hash(const BodyKey &p_key) {
uint32_t h = hash_one_uint64(p_key.rid.get_id());
- h = hash_djb2_one_64(p_key.instance_id, h);
- h = hash_djb2_one_32(p_key.area_shape, h);
- return hash_djb2_one_32(p_key.body_shape, h);
+ h = hash_murmur3_one_64(p_key.instance_id, h);
+ h = hash_murmur3_one_32(p_key.area_shape, h);
+ return hash_fmix32(hash_murmur3_one_32(p_key.body_shape, h));
}
_FORCE_INLINE_ bool operator==(const BodyKey &p_key) const {
diff --git a/servers/physics_3d/godot_area_3d.h b/servers/physics_3d/godot_area_3d.h
index a00451f602..51b435eb00 100644
--- a/servers/physics_3d/godot_area_3d.h
+++ b/servers/physics_3d/godot_area_3d.h
@@ -74,9 +74,9 @@ class GodotArea3D : public GodotCollisionObject3D {
static uint32_t hash(const BodyKey &p_key) {
uint32_t h = hash_one_uint64(p_key.rid.get_id());
- h = hash_djb2_one_64(p_key.instance_id, h);
- h = hash_djb2_one_32(p_key.area_shape, h);
- return hash_djb2_one_32(p_key.body_shape, h);
+ h = hash_murmur3_one_64(p_key.instance_id, h);
+ h = hash_murmur3_one_32(p_key.area_shape, h);
+ return hash_fmix32(hash_murmur3_one_32(p_key.body_shape, h));
}
_FORCE_INLINE_ bool operator==(const BodyKey &p_key) const {
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 8bbe987cb7..d7d2340119 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -57,6 +57,8 @@
#include "camera_server.h"
#include "debugger/servers_debugger.h"
#include "display_server.h"
+#include "movie_writer/movie_writer.h"
+#include "movie_writer/movie_writer_mjpeg.h"
#include "navigation_server_2d.h"
#include "navigation_server_3d.h"
#include "physics_2d/godot_physics_server_2d.h"
@@ -107,6 +109,9 @@ static bool has_server_feature_callback(const String &p_feature) {
return false;
}
+static MovieWriterMJPEG *writer_mjpeg = nullptr;
+static MovieWriterPNGWAV *writer_pngwav = nullptr;
+
void register_server_types() {
shader_types = memnew(ShaderTypes);
@@ -239,6 +244,8 @@ void register_server_types() {
GDREGISTER_CLASS(PhysicsTestMotionParameters3D);
GDREGISTER_CLASS(PhysicsTestMotionResult3D);
+ GDREGISTER_VIRTUAL_CLASS(MovieWriter);
+
ServersDebugger::initialize();
// Physics 2D
@@ -254,11 +261,19 @@ void register_server_types() {
PhysicsServer3DManager::register_server("GodotPhysics3D", &_createGodotPhysics3DCallback);
PhysicsServer3DManager::set_default_server("GodotPhysics3D");
+
+ writer_mjpeg = memnew(MovieWriterMJPEG);
+ MovieWriter::add_writer(writer_mjpeg);
+
+ writer_pngwav = memnew(MovieWriterPNGWAV);
+ MovieWriter::add_writer(writer_pngwav);
}
void unregister_server_types() {
ServersDebugger::deinitialize();
memdelete(shader_types);
+ memdelete(writer_mjpeg);
+ memdelete(writer_pngwav);
}
void register_server_singletons() {
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 2d3998bd90..d390614e53 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -692,6 +692,11 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat
projection = correction * p_render_data->view_projection[v];
RendererStorageRD::store_camera(projection, scene_state.ubo.projection_matrix_view[v]);
RendererStorageRD::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix_view[v]);
+
+ scene_state.ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x;
+ scene_state.ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y;
+ scene_state.ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z;
+ scene_state.ubo.eye_offset[v][3] = 0.0;
}
scene_state.ubo.taa_jitter[0] = p_render_data->taa_jitter.x;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 6ad42bf0ec..97f39164a4 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -237,6 +237,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
+ float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
float viewport_size[2];
float screen_pixel_size[2];
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index cfb30ef2f3..182aecd3be 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -621,7 +621,7 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
//builtins
- actions.renames["TIME"] = "scene_data_block.data.time";
+ actions.renames["TIME"] = "global_time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index b2e0af06cd..eae5685dd1 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -1542,6 +1542,11 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data,
projection = correction * p_render_data->view_projection[v];
RendererStorageRD::store_camera(projection, scene_state.ubo.projection_matrix_view[v]);
RendererStorageRD::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix_view[v]);
+
+ scene_state.ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x;
+ scene_state.ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y;
+ scene_state.ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z;
+ scene_state.ubo.eye_offset[v][3] = 0.0;
}
scene_state.ubo.z_far = p_render_data->z_far;
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index fc6f32ecb0..1b2df0ab9f 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -260,6 +260,7 @@ protected:
float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
+ float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
float viewport_size[2];
float screen_pixel_size[2];
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index ef959bc3c6..a50a05d905 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -5115,12 +5115,12 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.cam_projection = p_camera_data->main_projection;
- render_data.view_projection[0] = p_camera_data->main_projection;
render_data.cam_orthogonal = p_camera_data->is_orthogonal;
render_data.taa_jitter = p_camera_data->taa_jitter;
render_data.view_count = p_camera_data->view_count;
for (uint32_t v = 0; v < p_camera_data->view_count; v++) {
+ render_data.view_eye_offset[v] = p_camera_data->view_offset[v].origin;
render_data.view_projection[v] = p_camera_data->view_projection[v];
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 1b1df6469e..a90c165d83 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -58,6 +58,7 @@ struct RenderDataRD {
// For stereo rendering
uint32_t view_count = 1;
+ Vector3 view_eye_offset[RendererSceneRender::MAX_RENDER_VIEWS];
CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
Transform3D prev_cam_transform;
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index a8ccdea60b..62a7b0e7d7 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -448,6 +448,11 @@ void main() {
// Early Tonemap & SRGB Conversion
#ifndef SUBPASS
+ if (params.use_fxaa) {
+ // FXAA must be performed before glow to preserve the "bleed" effect of glow.
+ color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
+ }
+
if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier;
if (params.glow_map_strength > 0.001) {
@@ -455,10 +460,6 @@ void main() {
}
color.rgb = mix(color.rgb, glow, params.glow_intensity);
}
-
- if (params.use_fxaa) {
- color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
- }
#endif
if (params.use_debanding) {
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index 92c4fc3d67..268e1dd7d0 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -96,6 +96,8 @@ layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms
} material;
#endif
+float global_time;
+
#ifdef MODE_DUAL_PARABOLOID
layout(location = 9) out float dp_clip;
@@ -386,9 +388,12 @@ void main() {
mat4 model_matrix = instances.data[instance_index].transform;
#if defined(MOTION_VECTORS)
+ global_time = scene_data_block.prev_data.time;
vertex_shader(instance_index, is_multimesh, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
+ global_time = scene_data_block.data.time;
vertex_shader(instance_index, is_multimesh, scene_data_block.data, model_matrix, screen_position);
#else
+ global_time = scene_data_block.data.time;
vec4 screen_position;
vertex_shader(instance_index, is_multimesh, scene_data_block.data, model_matrix, screen_position);
#endif
@@ -486,6 +491,8 @@ layout(location = 10) in flat uint instance_index_interp;
#define inv_projection_matrix scene_data.inv_projection_matrix
#endif
+#define global_time scene_data_block.data.time
+
#if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE)
//both required for transmittance to be enabled
#define LIGHT_TRANSMITTANCE_USED
@@ -634,7 +641,11 @@ void fragment_shader(in SceneData scene_data) {
//lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
+#ifdef USE_MULTIVIEW
+ vec3 view = -normalize(vertex_interp - scene_data.eye_offset[ViewIndex].xyz);
+#else
vec3 view = -normalize(vertex_interp);
+#endif
vec3 albedo = vec3(1.0);
vec3 backlight = vec3(0.0);
vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0);
@@ -1191,7 +1202,7 @@ void fragment_shader(in SceneData scene_data) {
if (sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_VOXEL_GI)) { // process voxel_gi_instances
uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
- vec3 ref_vec = normalize(reflect(normalize(vertex), normal));
+ vec3 ref_vec = normalize(reflect(-view, normal));
//find arbitrary tangent and bitangent, then build a matrix
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, normal));
@@ -1309,7 +1320,7 @@ void fragment_shader(in SceneData scene_data) {
#else
vec3 bent_normal = normal;
#endif
- reflection_process(reflection_index, vertex, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
+ reflection_process(reflection_index, view, vertex, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
}
}
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl
index b700e21543..32ea83397a 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl
@@ -180,6 +180,7 @@ struct SceneData {
// only used for multiview
mat4 projection_matrix_view[MAX_VIEWS];
mat4 inv_projection_matrix_view[MAX_VIEWS];
+ vec4 eye_offset[MAX_VIEWS];
vec2 viewport_size;
vec2 screen_pixel_size;
@@ -250,7 +251,7 @@ struct SceneData {
bool pancake_shadows;
vec2 taa_jitter;
- uvec2 pad;
+ uvec2 pad2;
};
layout(set = 1, binding = 0, std140) uniform SceneDataBlock {
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 5a308bbd02..c92b29b14a 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -869,7 +869,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
diffuse_light, specular_light);
}
-void reflection_process(uint ref_index, vec3 vertex, vec3 normal, float roughness, vec3 ambient_light, vec3 specular_light, inout vec4 ambient_accum, inout vec4 reflection_accum) {
+void reflection_process(uint ref_index, vec3 view, vec3 vertex, vec3 normal, float roughness, vec3 ambient_light, vec3 specular_light, inout vec4 ambient_accum, inout vec4 reflection_accum) {
vec3 box_extents = reflections.data[ref_index].box_extents;
vec3 local_pos = (reflections.data[ref_index].local_matrix * vec4(vertex, 1.0)).xyz;
@@ -877,7 +877,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 normal, float roughnes
return;
}
- vec3 ref_vec = normalize(reflect(vertex, normal));
+ vec3 ref_vec = normalize(reflect(-view, normal));
vec3 inner_pos = abs(local_pos / box_extents);
float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
index e15ebbfc91..26d0de46c2 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
@@ -114,9 +114,9 @@ invariant gl_Position;
#GLOBALS
-void main() {
- SceneData scene_data = scene_data_block.data;
+#define scene_data scene_data_block.data
+void main() {
vec4 instance_custom = vec4(0.0);
#if defined(COLOR_USED)
color_interp = color_attrib;
@@ -576,17 +576,22 @@ vec4 fog_process(vec3 vertex) {
#endif //!MODE_RENDER DEPTH
+#define scene_data scene_data_block.data
+
void main() {
#ifdef MODE_DUAL_PARABOLOID
if (dp_clip > 0.0)
discard;
#endif
- SceneData scene_data = scene_data_block.data;
//lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
+#ifdef USE_MULTIVIEW
+ vec3 view = -normalize(vertex_interp - scene_data.eye_offset[ViewIndex].xyz);
+#else
vec3 view = -normalize(vertex_interp);
+#endif
vec3 albedo = vec3(1.0);
vec3 backlight = vec3(0.0);
vec4 transmittance_color = vec4(0.0);
@@ -1051,7 +1056,7 @@ void main() {
#else
vec3 bent_normal = normal;
#endif
- reflection_process(reflection_index, vertex, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
+ reflection_process(reflection_index, view, vertex, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
}
if (reflection_accum.a > 0.0) {
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl
index dd14a15837..7413d8730a 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl
@@ -134,6 +134,7 @@ struct SceneData {
// only used for multiview
highp mat4 projection_matrix_view[MAX_VIEWS];
highp mat4 inv_projection_matrix_view[MAX_VIEWS];
+ highp vec4 eye_offset[MAX_VIEWS];
highp vec2 viewport_size;
highp vec2 screen_pixel_size;
diff --git a/servers/rendering/renderer_rd/uniform_set_cache_rd.h b/servers/rendering/renderer_rd/uniform_set_cache_rd.h
index e49cf4dafa..af22a48716 100644
--- a/servers/rendering/renderer_rd/uniform_set_cache_rd.h
+++ b/servers/rendering/renderer_rd/uniform_set_cache_rd.h
@@ -57,13 +57,13 @@ class UniformSetCacheRD : public Object {
Cache *hash_table[HASH_TABLE_SIZE] = {};
static _FORCE_INLINE_ uint32_t _hash_uniform(const RD::Uniform &u, uint32_t h) {
- h = hash_djb2_one_32(u.uniform_type, h);
- h = hash_djb2_one_32(u.binding, h);
+ h = hash_murmur3_one_32(u.uniform_type, h);
+ h = hash_murmur3_one_32(u.binding, h);
uint32_t rsize = u.get_id_count();
for (uint32_t j = 0; j < rsize; j++) {
- h = hash_djb2_one_64(u.get_id(j).get_id(), h);
+ h = hash_murmur3_one_64(u.get_id(j).get_id(), h);
}
- return h;
+ return hash_fmix32(h);
}
static _FORCE_INLINE_ bool _compare_uniform(const RD::Uniform &a, const RD::Uniform &b) {
@@ -154,8 +154,8 @@ class UniformSetCacheRD : public Object {
public:
template <typename... Args>
RID get_cache(RID p_shader, uint32_t p_set, Args... args) {
- uint32_t h = hash_djb2_one_64(p_shader.get_id());
- h = hash_djb2_one_32(p_set, h);
+ uint32_t h = hash_murmur3_one_64(p_shader.get_id());
+ h = hash_murmur3_one_32(p_set, h);
h = _hash_args(h, args...);
uint32_t table_idx = h % HASH_TABLE_SIZE;
@@ -180,12 +180,14 @@ public:
template <typename... Args>
RID get_cache_vec(RID p_shader, uint32_t p_set, const Vector<RD::Uniform> &p_uniforms) {
- uint32_t h = hash_djb2_one_64(p_shader.get_id());
- h = hash_djb2_one_32(p_set, h);
+ uint32_t h = hash_murmur3_one_64(p_shader.get_id());
+ h = hash_murmur3_one_32(p_set, h);
for (int i = 0; i < p_uniforms.size(); i++) {
h = _hash_uniform(p_uniforms[i], h);
}
+ h = hash_fmix32(h);
+
uint32_t table_idx = h % HASH_TABLE_SIZE;
{
const Cache *c = hash_table[table_idx];
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 2e0c4e0f79..ab9ee2067f 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -1193,6 +1193,20 @@ void RendererViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::V
RSG::texture_storage->render_target_set_sdf_size_and_scale(viewport->render_target, p_size, p_scale);
}
+RID RendererViewport::viewport_find_from_screen_attachment(DisplayServer::WindowID p_id) const {
+ RID *rids = nullptr;
+ uint32_t rid_count = viewport_owner.get_rid_count();
+ rids = (RID *)alloca(sizeof(RID *) * rid_count);
+ viewport_owner.fill_owned_buffer(rids);
+ for (uint32_t i = 0; i < rid_count; i++) {
+ Viewport *viewport = viewport_owner.get_or_null(rids[i]);
+ if (viewport->viewport_to_screen == p_id) {
+ return rids[i];
+ }
+ }
+ return RID();
+}
+
bool RendererViewport::free(RID p_rid) {
if (viewport_owner.owns(p_rid)) {
Viewport *viewport = viewport_owner.get_or_null(p_rid);
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index 18385d1613..acab8063d1 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -282,6 +282,8 @@ public:
void viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::ViewportSDFOversize p_over_size, RS::ViewportSDFScale p_scale);
+ virtual RID viewport_find_from_screen_attachment(DisplayServer::WindowID p_id = DisplayServer::MAIN_WINDOW_ID) const;
+
void handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time);
void set_default_clear_color(const Color &p_color);
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 718f20f80a..3813db4b72 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -333,13 +333,10 @@ void RenderingServerDefault::_thread_exit() {
}
void RenderingServerDefault::_thread_draw(bool p_swap_buffers, double frame_step) {
- if (!draw_pending.decrement()) {
- _draw(p_swap_buffers, frame_step);
- }
+ _draw(p_swap_buffers, frame_step);
}
void RenderingServerDefault::_thread_flush() {
- draw_pending.decrement();
}
void RenderingServerDefault::_thread_callback(void *_instance) {
@@ -370,7 +367,6 @@ void RenderingServerDefault::_thread_loop() {
void RenderingServerDefault::sync() {
if (create_thread) {
- draw_pending.increment();
command_queue.push_and_sync(this, &RenderingServerDefault::_thread_flush);
} else {
command_queue.flush_all(); //flush all pending from other threads
@@ -379,7 +375,6 @@ void RenderingServerDefault::sync() {
void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
if (create_thread) {
- draw_pending.increment();
command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step);
} else {
_draw(p_swap_buffers, frame_step);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 6008f2b00e..2829dae837 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -84,7 +84,6 @@ class RenderingServerDefault : public RenderingServer {
SafeFlag draw_thread_up;
bool create_thread;
- SafeNumeric<uint64_t> draw_pending;
void _thread_draw(bool p_swap_buffers, double frame_step);
void _thread_flush();
@@ -629,6 +628,7 @@ public:
FUNC2(viewport_set_measure_render_time, RID, bool)
FUNC1RC(double, viewport_get_measured_render_time_cpu, RID)
FUNC1RC(double, viewport_get_measured_render_time_gpu, RID)
+ FUNC1RC(RID, viewport_find_from_screen_attachment, DisplayServer::WindowID)
FUNC2(call_set_vsync_mode, DisplayServer::VSyncMode, DisplayServer::WindowID)
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 26ab8b659e..9b407043fc 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2934,10 +2934,10 @@ void RenderingServer::init() {
ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/screen_space_roughness_limiter/amount", PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01"));
ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/screen_space_roughness_limiter/limit", PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"));
- GLOBAL_DEF_RST("rendering/scaling_3d/mode", 0);
- GLOBAL_DEF_RST("rendering/scaling_3d/scale", 1.0);
- GLOBAL_DEF_RST("rendering/scaling_3d/fsr_sharpness", 0.2f);
- GLOBAL_DEF_RST("rendering/scaling_3d/fsr_mipmap_bias", 0.0f);
+ GLOBAL_DEF("rendering/scaling_3d/mode", 0);
+ GLOBAL_DEF("rendering/scaling_3d/scale", 1.0);
+ GLOBAL_DEF("rendering/scaling_3d/fsr_sharpness", 0.2f);
+ GLOBAL_DEF("rendering/scaling_3d/fsr_mipmap_bias", 0.0f);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/scaling_3d/mode",
PropertyInfo(Variant::INT,
"rendering/scaling_3d/mode",
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 39484e532a..ccef95f5f2 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -944,6 +944,8 @@ public:
virtual double viewport_get_measured_render_time_cpu(RID p_viewport) const = 0;
virtual double viewport_get_measured_render_time_gpu(RID p_viewport) const = 0;
+ virtual RID viewport_find_from_screen_attachment(DisplayServer::WindowID p_id = DisplayServer::MAIN_WINDOW_ID) const = 0;
+
/* SKY API */
enum SkyMode {
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 41d9c74d82..fe5ade88a1 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -413,7 +413,7 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_glyphs", "shaped"), &TextServer::_shaped_text_get_ellipsis_glyphs_wrapper);
ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_glyph_count", "shaped"), &TextServer::shaped_text_get_ellipsis_glyph_count);
- ClassDB::bind_method(D_METHOD("shaped_text_overrun_trim_to_width", "shaped", "width", "overrun_trim_flags"), &TextServer::shaped_text_overrun_trim_to_width, DEFVAL(0), DEFVAL(OVERRUN_NO_TRIMMING));
+ ClassDB::bind_method(D_METHOD("shaped_text_overrun_trim_to_width", "shaped", "width", "overrun_trim_flags"), &TextServer::shaped_text_overrun_trim_to_width, DEFVAL(0), DEFVAL(OVERRUN_NO_TRIM));
ClassDB::bind_method(D_METHOD("shaped_text_get_objects", "shaped"), &TextServer::shaped_text_get_objects);
ClassDB::bind_method(D_METHOD("shaped_text_get_object_rect", "shaped", "key"), &TextServer::shaped_text_get_object_rect);
@@ -470,6 +470,12 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(JUSTIFICATION_AFTER_LAST_TAB);
BIND_ENUM_CONSTANT(JUSTIFICATION_CONSTRAIN_ELLIPSIS);
+ /* AutowrapMode */
+ BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
+ BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART);
+
/* LineBreakFlag */
BIND_ENUM_CONSTANT(BREAK_NONE);
BIND_ENUM_CONSTANT(BREAK_MANDATORY);
@@ -477,8 +483,22 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(BREAK_GRAPHEME_BOUND);
BIND_ENUM_CONSTANT(BREAK_WORD_BOUND_ADAPTIVE);
- /* TextOverrunFlag */
+ /* VisibleCharactersBehavior */
+ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
+ BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_LTR);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_RTL);
+
+ /* OverrunBehavior */
BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
+
+ /* TextOverrunFlag */
+ BIND_ENUM_CONSTANT(OVERRUN_NO_TRIM);
BIND_ENUM_CONSTANT(OVERRUN_TRIM);
BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ONLY);
BIND_ENUM_CONSTANT(OVERRUN_ADD_ELLIPSIS);
diff --git a/servers/text_server.h b/servers/text_server.h
index a67ad8fc9e..6339dde0ea 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -64,6 +64,21 @@ public:
JUSTIFICATION_CONSTRAIN_ELLIPSIS = 1 << 4,
};
+ enum VisibleCharactersBehavior {
+ VC_CHARS_BEFORE_SHAPING,
+ VC_CHARS_AFTER_SHAPING,
+ VC_GLYPHS_AUTO,
+ VC_GLYPHS_LTR,
+ VC_GLYPHS_RTL,
+ };
+
+ enum AutowrapMode {
+ AUTOWRAP_OFF,
+ AUTOWRAP_ARBITRARY,
+ AUTOWRAP_WORD,
+ AUTOWRAP_WORD_SMART
+ };
+
enum LineBreakFlag { // LineBreakFlag can be passed in the same value as the JustificationFlag, do not use the same values.
BREAK_NONE = 0,
BREAK_MANDATORY = 1 << 5,
@@ -72,8 +87,16 @@ public:
BREAK_WORD_BOUND_ADAPTIVE = 1 << 6 | 1 << 8,
};
+ enum OverrunBehavior {
+ OVERRUN_NO_TRIMMING,
+ OVERRUN_TRIM_CHAR,
+ OVERRUN_TRIM_WORD,
+ OVERRUN_TRIM_ELLIPSIS,
+ OVERRUN_TRIM_WORD_ELLIPSIS,
+ };
+
enum TextOverrunFlag {
- OVERRUN_NO_TRIMMING = 0,
+ OVERRUN_NO_TRIM = 0,
OVERRUN_TRIM = 1 << 0,
OVERRUN_TRIM_WORD_ONLY = 1 << 1,
OVERRUN_ADD_ELLIPSIS = 1 << 2,
@@ -522,6 +545,9 @@ public:
#define TS TextServerManager::get_singleton()->get_primary_interface()
+VARIANT_ENUM_CAST(TextServer::VisibleCharactersBehavior);
+VARIANT_ENUM_CAST(TextServer::AutowrapMode);
+VARIANT_ENUM_CAST(TextServer::OverrunBehavior);
VARIANT_ENUM_CAST(TextServer::Direction);
VARIANT_ENUM_CAST(TextServer::Orientation);
VARIANT_ENUM_CAST(TextServer::JustificationFlag);