diff options
Diffstat (limited to 'servers')
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); |