diff options
Diffstat (limited to 'servers')
-rw-r--r-- | servers/audio/audio_stream.cpp | 370 | ||||
-rw-r--r-- | servers/audio/audio_stream.h | 77 | ||||
-rw-r--r-- | servers/register_server_types.cpp | 2 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/effects_rd.cpp | 20 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/effects_rd.h | 2 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_storage_rd.cpp | 43 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_storage_rd.h | 7 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/shaders/copy.glsl | 91 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl | 3 | ||||
-rw-r--r-- | servers/rendering_server.cpp | 2 | ||||
-rw-r--r-- | servers/rendering_server.h | 2 |
11 files changed, 447 insertions, 172 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 170a7ef967..39060286a4 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -351,100 +351,372 @@ AudioStreamPlaybackMicrophone::AudioStreamPlaybackMicrophone() { //////////////////////////////// -void AudioStreamRandomPitch::set_audio_stream(const Ref<AudioStream> &p_audio_stream) { - audio_stream = p_audio_stream; - if (audio_stream.is_valid()) { - for (Set<AudioStreamPlaybackRandomPitch *>::Element *E = playbacks.front(); E; E = E->next()) { - E->get()->playback = audio_stream->instance_playback(); - } +void AudioStreamRandomizer::add_stream(int p_index) { + if (p_index < 0) { + p_index = audio_stream_pool.size(); + } + ERR_FAIL_COND(p_index > audio_stream_pool.size()); + PoolEntry entry{ nullptr, 1.0f }; + audio_stream_pool.insert(p_index, entry); + emit_signal(SNAME("changed")); + notify_property_list_changed(); +} + +void AudioStreamRandomizer::move_stream(int p_index_from, int p_index_to) { + ERR_FAIL_COND(p_index_from < 0); + ERR_FAIL_COND(p_index_from >= audio_stream_pool.size()); + ERR_FAIL_COND(p_index_to < 0); + ERR_FAIL_COND(p_index_to > audio_stream_pool.size()); + audio_stream_pool.insert(p_index_to, audio_stream_pool[p_index_from]); + // If 'from' is strictly after 'to' we need to increment the index by one because of the insertion. + if (p_index_from > p_index_to) { + p_index_from++; } + audio_stream_pool.remove_at(p_index_from); + emit_signal(SNAME("changed")); + notify_property_list_changed(); +} + +void AudioStreamRandomizer::remove_stream(int p_index) { + ERR_FAIL_COND(p_index < 0); + ERR_FAIL_COND(p_index >= audio_stream_pool.size()); + audio_stream_pool.remove_at(p_index); + emit_signal(SNAME("changed")); + notify_property_list_changed(); +} + +void AudioStreamRandomizer::set_stream(int p_index, Ref<AudioStream> p_stream) { + ERR_FAIL_COND(p_index < 0); + ERR_FAIL_COND(p_index >= audio_stream_pool.size()); + audio_stream_pool.write[p_index].stream = p_stream; + emit_signal(SNAME("changed")); } -Ref<AudioStream> AudioStreamRandomPitch::get_audio_stream() const { - return audio_stream; +Ref<AudioStream> AudioStreamRandomizer::get_stream(int p_index) const { + ERR_FAIL_COND_V(p_index < 0, nullptr); + ERR_FAIL_COND_V(p_index >= audio_stream_pool.size(), nullptr); + return audio_stream_pool[p_index].stream; } -void AudioStreamRandomPitch::set_random_pitch(float p_pitch) { +void AudioStreamRandomizer::set_stream_probability_weight(int p_index, float p_weight) { + ERR_FAIL_COND(p_index < 0); + ERR_FAIL_COND(p_index >= audio_stream_pool.size()); + audio_stream_pool.write[p_index].weight = p_weight; + emit_signal(SNAME("changed")); +} + +float AudioStreamRandomizer::get_stream_probability_weight(int p_index) const { + ERR_FAIL_COND_V(p_index < 0, 0); + ERR_FAIL_COND_V(p_index >= audio_stream_pool.size(), 0); + return audio_stream_pool[p_index].weight; +} + +void AudioStreamRandomizer::set_streams_count(int p_count) { + audio_stream_pool.resize(p_count); +} + +int AudioStreamRandomizer::get_streams_count() const { + return audio_stream_pool.size(); +} + +void AudioStreamRandomizer::set_random_pitch(float p_pitch) { if (p_pitch < 1) { p_pitch = 1; } - random_pitch = p_pitch; + random_pitch_scale = p_pitch; +} + +float AudioStreamRandomizer::get_random_pitch() const { + return random_pitch_scale; +} + +void AudioStreamRandomizer::set_random_volume_offset_db(float p_volume_offset_db) { + if (p_volume_offset_db < 0) { + p_volume_offset_db = 0; + } + random_volume_offset_db = p_volume_offset_db; +} + +float AudioStreamRandomizer::get_random_volume_offset_db() const { + return random_volume_offset_db; } -float AudioStreamRandomPitch::get_random_pitch() const { - return random_pitch; +void AudioStreamRandomizer::set_playback_mode(PlaybackMode p_playback_mode) { + playback_mode = p_playback_mode; } -Ref<AudioStreamPlayback> AudioStreamRandomPitch::instance_playback() { - Ref<AudioStreamPlaybackRandomPitch> playback; +AudioStreamRandomizer::PlaybackMode AudioStreamRandomizer::get_playback_mode() const { + return playback_mode; +} + +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() { + Ref<AudioStreamPlaybackRandomizer> playback; playback.instantiate(); - if (audio_stream.is_valid()) { - playback->playback = audio_stream->instance_playback(); + playbacks.insert(playback.ptr()); + playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this); + + double total_weight = 0; + Vector<PoolEntry> local_pool; + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream.is_valid() && entry.weight > 0) { + local_pool.push_back(entry); + total_weight += entry.weight; + } + } + if (local_pool.is_empty()) { + return playback; } + double chosen_cumulative_weight = Math::random(0.0, total_weight); + double cumulative_weight = 0; + for (PoolEntry &entry : local_pool) { + cumulative_weight += entry.weight; + if (cumulative_weight > chosen_cumulative_weight) { + playback->playback = entry.stream->instance_playback(); + last_playback = entry.stream; + break; + } + } + if (playback->playback.is_null()) { + // This indicates a floating point error. Take the last element. + last_playback = local_pool[local_pool.size() - 1].stream; + playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback(); + } + return playback; +} + +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_no_repeats() { + Ref<AudioStreamPlaybackRandomizer> playback; + double total_weight = 0; + Vector<PoolEntry> local_pool; + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream == last_playback) { + continue; + } + if (entry.stream.is_valid() && entry.weight > 0) { + local_pool.push_back(entry); + total_weight += entry.weight; + } + } + if (local_pool.is_empty()) { + playback = instance_playback_random(); + WARN_PRINT("Playback stream pool is too small to prevent repeats."); + return playback; + } + + playback.instantiate(); playbacks.insert(playback.ptr()); - playback->random_pitch = Ref<AudioStreamRandomPitch>((AudioStreamRandomPitch *)this); + playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this); + double chosen_cumulative_weight = Math::random(0.0, total_weight); + double cumulative_weight = 0; + for (PoolEntry &entry : local_pool) { + cumulative_weight += entry.weight; + if (cumulative_weight > chosen_cumulative_weight) { + last_playback = entry.stream; + playback->playback = entry.stream->instance_playback(); + break; + } + } + if (playback->playback.is_null()) { + // This indicates a floating point error. Take the last element. + last_playback = local_pool[local_pool.size() - 1].stream; + playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback(); + } return playback; } -String AudioStreamRandomPitch::get_stream_name() const { - if (audio_stream.is_valid()) { - return "Random: " + audio_stream->get_name(); +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() { + Ref<AudioStreamPlaybackRandomizer> playback; + playback.instantiate(); + playbacks.insert(playback.ptr()); + playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this); + + Vector<Ref<AudioStream>> local_pool; + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream.is_null()) { + continue; + } + if (local_pool.find(entry.stream) != -1) { + WARN_PRINT("Duplicate stream in sequential playback pool"); + continue; + } + local_pool.push_back(entry.stream); + } + if (local_pool.is_empty()) { + return playback; } - return "RandomPitch"; + bool found_last_stream = false; + for (Ref<AudioStream> &entry : local_pool) { + if (found_last_stream) { + last_playback = entry; + playback->playback = entry->instance_playback(); + break; + } + if (entry == last_playback) { + found_last_stream = true; + } + } + if (playback->playback.is_null()) { + // Wrap around + last_playback = local_pool[0]; + playback->playback = local_pool.write[0]->instance_playback(); + } + return playback; } -float AudioStreamRandomPitch::get_length() const { - if (audio_stream.is_valid()) { - return audio_stream->get_length(); +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback() { + switch (playback_mode) { + case PLAYBACK_RANDOM: + return instance_playback_random(); + case PLAYBACK_RANDOM_NO_REPEATS: + return instance_playback_no_repeats(); + case PLAYBACK_SEQUENTIAL: + return instance_playback_sequential(); + default: + ERR_FAIL_V_MSG(nullptr, "Unhandled playback mode."); } +} +String AudioStreamRandomizer::get_stream_name() const { + return "Randomizer"; +} + +float AudioStreamRandomizer::get_length() const { return 0; } -bool AudioStreamRandomPitch::is_monophonic() const { - if (audio_stream.is_valid()) { - return audio_stream->is_monophonic(); +bool AudioStreamRandomizer::is_monophonic() const { + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream.is_valid() && entry.stream->is_monophonic()) { + return true; + } + } + return false; +} + +bool AudioStreamRandomizer::_get(const StringName &p_name, Variant &r_ret) const { + if (AudioStream::_get(p_name, r_ret)) { + return true; } + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) { + int index = components[0].trim_prefix("stream_").to_int(); + if (index < 0 || index >= (int)audio_stream_pool.size()) { + return false; + } - return true; // It doesn't really matter what we return here, but no sense instancing a many playbacks of a null stream. + if (components[1] == "stream") { + r_ret = get_stream(index); + return true; + } else if (components[1] == "weight") { + r_ret = get_stream_probability_weight(index); + return true; + } else { + return false; + } + } + return false; +} + +bool AudioStreamRandomizer::_set(const StringName &p_name, const Variant &p_value) { + if (AudioStream::_set(p_name, p_value)) { + return true; + } + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) { + int index = components[0].trim_prefix("stream_").to_int(); + if (index < 0 || index >= (int)audio_stream_pool.size()) { + return false; + } + + if (components[1] == "stream") { + set_stream(index, p_value); + return true; + } else if (components[1] == "weight") { + set_stream_probability_weight(index, p_value); + return true; + } else { + return false; + } + } + return false; +} + +void AudioStreamRandomizer::_get_property_list(List<PropertyInfo> *p_list) const { + AudioStream::_get_property_list(p_list); // Define the trivial scalar properties. + p_list->push_back(PropertyInfo(Variant::NIL, "Streams", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < audio_stream_pool.size(); i++) { + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("stream_%d/stream", i), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("stream_%d/weight", i), PROPERTY_HINT_RANGE, "0,100,0.001,or_greater")); + } } -void AudioStreamRandomPitch::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream); - ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream); +void AudioStreamRandomizer::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_stream", "index"), &AudioStreamRandomizer::add_stream); + ClassDB::bind_method(D_METHOD("move_stream", "index_from", "index_to"), &AudioStreamRandomizer::move_stream); + ClassDB::bind_method(D_METHOD("remove_stream", "index"), &AudioStreamRandomizer::remove_stream); + + ClassDB::bind_method(D_METHOD("set_stream", "index", "stream"), &AudioStreamRandomizer::set_stream); + ClassDB::bind_method(D_METHOD("get_stream", "index"), &AudioStreamRandomizer::get_stream); + ClassDB::bind_method(D_METHOD("set_stream_probability_weight", "index", "weight"), &AudioStreamRandomizer::set_stream_probability_weight); + ClassDB::bind_method(D_METHOD("get_stream_probability_weight", "index"), &AudioStreamRandomizer::get_stream_probability_weight); + + ClassDB::bind_method(D_METHOD("set_streams_count", "count"), &AudioStreamRandomizer::set_streams_count); + ClassDB::bind_method(D_METHOD("get_streams_count"), &AudioStreamRandomizer::get_streams_count); + + ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomizer::set_random_pitch); + ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomizer::get_random_pitch); - ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomPitch::set_random_pitch); - ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomPitch::get_random_pitch); + ClassDB::bind_method(D_METHOD("set_random_volume_offset_db", "db_offset"), &AudioStreamRandomizer::set_random_volume_offset_db); + ClassDB::bind_method(D_METHOD("get_random_volume_offset_db"), &AudioStreamRandomizer::get_random_volume_offset_db); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "audio_stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_audio_stream", "get_audio_stream"); + ClassDB::bind_method(D_METHOD("set_playback_mode", "mode"), &AudioStreamRandomizer::set_playback_mode); + ClassDB::bind_method(D_METHOD("get_playback_mode"), &AudioStreamRandomizer::get_playback_mode); + + ADD_ARRAY("streams", "stream_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "streams_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_streams_count", "get_streams_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_mode", PROPERTY_HINT_ENUM, "Random (Avoid Repeats),Random,Sequential"), "set_playback_mode", "get_playback_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_pitch", PROPERTY_HINT_RANGE, "1,16,0.01"), "set_random_pitch", "get_random_pitch"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_volume_offset_db", PROPERTY_HINT_RANGE, "0,40,0"), "set_random_volume_offset_db", "get_random_volume_offset_db"); + + BIND_ENUM_CONSTANT(PLAYBACK_RANDOM_NO_REPEATS); + BIND_ENUM_CONSTANT(PLAYBACK_RANDOM); + BIND_ENUM_CONSTANT(PLAYBACK_SEQUENTIAL); } -AudioStreamRandomPitch::AudioStreamRandomPitch() { - random_pitch = 1.1; +AudioStreamRandomizer::AudioStreamRandomizer() { + random_pitch_scale = 1.1; + random_volume_offset_db = 5; } -void AudioStreamPlaybackRandomPitch::start(float p_from_pos) { +void AudioStreamPlaybackRandomizer::start(float p_from_pos) { playing = playback; - float range_from = 1.0 / random_pitch->random_pitch; - float range_to = random_pitch->random_pitch; + { + float range_from = 1.0 / randomizer->random_pitch_scale; + float range_to = randomizer->random_pitch_scale; - pitch_scale = range_from + Math::randf() * (range_to - range_from); + pitch_scale = range_from + Math::randf() * (range_to - range_from); + } + { + float range_from = -randomizer->random_volume_offset_db; + float range_to = randomizer->random_volume_offset_db; + + float volume_offset_db = range_from + Math::randf() * (range_to - range_from); + volume_scale = Math::db2linear(volume_offset_db); + } if (playing.is_valid()) { playing->start(p_from_pos); } } -void AudioStreamPlaybackRandomPitch::stop() { +void AudioStreamPlaybackRandomizer::stop() { if (playing.is_valid()) { playing->stop(); - ; } } -bool AudioStreamPlaybackRandomPitch::is_playing() const { +bool AudioStreamPlaybackRandomizer::is_playing() const { if (playing.is_valid()) { return playing->is_playing(); } @@ -452,7 +724,7 @@ bool AudioStreamPlaybackRandomPitch::is_playing() const { return false; } -int AudioStreamPlaybackRandomPitch::get_loop_count() const { +int AudioStreamPlaybackRandomizer::get_loop_count() const { if (playing.is_valid()) { return playing->get_loop_count(); } @@ -460,7 +732,7 @@ int AudioStreamPlaybackRandomPitch::get_loop_count() const { return 0; } -float AudioStreamPlaybackRandomPitch::get_playback_position() const { +float AudioStreamPlaybackRandomizer::get_playback_position() const { if (playing.is_valid()) { return playing->get_playback_position(); } @@ -468,13 +740,13 @@ float AudioStreamPlaybackRandomPitch::get_playback_position() const { return 0; } -void AudioStreamPlaybackRandomPitch::seek(float p_time) { +void AudioStreamPlaybackRandomizer::seek(float p_time) { if (playing.is_valid()) { playing->seek(p_time); } } -int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackRandomizer::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { if (playing.is_valid()) { return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); } else { @@ -485,7 +757,7 @@ int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale } } -AudioStreamPlaybackRandomPitch::~AudioStreamPlaybackRandomPitch() { - random_pitch->playbacks.erase(this); +AudioStreamPlaybackRandomizer::~AudioStreamPlaybackRandomizer() { + randomizer->playbacks.erase(this); } ///////////////////////////////////////////// diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 32159e96ef..ce9bcabb9c 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -167,43 +167,86 @@ public: // -class AudioStreamPlaybackRandomPitch; +class AudioStreamPlaybackRandomizer; -class AudioStreamRandomPitch : public AudioStream { - GDCLASS(AudioStreamRandomPitch, AudioStream); - friend class AudioStreamPlaybackRandomPitch; +class AudioStreamRandomizer : public AudioStream { + GDCLASS(AudioStreamRandomizer, AudioStream); - Set<AudioStreamPlaybackRandomPitch *> playbacks; - Ref<AudioStream> audio_stream; - float random_pitch; +public: + enum PlaybackMode { + PLAYBACK_RANDOM_NO_REPEATS, + PLAYBACK_RANDOM, + PLAYBACK_SEQUENTIAL, + }; + +private: + friend class AudioStreamPlaybackRandomizer; + + struct PoolEntry { + Ref<AudioStream> stream; + float weight; + }; + + Set<AudioStreamPlaybackRandomizer *> playbacks; + Vector<PoolEntry> audio_stream_pool; + float random_pitch_scale; + float random_volume_offset_db; + + Ref<AudioStreamPlayback> instance_playback_random(); + Ref<AudioStreamPlayback> instance_playback_no_repeats(); + Ref<AudioStreamPlayback> instance_playback_sequential(); + + Ref<AudioStream> last_playback = nullptr; + PlaybackMode playback_mode = PLAYBACK_RANDOM_NO_REPEATS; protected: static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: - void set_audio_stream(const Ref<AudioStream> &p_audio_stream); - Ref<AudioStream> get_audio_stream() const; + void add_stream(int p_index); + void move_stream(int p_index_from, int p_index_to); + void remove_stream(int p_index); + + void set_stream(int p_index, Ref<AudioStream> p_stream); + Ref<AudioStream> get_stream(int p_index) const; + void set_stream_probability_weight(int p_index, float p_weight); + float get_stream_probability_weight(int p_index) const; - void set_random_pitch(float p_pitch); + void set_streams_count(int p_count); + int get_streams_count() const; + + void set_random_pitch(float p_pitch_scale); float get_random_pitch() const; + void set_random_volume_offset_db(float p_volume_offset_db); + float get_random_volume_offset_db() const; + + void set_playback_mode(PlaybackMode p_playback_mode); + PlaybackMode get_playback_mode() const; + virtual Ref<AudioStreamPlayback> instance_playback() override; virtual String get_stream_name() const override; virtual float get_length() const override; //if supported, otherwise return 0 virtual bool is_monophonic() const override; - AudioStreamRandomPitch(); + AudioStreamRandomizer(); }; -class AudioStreamPlaybackRandomPitch : public AudioStreamPlayback { - GDCLASS(AudioStreamPlaybackRandomPitch, AudioStreamPlayback); - friend class AudioStreamRandomPitch; +class AudioStreamPlaybackRandomizer : public AudioStreamPlayback { + GDCLASS(AudioStreamPlaybackRandomizer, AudioStreamPlayback); + friend class AudioStreamRandomizer; - Ref<AudioStreamRandomPitch> random_pitch; + Ref<AudioStreamRandomizer> randomizer; Ref<AudioStreamPlayback> playback; Ref<AudioStreamPlayback> playing; + float pitch_scale; + float volume_scale; public: virtual void start(float p_from_pos = 0.0) override; @@ -217,7 +260,9 @@ public: virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; - ~AudioStreamPlaybackRandomPitch(); + ~AudioStreamPlaybackRandomizer(); }; +VARIANT_ENUM_CAST(AudioStreamRandomizer::PlaybackMode); + #endif // AUDIO_STREAM_H diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index ed654013ef..f405dea770 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -141,7 +141,7 @@ void register_server_types() { GDREGISTER_CLASS(AudioStreamPlayback); GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackResampled); GDREGISTER_CLASS(AudioStreamMicrophone); - GDREGISTER_CLASS(AudioStreamRandomPitch); + GDREGISTER_CLASS(AudioStreamRandomizer); GDREGISTER_VIRTUAL_CLASS(AudioEffect); GDREGISTER_VIRTUAL_CLASS(AudioEffectInstance); GDREGISTER_CLASS(AudioEffectEQ); diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 7183fd110f..02a0b6f184 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -482,12 +482,11 @@ void EffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i RD::get_singleton()->compute_list_end(); } -void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst) { +void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst) { ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer."); memset(©.push_constant, 0, sizeof(CopyPushConstant)); - uint32_t base_flags = 0; copy.push_constant.section[0] = p_region.position.x; copy.push_constant.section[1] = p_region.position.y; copy.push_constant.section[2] = p_region.size.width; @@ -497,23 +496,12 @@ void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back RD::DrawListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_GAUSSIAN_COPY_8BIT : COPY_MODE_GAUSSIAN_COPY]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_back_texture), 3); - - copy.push_constant.flags = base_flags | COPY_FLAG_HORIZONTAL; - RD::get_singleton()->compute_list_set_push_constant(compute_list, ©.push_constant, sizeof(CopyPushConstant)); - - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1); - - RD::get_singleton()->compute_list_add_barrier(compute_list); - - //VERTICAL - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_back_texture), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_texture), 3); - copy.push_constant.flags = base_flags; RD::get_singleton()->compute_list_set_push_constant(compute_list, ©.push_constant, sizeof(CopyPushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1); + RD::get_singleton()->compute_list_end(); } @@ -2343,8 +2331,8 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { Vector<String> copy_modes; copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define DST_IMAGE_8BIT\n"); - copy_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); - copy_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); + copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n"); + copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n"); copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n#define DST_IMAGE_8BIT\n"); copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n"); diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index 1a080756a8..f5e5b1ace7 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -899,7 +899,7 @@ public: void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false); void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far); void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false); - void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false); + void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false); void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false); void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); void gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 145c4f902e..f345001539 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -7516,10 +7516,6 @@ void RendererStorageRD::_clear_render_target(RenderTarget *rt) { if (rt->backbuffer.is_valid()) { RD::get_singleton()->free(rt->backbuffer); rt->backbuffer = RID(); - for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) { - //just erase copies, since the rest are erased by dependency - RD::get_singleton()->free(rt->backbuffer_mipmaps[i].mipmap_copy); - } rt->backbuffer_mipmaps.clear(); rt->backbuffer_uniform_set = RID(); //chain deleted } @@ -7636,7 +7632,9 @@ void RendererStorageRD::_create_render_target_backbuffer(RenderTarget *rt) { tf.mipmaps = mipmaps_required; rt->backbuffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + RD::get_singleton()->set_resource_name(rt->backbuffer, "Render Target Back Buffer"); rt->backbuffer_mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0); + RD::get_singleton()->set_resource_name(rt->backbuffer_mipmap0, "Back Buffer slice mipmap 0"); { Vector<RID> fb_tex; @@ -7651,23 +7649,10 @@ void RendererStorageRD::_create_render_target_backbuffer(RenderTarget *rt) { } //create mipmaps for (uint32_t i = 1; i < mipmaps_required; i++) { - RenderTarget::BackbufferMipmap mm; - { - mm.mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i); - } - - { - Size2 mm_size = Image::get_image_mipmap_size(tf.width, tf.height, Image::FORMAT_RGBA8, i); + RID mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i); + RD::get_singleton()->set_resource_name(mipmap, "Back Buffer slice mip: " + itos(i)); - RD::TextureFormat mmtf = tf; - mmtf.width = mm_size.width; - mmtf.height = mm_size.height; - mmtf.mipmaps = 1; - - mm.mipmap_copy = RD::get_singleton()->texture_create(mmtf, RD::TextureView()); - } - - rt->backbuffer_mipmaps.push_back(mm); + rt->backbuffer_mipmaps.push_back(mipmap); } } @@ -8111,7 +8096,7 @@ void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, c if (!p_gen_mipmaps) { return; } - + RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps"); //then mipmap blur RID prev_texture = rt->color; //use color, not backbuffer, as bb has mipmaps. @@ -8121,10 +8106,11 @@ void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, c region.size.x = MAX(1, region.size.x >> 1); region.size.y = MAX(1, region.size.y >> 1); - const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i]; - effects->gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true); - prev_texture = mm.mipmap; + RID mipmap = rt->backbuffer_mipmaps[i]; + effects->gaussian_blur(prev_texture, mipmap, region, true); + prev_texture = mipmap; } + RD::get_singleton()->draw_command_end_label(); } void RendererStorageRD::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) { @@ -8164,7 +8150,7 @@ void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_targe return; //nothing to do } } - + RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps2"); //then mipmap blur RID prev_texture = rt->backbuffer_mipmap0; @@ -8174,10 +8160,11 @@ void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_targe region.size.x = MAX(1, region.size.x >> 1); region.size.y = MAX(1, region.size.y >> 1); - const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i]; - effects->gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true); - prev_texture = mm.mipmap; + RID mipmap = rt->backbuffer_mipmaps[i]; + effects->gaussian_blur(prev_texture, mipmap, region, true); + prev_texture = mipmap; } + RD::get_singleton()->draw_command_end_label(); } RID RendererStorageRD::render_target_get_framebuffer_uniform_set(RID p_render_target) { diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 43bbcf6520..6febb71e93 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -1169,12 +1169,7 @@ private: RID backbuffer_fb; RID backbuffer_mipmap0; - struct BackbufferMipmap { - RID mipmap; - RID mipmap_copy; - }; - - Vector<BackbufferMipmap> backbuffer_mipmaps; + Vector<RID> backbuffer_mipmaps; RID framebuffer_uniform_set; RID backbuffer_uniform_set; diff --git a/servers/rendering/renderer_rd/shaders/copy.glsl b/servers/rendering/renderer_rd/shaders/copy.glsl index d4d0ed0f56..ecf7bb9817 100644 --- a/servers/rendering/renderer_rd/shaders/copy.glsl +++ b/servers/rendering/renderer_rd/shaders/copy.glsl @@ -61,7 +61,7 @@ layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buff layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer; #endif -#ifdef MODE_GAUSSIAN_GLOW +#ifdef MODE_GAUSSIAN_BLUR shared vec4 local_cache[256]; shared vec4 temp_cache[128]; #endif @@ -70,7 +70,7 @@ void main() { // Pixel being shaded ivec2 pos = ivec2(gl_GlobalInvocationID.xy); -#ifndef MODE_GAUSSIAN_GLOW // Glow needs the extra threads +#ifndef MODE_GAUSSIAN_BLUR // Gaussian blur needs the extra threads if (any(greaterThanEqual(pos, params.section.zw))) { //too large, do nothing return; } @@ -92,35 +92,11 @@ void main() { #ifdef MODE_GAUSSIAN_BLUR - //Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect - - if (bool(params.flags & FLAG_HORIZONTAL)) { - ivec2 base_pos = (pos + params.section.xy) << 1; - vec4 color = texelFetch(source_color, base_pos + ivec2(0, 0), 0) * 0.214607; - color += texelFetch(source_color, base_pos + ivec2(1, 0), 0) * 0.189879; - color += texelFetch(source_color, base_pos + ivec2(2, 0), 0) * 0.131514; - color += texelFetch(source_color, base_pos + ivec2(3, 0), 0) * 0.071303; - color += texelFetch(source_color, base_pos + ivec2(-1, 0), 0) * 0.189879; - color += texelFetch(source_color, base_pos + ivec2(-2, 0), 0) * 0.131514; - color += texelFetch(source_color, base_pos + ivec2(-3, 0), 0) * 0.071303; - imageStore(dest_buffer, pos + params.target, color); - } else { - ivec2 base_pos = (pos + params.section.xy); - vec4 color = texelFetch(source_color, base_pos + ivec2(0, 0), 0) * 0.38774; - color += texelFetch(source_color, base_pos + ivec2(0, 1), 0) * 0.24477; - color += texelFetch(source_color, base_pos + ivec2(0, 2), 0) * 0.06136; - color += texelFetch(source_color, base_pos + ivec2(0, -1), 0) * 0.24477; - color += texelFetch(source_color, base_pos + ivec2(0, -2), 0) * 0.06136; - imageStore(dest_buffer, pos + params.target, color); - } -#endif - -#ifdef MODE_GAUSSIAN_GLOW - // First pass copy texture into 16x16 local memory for every 8x8 thread block vec2 quad_center_uv = clamp(vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw)); uint dest_index = gl_LocalInvocationID.x * 2 + gl_LocalInvocationID.y * 2 * 16; +#ifdef MODE_GLOW if (bool(params.flags & FLAG_HIGH_QUALITY_GLOW)) { vec2 quad_offset_uv = clamp((vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.0)) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw)); @@ -128,12 +104,15 @@ void main() { local_cache[dest_index + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.z, 0.0), 0)) * 0.5; local_cache[dest_index + 16] = (textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0) + textureLod(source_color, quad_offset_uv + vec2(0.0, 1.0 / params.section.w), 0)) * 0.5; local_cache[dest_index + 16 + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.zw), 0)) * 0.5; - } else { + } else +#endif + { local_cache[dest_index] = textureLod(source_color, quad_center_uv, 0); local_cache[dest_index + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0); local_cache[dest_index + 16] = textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0); local_cache[dest_index + 16 + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0); } +#ifdef MODE_GLOW if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) { // Tonemap initial samples to reduce weight of fireflies: https://graphicrants.blogspot.com/2013/12/tone-mapping.html local_cache[dest_index] /= 1.0 + dot(local_cache[dest_index].rgb, vec3(0.299, 0.587, 0.114)); @@ -141,29 +120,33 @@ void main() { local_cache[dest_index + 16] /= 1.0 + dot(local_cache[dest_index + 16].rgb, vec3(0.299, 0.587, 0.114)); local_cache[dest_index + 16 + 1] /= 1.0 + dot(local_cache[dest_index + 16 + 1].rgb, vec3(0.299, 0.587, 0.114)); } - + const float kernel[4] = { 0.174938, 0.165569, 0.140367, 0.106595 }; +#else + // Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect. + const float kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 }; +#endif memoryBarrierShared(); barrier(); // Horizontal pass. Needs to copy into 8x16 chunk of local memory so vertical pass has full resolution uint read_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 32 + 4; vec4 color_top = vec4(0.0); - color_top += local_cache[read_index] * 0.174938; - color_top += local_cache[read_index + 1] * 0.165569; - color_top += local_cache[read_index + 2] * 0.140367; - color_top += local_cache[read_index + 3] * 0.106595; - color_top += local_cache[read_index - 1] * 0.165569; - color_top += local_cache[read_index - 2] * 0.140367; - color_top += local_cache[read_index - 3] * 0.106595; + color_top += local_cache[read_index] * kernel[0]; + color_top += local_cache[read_index + 1] * kernel[1]; + color_top += local_cache[read_index + 2] * kernel[2]; + color_top += local_cache[read_index + 3] * kernel[3]; + color_top += local_cache[read_index - 1] * kernel[1]; + color_top += local_cache[read_index - 2] * kernel[2]; + color_top += local_cache[read_index - 3] * kernel[3]; vec4 color_bottom = vec4(0.0); - color_bottom += local_cache[read_index + 16] * 0.174938; - color_bottom += local_cache[read_index + 1 + 16] * 0.165569; - color_bottom += local_cache[read_index + 2 + 16] * 0.140367; - color_bottom += local_cache[read_index + 3 + 16] * 0.106595; - color_bottom += local_cache[read_index - 1 + 16] * 0.165569; - color_bottom += local_cache[read_index - 2 + 16] * 0.140367; - color_bottom += local_cache[read_index - 3 + 16] * 0.106595; + color_bottom += local_cache[read_index + 16] * kernel[0]; + color_bottom += local_cache[read_index + 1 + 16] * kernel[1]; + color_bottom += local_cache[read_index + 2 + 16] * kernel[2]; + color_bottom += local_cache[read_index + 3 + 16] * kernel[3]; + color_bottom += local_cache[read_index - 1 + 16] * kernel[1]; + color_bottom += local_cache[read_index - 2 + 16] * kernel[2]; + color_bottom += local_cache[read_index - 3 + 16] * kernel[3]; // rotate samples to take advantage of cache coherency uint write_index = gl_LocalInvocationID.y * 2 + gl_LocalInvocationID.x * 16; @@ -174,18 +157,24 @@ void main() { memoryBarrierShared(); barrier(); + // If destination outside of texture, can stop doing work now + if (any(greaterThanEqual(pos, params.section.zw))) { + return; + } + // Vertical pass uint index = gl_LocalInvocationID.y + gl_LocalInvocationID.x * 16 + 4; vec4 color = vec4(0.0); - color += temp_cache[index] * 0.174938; - color += temp_cache[index + 1] * 0.165569; - color += temp_cache[index + 2] * 0.140367; - color += temp_cache[index + 3] * 0.106595; - color += temp_cache[index - 1] * 0.165569; - color += temp_cache[index - 2] * 0.140367; - color += temp_cache[index - 3] * 0.106595; + color += temp_cache[index] * kernel[0]; + color += temp_cache[index + 1] * kernel[1]; + color += temp_cache[index + 2] * kernel[2]; + color += temp_cache[index + 3] * kernel[3]; + color += temp_cache[index - 1] * kernel[1]; + color += temp_cache[index - 2] * kernel[2]; + color += temp_cache[index - 3] * kernel[3]; +#ifdef MODE_GLOW if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) { // Undo tonemap to restore range: https://graphicrants.blogspot.com/2013/12/tone-mapping.html color /= 1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114)); @@ -205,7 +194,7 @@ void main() { color = min(color * feedback, vec4(params.glow_luminance_cap)); } - +#endif imageStore(dest_buffer, pos + params.target, color); #endif diff --git a/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl index fde5688301..57349e55b1 100644 --- a/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl @@ -190,8 +190,7 @@ void main() { } vec2 final_pos; - float grad; - grad = steps_taken / float(params.num_steps); + float grad = (steps_taken + 1.0) / float(params.num_steps); float initial_fade = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in); float fade = pow(clamp(1.0 - grad, 0.0, 1.0), params.distance_fade) * initial_fade; final_pos = pos; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 584abbc351..ac181cb5eb 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -933,7 +933,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa } } - ERR_FAIL_COND_V_MSG((bsformat & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK) != (format & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK), ERR_INVALID_PARAMETER, "Blend shape format must match the main array format for Vertex, Normal and Tangent arrays."); + ERR_FAIL_COND_V_MSG(bsformat != (format & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK), ERR_INVALID_PARAMETER, "Blend shape format must match the main array format for Vertex, Normal and Tangent arrays."); } } diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 472fff1bf1..d21f3a3299 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -259,7 +259,7 @@ public: ARRAY_FORMAT_WEIGHTS = 1 << ARRAY_WEIGHTS, ARRAY_FORMAT_INDEX = 1 << ARRAY_INDEX, - ARRAY_FORMAT_BLEND_SHAPE_MASK = (~(ARRAY_FORMAT_COLOR | ARRAY_FORMAT_TEX_UV | ARRAY_FORMAT_TEX_UV2 | ARRAY_FORMAT_BONES | ARRAY_FORMAT_WEIGHTS | ARRAY_FORMAT_CUSTOM0 | ARRAY_FORMAT_CUSTOM1 | ARRAY_FORMAT_CUSTOM2 | ARRAY_FORMAT_CUSTOM3 | ARRAY_FORMAT_INDEX)) & 0x7FFFFFFF, + ARRAY_FORMAT_BLEND_SHAPE_MASK = ARRAY_FORMAT_VERTEX | ARRAY_FORMAT_NORMAL | ARRAY_FORMAT_TANGENT, ARRAY_FORMAT_CUSTOM_BASE = (ARRAY_INDEX + 1), ARRAY_FORMAT_CUSTOM_BITS = 3, |