diff options
Diffstat (limited to 'servers')
101 files changed, 3663 insertions, 1603 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 5544a09ac0..e1b391b823 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -74,11 +74,13 @@ void AudioStreamPlayback::seek(float p_time) { } } -void AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { - if (GDVIRTUAL_CALL(_mix, p_buffer, p_rate_scale, p_frames)) { - return; +int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { + int ret; + if (GDVIRTUAL_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret)) { + return ret; } WARN_PRINT_ONCE("AudioStreamPlayback::mix unimplemented!"); + return 0; } void AudioStreamPlayback::_bind_methods() { @@ -103,12 +105,14 @@ void AudioStreamPlaybackResampled::_begin_resample() { mix_offset = 0; } -void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { float target_rate = AudioServer::get_singleton()->get_mix_rate(); float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale(); uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale * playback_speed_scale) / double(target_rate)) * double(FP_LEN)); + int mixed_frames_total = p_frames; + for (int i = 0; i < p_frames; i++) { uint32_t idx = CUBIC_INTERP_HISTORY + uint32_t(mix_offset >> FP_BITS); //standard cubic interpolation (great quality/performance ratio) @@ -119,6 +123,11 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, AudioFrame y2 = internal_buffer[idx - 1]; AudioFrame y3 = internal_buffer[idx - 0]; + if (idx <= internal_buffer_end && idx >= internal_buffer_end && mixed_frames_total == p_frames) { + // The internal buffer ends somewhere in this range, and we haven't yet recorded the number of good frames we have. + mixed_frames_total = i; + } + float mu2 = mu * mu; AudioFrame a0 = 3 * y1 - 3 * y2 + y3 - y0; AudioFrame a1 = 2 * y0 - 5 * y1 + 4 * y2 - y3; @@ -135,7 +144,14 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, internal_buffer[2] = internal_buffer[INTERNAL_BUFFER_LEN + 2]; internal_buffer[3] = internal_buffer[INTERNAL_BUFFER_LEN + 3]; if (is_playing()) { - _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN); + int mixed_frames = _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN); + if (mixed_frames != INTERNAL_BUFFER_LEN) { + // internal_buffer[mixed_frames] is the first frame of silence. + internal_buffer_end = mixed_frames; + } else { + // The internal buffer does not contain the first frame of silence. + internal_buffer_end = -1; + } } else { //fill with silence, not playing for (int j = 0; j < INTERNAL_BUFFER_LEN; ++j) { @@ -145,6 +161,7 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, mix_offset -= (INTERNAL_BUFFER_LEN << FP_BITS); } } + return mixed_frames_total; } //////////////////////////////// @@ -210,7 +227,7 @@ void AudioStreamMicrophone::_bind_methods() { AudioStreamMicrophone::AudioStreamMicrophone() { } -void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) { +int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) { AudioDriver::get_singleton()->lock(); Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer(); @@ -221,6 +238,8 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr unsigned int input_position = AudioDriver::get_singleton()->get_input_position(); #endif + int mixed_frames = p_frames; + if (playback_delay > input_size) { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0.0f, 0.0f); @@ -240,6 +259,9 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr p_buffer[i] = AudioFrame(l, r); } else { + if (mixed_frames == p_frames) { + mixed_frames = i; + } p_buffer[i] = AudioFrame(0.0f, 0.0f); } } @@ -252,10 +274,12 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr #endif AudioDriver::get_singleton()->unlock(); + + return mixed_frames; } -void AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { - AudioStreamPlaybackResampled::mix(p_buffer, p_rate_scale, p_frames); +int AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { + return AudioStreamPlaybackResampled::mix(p_buffer, p_rate_scale, p_frames); } float AudioStreamPlaybackMicrophone::get_stream_sampling_rate() { @@ -428,13 +452,14 @@ void AudioStreamPlaybackRandomPitch::seek(float p_time) { } } -void AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { if (playing.is_valid()) { - playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); + return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); } else { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); } + return p_frames; } } diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 25f0017211..922335508e 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -51,7 +51,7 @@ protected: GDVIRTUAL0RC(int, _get_loop_count) GDVIRTUAL0RC(float, _get_playback_position) GDVIRTUAL1(_seek, float) - GDVIRTUAL3(_mix, GDNativePtr<AudioFrame>, float, int) + GDVIRTUAL3R(int, _mix, GDNativePtr<AudioFrame>, float, int) public: virtual void start(float p_from_pos = 0.0); virtual void stop(); @@ -62,7 +62,7 @@ public: virtual float get_playback_position() const; virtual void seek(float p_time); - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); }; class AudioStreamPlaybackResampled : public AudioStreamPlayback { @@ -77,15 +77,17 @@ class AudioStreamPlaybackResampled : public AudioStreamPlayback { }; AudioFrame internal_buffer[INTERNAL_BUFFER_LEN + CUBIC_INTERP_HISTORY]; + unsigned int internal_buffer_end = -1; uint64_t mix_offset; protected: void _begin_resample(); - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) = 0; + // Returns the number of frames that were mixed. + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) = 0; virtual float get_stream_sampling_rate() = 0; public: - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; AudioStreamPlaybackResampled() { mix_offset = 0; } }; @@ -140,11 +142,11 @@ class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled { Ref<AudioStreamMicrophone> microphone; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; public: - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; virtual void start(float p_from_pos = 0.0) override; virtual void stop() override; @@ -208,7 +210,7 @@ public: virtual float get_playback_position() const override; virtual void seek(float p_time) override; - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; ~AudioStreamPlaybackRandomPitch(); }; diff --git a/servers/audio/effects/audio_effect_capture.h b/servers/audio/effects/audio_effect_capture.h index 7f50fc4965..bb1d03be8c 100644 --- a/servers/audio/effects/audio_effect_capture.h +++ b/servers/audio/effects/audio_effect_capture.h @@ -34,6 +34,7 @@ #include "core/config/engine.h" #include "core/math/audio_frame.h" #include "core/object/ref_counted.h" +#include "core/templates/ring_buffer.h" #include "core/templates/vector.h" #include "servers/audio/audio_effect.h" #include "servers/audio_server.h" diff --git a/servers/audio/effects/audio_stream_generator.cpp b/servers/audio/effects/audio_stream_generator.cpp index bced2997ce..edb5c6d2dd 100644 --- a/servers/audio/effects/audio_stream_generator.cpp +++ b/servers/audio/effects/audio_stream_generator.cpp @@ -138,7 +138,7 @@ void AudioStreamGeneratorPlayback::clear_buffer() { mixed = 0; } -void AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) { +int AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) { int read_amount = buffer.data_left(); if (p_frames < read_amount) { read_amount = p_frames; @@ -156,6 +156,7 @@ void AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_fra } mixed += p_frames / generator->get_mix_rate(); + return read_amount < p_frames ? read_amount : p_frames; } float AudioStreamGeneratorPlayback::get_stream_sampling_rate() { diff --git a/servers/audio/effects/audio_stream_generator.h b/servers/audio/effects/audio_stream_generator.h index 5d46771f4d..6bec744081 100644 --- a/servers/audio/effects/audio_stream_generator.h +++ b/servers/audio/effects/audio_stream_generator.h @@ -67,7 +67,7 @@ class AudioStreamGeneratorPlayback : public AudioStreamPlaybackResampled { AudioStreamGenerator *generator; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; static void _bind_methods(); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 4c54188cb2..81735d522f 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -32,13 +32,19 @@ #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" +#include "core/error/error_macros.h" #include "core/io/file_access.h" #include "core/io/resource_loader.h" +#include "core/math/audio_frame.h" #include "core/os/os.h" +#include "core/string/string_name.h" +#include "core/templates/pair.h" #include "scene/resources/audio_stream_sample.h" #include "servers/audio/audio_driver_dummy.h" #include "servers/audio/effects/audio_effect_compressor.h" +#include <cstring> + #ifdef TOOLS_ENABLED #define MARK_EDITED set_edited(true); #else @@ -234,6 +240,7 @@ AudioDriver *AudioDriverManager::get_driver(int p_driver) { ////////////////////////////////////////////// void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) { + mix_count++; int todo = p_frames; #ifdef DEBUG_ENABLED @@ -331,10 +338,156 @@ void AudioServer::_mix_step() { bus->soloed = false; } } + for (CallbackItem *ci : mix_callback_list) { + ci->callback(ci->userdata); + } + + for (AudioStreamPlaybackListNode *playback : playback_list) { + // Paused streams are no-ops. Don't even mix audio from the stream playback. + if (playback->state.load() == AudioStreamPlaybackListNode::PAUSED) { + continue; + } + + bool fading_out = playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION || playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE; + + AudioFrame *buf = mix_buffer.ptrw(); + + // Copy the lookeahead buffer into the mix buffer. + for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) { + buf[i] = playback->lookahead[i]; + } + + // Mix the audio stream + unsigned int mixed_frames = playback->stream_playback->mix(&buf[LOOKAHEAD_BUFFER_SIZE], playback->pitch_scale.get(), buffer_size); + + if (mixed_frames != buffer_size) { + // We know we have at least the size of our lookahead buffer for fade-out purposes. + + float fadeout_base = 0.87; + float fadeout_coefficient = 1; + static_assert(LOOKAHEAD_BUFFER_SIZE == 32, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE."); + // 0.87 ^ 32 = 0.0116. There might still be a pop but it'll be way better than if we didn't do this. + for (unsigned int idx = mixed_frames; idx < buffer_size; idx++) { + fadeout_coefficient *= fadeout_base; + buf[idx] *= fadeout_coefficient; + } + AudioStreamPlaybackListNode::PlaybackState new_state; + new_state = AudioStreamPlaybackListNode::AWAITING_DELETION; + playback->state.store(new_state); + } else { + // Move the last little bit of what we just mixed into our lookahead buffer. + for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) { + playback->lookahead[i] = buf[buffer_size + i]; + } + } + + ERR_FAIL_COND(playback->bus_details.load() == nullptr); + // By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix. + AudioStreamPlaybackBusDetails *bus_details = nullptr; + { + std::atomic<AudioStreamPlaybackBusDetails *> bus_details_atomic = nullptr; + bus_details = playback->bus_details.exchange(bus_details_atomic); + } + ERR_FAIL_COND(bus_details == nullptr); + AudioStreamPlaybackBusDetails *prev_bus_details = playback->prev_bus_details; + + // Mix to any active buses. + for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) { + if (!bus_details->bus_active[idx]) { + continue; + } + int bus_idx = thread_find_bus_index(bus_details->bus[idx]); + + int prev_bus_idx = -1; + for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) { + if (!prev_bus_details->bus_active[search_idx]) { + continue; + } + if (prev_bus_details->bus[search_idx].hash() == bus_details->bus[idx].hash()) { + prev_bus_idx = search_idx; + } + } + + for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) { + AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx); + if (fading_out) { + bus_details->volume[idx][channel_idx] = AudioFrame(0, 0); + } + AudioFrame channel_vol = bus_details->volume[idx][channel_idx]; + + AudioFrame prev_channel_vol = AudioFrame(0, 0); + if (prev_bus_idx != -1) { + prev_channel_vol = prev_bus_details->volume[prev_bus_idx][channel_idx]; + } + _mix_step_for_channel(channel_buf, buf, prev_channel_vol, channel_vol, playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]); + } + } + + // Now go through and fade-out any buses that were being played to previously that we missed by going through current data. + for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) { + if (!prev_bus_details->bus_active[idx]) { + continue; + } + int bus_idx = thread_find_bus_index(prev_bus_details->bus[idx]); + + int current_bus_idx = -1; + for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) { + if (bus_details->bus[search_idx] == prev_bus_details->bus[idx]) { + current_bus_idx = search_idx; + } + } + if (current_bus_idx != -1) { + // If we found a corresponding bus in the current bus assignments, we've already mixed to this bus. + continue; + } + + for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) { + AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx); + AudioFrame prev_channel_vol = prev_bus_details->volume[idx][channel_idx]; + // Fade out to silence + _mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]); + } + } + + // Copy the bus details we mixed with to the previous bus details to maintain volume ramps. + std::copy(std::begin(bus_details->bus_active), std::end(bus_details->bus_active), std::begin(prev_bus_details->bus_active)); + std::copy(std::begin(bus_details->bus), std::end(bus_details->bus), std::begin(prev_bus_details->bus)); + for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) { + std::copy(std::begin(bus_details->volume[bus_idx]), std::end(bus_details->volume[bus_idx]), std::begin(prev_bus_details->volume[bus_idx])); + } + + AudioStreamPlaybackBusDetails *bus_details_expected = nullptr; + // Only put the bus details pointer back if it hasn't been updated already. + if (!playback->bus_details.compare_exchange_strong(/* expected= */ bus_details_expected, /* new= */ bus_details)) { + // If it *has* been updated already, queue the old one for deletion. + bus_details_graveyard.insert(bus_details); + } - //make callbacks for mixing the audio - for (Set<CallbackItem>::Element *E = callbacks.front(); E; E = E->next()) { - E->get().callback(E->get().userdata); + switch (playback->state.load()) { + case AudioStreamPlaybackListNode::AWAITING_DELETION: + case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION: + playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) { + if (p->prev_bus_details) + delete p->prev_bus_details; + if (p->bus_details) + delete p->bus_details; + p->stream_playback.unref(); + delete p; + }); + break; + case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: { + // Pause the stream. + AudioStreamPlaybackListNode::PlaybackState old_state, new_state; + do { + old_state = playback->state.load(); + new_state = AudioStreamPlaybackListNode::PAUSED; + } while (!playback->state.compare_exchange_strong(/* expected= */ old_state, new_state)); + } break; + case AudioStreamPlaybackListNode::PLAYING: + case AudioStreamPlaybackListNode::PAUSED: + // No-op! + break; + } } for (int i = buses.size() - 1; i >= 0; i--) { @@ -464,6 +617,53 @@ void AudioServer::_mix_step() { to_mix = buffer_size; } +void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_source_buf, AudioFrame p_vol_start, AudioFrame p_vol_final, float p_attenuation_filter_cutoff_hz, float p_highshelf_gain, AudioFilterSW::Processor *p_processor_l, AudioFilterSW::Processor *p_processor_r) { + if (p_highshelf_gain != 0) { + AudioFilterSW filter; + filter.set_mode(AudioFilterSW::HIGHSHELF); + filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate()); + filter.set_cutoff(p_attenuation_filter_cutoff_hz); + filter.set_resonance(1); + filter.set_stages(1); + filter.set_gain(p_highshelf_gain); + + ERR_FAIL_COND(p_processor_l == nullptr); + ERR_FAIL_COND(p_processor_r == nullptr); + + bool is_just_started = p_vol_start.l == 0 && p_vol_start.r == 0; + p_processor_l->set_filter(&filter, /* clear_history= */ is_just_started); + p_processor_l->update_coeffs(buffer_size); + p_processor_r->set_filter(&filter, /* clear_history= */ is_just_started); + p_processor_r->update_coeffs(buffer_size); + + for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) { + // Make this buffer size invariant if buffer_size ever becomes a project setting. + float lerp_param = (float)frame_idx / buffer_size; + AudioFrame vol = p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start; + AudioFrame mixed = vol * p_source_buf[frame_idx]; + p_processor_l->process_one_interp(mixed.l); + p_processor_r->process_one_interp(mixed.r); + p_out_buf[frame_idx] += mixed; + } + + } else { + for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) { + // Make this buffer size invariant if buffer_size ever becomes a project setting. + float lerp_param = (float)frame_idx / buffer_size; + p_out_buf[frame_idx] += (p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start) * p_source_buf[frame_idx]; + } + } +} + +AudioServer::AudioStreamPlaybackListNode *AudioServer::_find_playback_list_node(Ref<AudioStreamPlayback> p_playback) { + for (AudioStreamPlaybackListNode *playback_list_node : playback_list) { + if (playback_list_node->stream_playback == p_playback) { + return playback_list_node; + } + } + return nullptr; +} + bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const { if (p_bus < 0 || p_bus >= buses.size()) { return false; @@ -923,9 +1123,216 @@ float AudioServer::get_playback_speed_scale() const { return playback_speed_scale; } +void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time) { + ERR_FAIL_COND(p_playback.is_null()); + + Map<StringName, Vector<AudioFrame>> map; + map[p_bus] = p_volume_db_vector; + + start_playback_stream(p_playback, map, p_start_time); +} + +void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time) { + ERR_FAIL_COND(p_playback.is_null()); + + AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode(); + playback_node->stream_playback = p_playback; + playback_node->stream_playback->start(p_start_time); + + AudioStreamPlaybackBusDetails *new_bus_details = new AudioStreamPlaybackBusDetails(); + int idx = 0; + for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) { + ERR_FAIL_COND(pair.value.size() < channel_count); + ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS); + + new_bus_details->bus_active[idx] = true; + new_bus_details->bus[idx] = pair.key; + for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) { + new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx]; + } + } + playback_node->bus_details = new_bus_details; + playback_node->prev_bus_details = new AudioStreamPlaybackBusDetails(); + + playback_node->setseek.set(-1); + playback_node->pitch_scale.set(1); + playback_node->highshelf_gain.set(0); + playback_node->attenuation_filter_cutoff_hz.set(0); + + memset(playback_node->prev_bus_details->volume, 0, sizeof(playback_node->prev_bus_details->volume)); + + for (AudioFrame &frame : playback_node->lookahead) { + frame = AudioFrame(0, 0); + } + + playback_node->state.store(AudioStreamPlaybackListNode::PLAYING); + + playback_list.insert(playback_node); +} + +void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) { + ERR_FAIL_COND(p_playback.is_null()); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return; + } + + AudioStreamPlaybackListNode::PlaybackState new_state, old_state; + do { + old_state = playback_node->state.load(); + new_state = AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION; + + } while (!playback_node->state.compare_exchange_strong(old_state, new_state)); +} + +void AudioServer::set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes) { + ERR_FAIL_COND(p_volumes.size() != MAX_CHANNELS_PER_BUS); + + Map<StringName, Vector<AudioFrame>> map; + map[p_bus] = p_volumes; + + set_playback_bus_volumes_linear(p_playback, map); +} + +void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes) { + ERR_FAIL_COND(p_bus_volumes.size() > MAX_BUSES_PER_PLAYBACK); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return; + } + AudioStreamPlaybackBusDetails *old_bus_details, *new_bus_details = new AudioStreamPlaybackBusDetails(); + + int idx = 0; + for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) { + ERR_FAIL_COND(pair.value.size() < channel_count); + ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS); + + new_bus_details->bus_active[idx] = true; + new_bus_details->bus[idx] = pair.key; + for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) { + new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx]; + } + } + + do { + old_bus_details = playback_node->bus_details.load(); + } while (!playback_node->bus_details.compare_exchange_strong(old_bus_details, new_bus_details)); + + bus_details_graveyard.insert(old_bus_details); +} + +void AudioServer::set_playback_all_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Vector<AudioFrame> p_volumes) { + ERR_FAIL_COND(p_playback.is_null()); + ERR_FAIL_COND(p_volumes.size() != MAX_CHANNELS_PER_BUS); + + Map<StringName, Vector<AudioFrame>> map; + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return; + } + for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) { + if (playback_node->bus_details.load()->bus_active[bus_idx]) { + map[playback_node->bus_details.load()->bus[bus_idx]] = p_volumes; + } + } + + set_playback_bus_volumes_linear(p_playback, map); +} + +void AudioServer::set_playback_pitch_scale(Ref<AudioStreamPlayback> p_playback, float p_pitch_scale) { + ERR_FAIL_COND(p_playback.is_null()); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return; + } + + playback_node->pitch_scale.set(p_pitch_scale); +} + +void AudioServer::set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool p_paused) { + ERR_FAIL_COND(p_playback.is_null()); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return; + } + if (!p_paused && playback_node->state == AudioStreamPlaybackListNode::PLAYING) { + return; // No-op. + } + if (p_paused && (playback_node->state == AudioStreamPlaybackListNode::PAUSED || playback_node->state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) { + return; // No-op. + } + + AudioStreamPlaybackListNode::PlaybackState new_state, old_state; + do { + old_state = playback_node->state.load(); + new_state = p_paused ? AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE : AudioStreamPlaybackListNode::PLAYING; + } while (!playback_node->state.compare_exchange_strong(old_state, new_state)); +} + +void AudioServer::set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playback, float p_gain, float p_attenuation_cutoff_hz) { + ERR_FAIL_COND(p_playback.is_null()); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return; + } + + playback_node->attenuation_filter_cutoff_hz.set(p_attenuation_cutoff_hz); + playback_node->highshelf_gain.set(p_gain); +} + +bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) { + ERR_FAIL_COND_V(p_playback.is_null(), false); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return false; + } + + return playback_node->state.load() == AudioStreamPlaybackListNode::PLAYING; +} + +float AudioServer::get_playback_position(Ref<AudioStreamPlayback> p_playback) { + ERR_FAIL_COND_V(p_playback.is_null(), 0); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return 0; + } + + return playback_node->stream_playback->get_playback_position(); +} + +bool AudioServer::is_playback_paused(Ref<AudioStreamPlayback> p_playback) { + ERR_FAIL_COND_V(p_playback.is_null(), false); + + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (!playback_node) { + return false; + } + + return playback_node->state.load() == AudioStreamPlaybackListNode::PAUSED || playback_node->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE; +} + +uint64_t AudioServer::get_mix_count() const { + return mix_count; +} + +void AudioServer::notify_listener_changed() { + for (CallbackItem *ci : listener_changed_callback_list) { + ci->callback(ci->userdata); + } +} + void AudioServer::init_channels_and_buffers() { channel_count = get_channel_count(); temp_buffer.resize(channel_count); + mix_buffer.resize(buffer_size + LOOKAHEAD_BUFFER_SIZE); for (int i = 0; i < temp_buffer.size(); i++) { temp_buffer.write[i].resize(buffer_size); @@ -943,7 +1350,7 @@ void AudioServer::init() { channel_disable_threshold_db = GLOBAL_DEF_RST("audio/buses/channel_disable_threshold_db", -60.0); channel_disable_frames = float(GLOBAL_DEF_RST("audio/buses/channel_disable_time", 2.0)) * get_mix_rate(); ProjectSettings::get_singleton()->set_custom_property_info("audio/buses/channel_disable_time", PropertyInfo(Variant::FLOAT, "audio/buses/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); - buffer_size = 1024; //hardcoded for now + buffer_size = 512; //hardcoded for now init_channels_and_buffers(); @@ -1030,9 +1437,17 @@ void AudioServer::update() { prof_time = 0; #endif - for (Set<CallbackItem>::Element *E = update_callbacks.front(); E; E = E->next()) { - E->get().callback(E->get().userdata); + for (CallbackItem *ci : update_callback_list) { + ci->callback(ci->userdata); } + mix_callback_list.maybe_cleanup(); + update_callback_list.maybe_cleanup(); + listener_changed_callback_list.maybe_cleanup(); + playback_list.maybe_cleanup(); + for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard) { + bus_details_graveyard.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; }); + } + bus_details_graveyard.maybe_cleanup(); } void AudioServer::load_default_bus_layout() { @@ -1098,40 +1513,49 @@ double AudioServer::get_time_since_last_mix() const { AudioServer *AudioServer::singleton = nullptr; -void AudioServer::add_callback(AudioCallback p_callback, void *p_userdata) { - lock(); - CallbackItem ci; - ci.callback = p_callback; - ci.userdata = p_userdata; - callbacks.insert(ci); - unlock(); +void AudioServer::add_update_callback(AudioCallback p_callback, void *p_userdata) { + CallbackItem *ci = new CallbackItem(); + ci->callback = p_callback; + ci->userdata = p_userdata; + update_callback_list.insert(ci); } -void AudioServer::remove_callback(AudioCallback p_callback, void *p_userdata) { - lock(); - CallbackItem ci; - ci.callback = p_callback; - ci.userdata = p_userdata; - callbacks.erase(ci); - unlock(); +void AudioServer::remove_update_callback(AudioCallback p_callback, void *p_userdata) { + for (CallbackItem *ci : update_callback_list) { + if (ci->callback == p_callback && ci->userdata == p_userdata) { + update_callback_list.erase(ci, [](CallbackItem *c) { delete c; }); + } + } } -void AudioServer::add_update_callback(AudioCallback p_callback, void *p_userdata) { - lock(); - CallbackItem ci; - ci.callback = p_callback; - ci.userdata = p_userdata; - update_callbacks.insert(ci); - unlock(); +void AudioServer::add_mix_callback(AudioCallback p_callback, void *p_userdata) { + CallbackItem *ci = new CallbackItem(); + ci->callback = p_callback; + ci->userdata = p_userdata; + mix_callback_list.insert(ci); } -void AudioServer::remove_update_callback(AudioCallback p_callback, void *p_userdata) { - lock(); - CallbackItem ci; - ci.callback = p_callback; - ci.userdata = p_userdata; - update_callbacks.erase(ci); - unlock(); +void AudioServer::remove_mix_callback(AudioCallback p_callback, void *p_userdata) { + for (CallbackItem *ci : mix_callback_list) { + if (ci->callback == p_callback && ci->userdata == p_userdata) { + mix_callback_list.erase(ci, [](CallbackItem *c) { delete c; }); + } + } +} + +void AudioServer::add_listener_changed_callback(AudioCallback p_callback, void *p_userdata) { + CallbackItem *ci = new CallbackItem(); + ci->callback = p_callback; + ci->userdata = p_userdata; + listener_changed_callback_list.insert(ci); +} + +void AudioServer::remove_listener_changed_callback(AudioCallback p_callback, void *p_userdata) { + for (CallbackItem *ci : listener_changed_callback_list) { + if (ci->callback == p_callback && ci->userdata == p_userdata) { + listener_changed_callback_list.erase(ci, [](CallbackItem *c) { delete c; }); + } + } } void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) { diff --git a/servers/audio_server.h b/servers/audio_server.h index 7974c4a2ad..affcb3df7b 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -34,12 +34,17 @@ #include "core/math/audio_frame.h" #include "core/object/class_db.h" #include "core/os/os.h" +#include "core/templates/safe_list.h" #include "core/variant/variant.h" #include "servers/audio/audio_effect.h" +#include "servers/audio/audio_filter_sw.h" + +#include <atomic> class AudioDriverDummy; class AudioStream; class AudioStreamSample; +class AudioStreamPlayback; class AudioDriver { static AudioDriver *singleton; @@ -155,7 +160,10 @@ public: }; enum { - AUDIO_DATA_INVALID_ID = -1 + AUDIO_DATA_INVALID_ID = -1, + MAX_CHANNELS_PER_BUS = 4, + MAX_BUSES_PER_PLAYBACK = 6, + LOOKAHEAD_BUFFER_SIZE = 32, }; typedef void (*AudioCallback)(void *p_userdata); @@ -219,7 +227,43 @@ private: int index_cache; }; + struct AudioStreamPlaybackBusDetails { + bool bus_active[MAX_BUSES_PER_PLAYBACK] = { false, false, false, false, false, false }; + StringName bus[MAX_BUSES_PER_PLAYBACK]; + AudioFrame volume[MAX_BUSES_PER_PLAYBACK][MAX_CHANNELS_PER_BUS]; + }; + + struct AudioStreamPlaybackListNode { + enum PlaybackState { + PAUSED = 0, // Paused. Keep this stream playback around though so it can be restarted. + PLAYING = 1, // Playing. Fading may still be necessary if volume changes! + FADE_OUT_TO_PAUSE = 2, // About to pause. + FADE_OUT_TO_DELETION = 3, // About to stop. + AWAITING_DELETION = 4, + }; + // If zero or positive, a place in the stream to seek to during the next mix. + SafeNumeric<float> setseek; + SafeNumeric<float> pitch_scale; + SafeNumeric<float> highshelf_gain; + SafeNumeric<float> attenuation_filter_cutoff_hz; // This isn't used unless highshelf_gain is nonzero. + AudioFilterSW::Processor filter_process[8]; + // Updating this ref after the list node is created breaks consistency guarantees, don't do it! + Ref<AudioStreamPlayback> stream_playback; + // Playback state determines the fate of a particular AudioStreamListNode during the mix step. Must be atomically replaced. + std::atomic<PlaybackState> state = AWAITING_DELETION; + // This data should only ever be modified by an atomic replacement of the pointer. + std::atomic<AudioStreamPlaybackBusDetails *> bus_details = nullptr; + // Previous bus details should only be accessed on the audio thread. + AudioStreamPlaybackBusDetails *prev_bus_details = nullptr; + // The next few samples are stored here so we have some time to fade audio out if it ends abruptly at the beginning of the next mix. + AudioFrame lookahead[LOOKAHEAD_BUFFER_SIZE]; + }; + + SafeList<AudioStreamPlaybackListNode *> playback_list; + SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard; + Vector<Vector<AudioFrame>> temp_buffer; //temp_buffer for each level + Vector<AudioFrame> mix_buffer; Vector<Bus *> buses; Map<StringName, Bus *> bus_map; @@ -230,18 +274,19 @@ private: void init_channels_and_buffers(); void _mix_step(); + void _mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_source_buf, AudioFrame p_vol_start, AudioFrame p_vol_final, float p_attenuation_filter_cutoff_hz, float p_highshelf_gain, AudioFilterSW::Processor *p_processor_l, AudioFilterSW::Processor *p_processor_r); + + // Should only be called on the main thread. + AudioStreamPlaybackListNode *_find_playback_list_node(Ref<AudioStreamPlayback> p_playback); struct CallbackItem { AudioCallback callback; void *userdata; - - bool operator<(const CallbackItem &p_item) const { - return (callback == p_item.callback ? userdata < p_item.userdata : callback < p_item.callback); - } }; - Set<CallbackItem> callbacks; - Set<CallbackItem> update_callbacks; + SafeList<CallbackItem *> update_callback_list; + SafeList<CallbackItem *> mix_callback_list; + SafeList<CallbackItem *> listener_changed_callback_list; friend class AudioDriver; void _driver_process(int p_frames, int32_t *p_buffer); @@ -319,6 +364,25 @@ public: void set_playback_speed_scale(float p_scale); float get_playback_speed_scale() const; + void start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0); + void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0); + void stop_playback_stream(Ref<AudioStreamPlayback> p_playback); + + void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes); + void set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes); + void set_playback_all_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Vector<AudioFrame> p_volumes); + void set_playback_pitch_scale(Ref<AudioStreamPlayback> p_playback, float p_pitch_scale); + void set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool p_paused); + void set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playback, float p_gain, float p_attenuation_cutoff_hz); + + bool is_playback_active(Ref<AudioStreamPlayback> p_playback); + float get_playback_position(Ref<AudioStreamPlayback> p_playback); + bool is_playback_paused(Ref<AudioStreamPlayback> p_playback); + + uint64_t get_mix_count() const; + + void notify_listener_changed(); + virtual void init(); virtual void finish(); virtual void update(); @@ -340,12 +404,15 @@ public: virtual double get_time_to_next_mix() const; virtual double get_time_since_last_mix() const; - void add_callback(AudioCallback p_callback, void *p_userdata); - void remove_callback(AudioCallback p_callback, void *p_userdata); + void add_listener_changed_callback(AudioCallback p_callback, void *p_userdata); + void remove_listener_changed_callback(AudioCallback p_callback, void *p_userdata); void add_update_callback(AudioCallback p_callback, void *p_userdata); void remove_update_callback(AudioCallback p_callback, void *p_userdata); + void add_mix_callback(AudioCallback p_callback, void *p_userdata); + void remove_mix_callback(AudioCallback p_callback, void *p_userdata); + void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout); Ref<AudioBusLayout> generate_bus_layout() const; diff --git a/servers/physics_2d/area_2d_sw.cpp b/servers/physics_2d/area_2d_sw.cpp index 532cb259b3..663a47f273 100644 --- a/servers/physics_2d/area_2d_sw.cpp +++ b/servers/physics_2d/area_2d_sw.cpp @@ -274,6 +274,26 @@ void Area2DSW::call_queries() { } } +void Area2DSW::compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const { + if (is_gravity_point()) { + const real_t gravity_distance_scale = get_gravity_distance_scale(); + Vector2 v = get_transform().xform(get_gravity_vector()) - p_position; + if (gravity_distance_scale > 0) { + const real_t v_length = v.length(); + if (v_length > 0) { + const real_t v_scaled = v_length * gravity_distance_scale; + r_gravity = (v.normalized() * (get_gravity() / (v_scaled * v_scaled))); + } else { + r_gravity = Vector2(); + } + } else { + r_gravity = v.normalized() * get_gravity(); + } + } else { + r_gravity = get_gravity_vector() * get_gravity(); + } +} + Area2DSW::Area2DSW() : CollisionObject2DSW(TYPE_AREA), monitor_query_list(this), diff --git a/servers/physics_2d/area_2d_sw.h b/servers/physics_2d/area_2d_sw.h index 3bf603b30d..d9147d6f1d 100644 --- a/servers/physics_2d/area_2d_sw.h +++ b/servers/physics_2d/area_2d_sw.h @@ -34,7 +34,6 @@ #include "collision_object_2d_sw.h" #include "core/templates/self_list.h" #include "servers/physics_server_2d.h" -//#include "servers/physics_3d/query_sw.h" class Space2DSW; class Body2DSW; @@ -94,17 +93,12 @@ class Area2DSW : public CollisionObject2DSW { Map<BodyKey, BodyState> monitored_bodies; Map<BodyKey, BodyState> monitored_areas; - //virtual void shape_changed_notify(Shape2DSW *p_shape); - //virtual void shape_deleted_notify(Shape2DSW *p_shape); Set<Constraint2DSW *> constraints; virtual void _shapes_changed(); void _queue_monitor_update(); public: - //_FORCE_INLINE_ const Matrix32& get_inverse_transform() const { return inverse_transform; } - //_FORCE_INLINE_ SpaceSW* get_owner() { return owner; } - void set_monitor_callback(ObjectID p_id, const StringName &p_method); _FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback_id.is_valid(); } @@ -161,6 +155,8 @@ public: void call_queries(); + void compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const; + Area2DSW(); ~Area2DSW(); }; diff --git a/servers/physics_2d/area_pair_2d_sw.cpp b/servers/physics_2d/area_pair_2d_sw.cpp index 5ca16cb6fc..4f1148c26f 100644 --- a/servers/physics_2d/area_pair_2d_sw.cpp +++ b/servers/physics_2d/area_pair_2d_sw.cpp @@ -33,7 +33,7 @@ bool AreaPair2DSW::setup(real_t p_step) { bool result = false; - if (area->interacts_with(body) && CollisionSolver2DSW::solve(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), Vector2(), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), Vector2(), nullptr, this)) { + if (area->collides_with(body) && CollisionSolver2DSW::solve(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), Vector2(), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), Vector2(), nullptr, this)) { result = true; } @@ -109,46 +109,51 @@ AreaPair2DSW::~AreaPair2DSW() { ////////////////////////////////// bool Area2Pair2DSW::setup(real_t p_step) { - bool result = false; - if (area_a->interacts_with(area_b) && CollisionSolver2DSW::solve(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), Vector2(), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), Vector2(), nullptr, this)) { - result = true; + bool result_a = area_a->collides_with(area_b); + bool result_b = area_b->collides_with(area_a); + if ((result_a || result_b) && !CollisionSolver2DSW::solve(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), Vector2(), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), Vector2(), nullptr, this)) { + result_a = false; + result_b = false; } - process_collision = false; - if (result != colliding) { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { - process_collision = true; - } else if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { + bool process_collision = false; + + process_collision_a = false; + if (result_a != colliding_a) { + if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { + process_collision_a = true; process_collision = true; } + colliding_a = result_a; + } - colliding = result; + process_collision_b = false; + if (result_b != colliding_b) { + if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { + process_collision_b = true; + process_collision = true; + } + colliding_b = result_b; } return process_collision; } bool Area2Pair2DSW::pre_solve(real_t p_step) { - if (!process_collision) { - return false; + if (process_collision_a) { + if (colliding_a) { + area_a->add_area_to_query(area_b, shape_b, shape_a); + } else { + area_a->remove_area_from_query(area_b, shape_b, shape_a); + } } - if (colliding) { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { + if (process_collision_b) { + if (colliding_b) { area_b->add_area_to_query(area_a, shape_a, shape_b); - } - - if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { - area_a->add_area_to_query(area_b, shape_b, shape_a); - } - } else { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { + } else { area_b->remove_area_from_query(area_a, shape_a, shape_b); } - - if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { - area_a->remove_area_from_query(area_b, shape_b, shape_a); - } } return false; // Never do any post solving. @@ -168,16 +173,18 @@ Area2Pair2DSW::Area2Pair2DSW(Area2DSW *p_area_a, int p_shape_a, Area2DSW *p_area } Area2Pair2DSW::~Area2Pair2DSW() { - if (colliding) { - if (area_b->has_area_monitor_callback()) { - area_b->remove_area_from_query(area_a, shape_a, shape_b); - } - + if (colliding_a) { if (area_a->has_area_monitor_callback()) { area_a->remove_area_from_query(area_b, shape_b, shape_a); } } + if (colliding_b) { + if (area_b->has_area_monitor_callback()) { + area_b->remove_area_from_query(area_a, shape_a, shape_b); + } + } + area_a->remove_constraint(this); area_b->remove_constraint(this); } diff --git a/servers/physics_2d/area_pair_2d_sw.h b/servers/physics_2d/area_pair_2d_sw.h index 4632a307d9..66e9f1afee 100644 --- a/servers/physics_2d/area_pair_2d_sw.h +++ b/servers/physics_2d/area_pair_2d_sw.h @@ -57,8 +57,10 @@ class Area2Pair2DSW : public Constraint2DSW { Area2DSW *area_b = nullptr; int shape_a = 0; int shape_b = 0; - bool colliding = false; - bool process_collision = false; + bool colliding_a = false; + bool colliding_b = false; + bool process_collision_a = false; + bool process_collision_b = false; public: virtual bool setup(real_t p_step) override; diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp index 7aa2f9b7de..edd769aa9a 100644 --- a/servers/physics_2d/body_2d_sw.cpp +++ b/servers/physics_2d/body_2d_sw.cpp @@ -29,51 +29,77 @@ /*************************************************************************/ #include "body_2d_sw.h" + #include "area_2d_sw.h" -#include "physics_server_2d_sw.h" +#include "body_direct_state_2d_sw.h" #include "space_2d_sw.h" -void Body2DSW::_update_inertia() { - if (!user_inertia && get_space() && !inertia_update_list.in_list()) { - get_space()->body_add_to_inertia_update_list(&inertia_update_list); +void Body2DSW::_mass_properties_changed() { + if (get_space() && !mass_properties_update_list.in_list() && (calculate_inertia || calculate_center_of_mass)) { + get_space()->body_add_to_mass_properties_update_list(&mass_properties_update_list); } } -void Body2DSW::update_inertias() { +void Body2DSW::update_mass_properties() { //update shapes and motions switch (mode) { case PhysicsServer2D::BODY_MODE_DYNAMIC: { - if (user_inertia) { - _inv_inertia = inertia > 0 ? (1.0 / inertia) : 0; - break; - } - //update tensor for allshapes, not the best way but should be somehow OK. (inspired from bullet) real_t total_area = 0; - for (int i = 0; i < get_shape_count(); i++) { + if (is_shape_disabled(i)) { + continue; + } total_area += get_shape_aabb(i).get_area(); } - inertia = 0; + if (calculate_center_of_mass) { + // We have to recompute the center of mass. + center_of_mass = Vector2(); - for (int i = 0; i < get_shape_count(); i++) { - if (is_shape_disabled(i)) { - continue; + if (total_area != 0.0) { + for (int i = 0; i < get_shape_count(); i++) { + if (is_shape_disabled(i)) { + continue; + } + + real_t area = get_shape_aabb(i).get_area(); + + real_t mass = area * this->mass / total_area; + + // NOTE: we assume that the shape origin is also its center of mass. + center_of_mass += mass * get_shape_transform(i).get_origin(); + } + + center_of_mass /= mass; } + } + + if (calculate_inertia) { + inertia = 0; + + for (int i = 0; i < get_shape_count(); i++) { + if (is_shape_disabled(i)) { + continue; + } - const Shape2DSW *shape = get_shape(i); + const Shape2DSW *shape = get_shape(i); - real_t area = get_shape_aabb(i).get_area(); + real_t area = get_shape_aabb(i).get_area(); + if (area == 0.0) { + continue; + } - real_t mass = area * this->mass / total_area; + real_t mass = area * this->mass / total_area; - Transform2D mtx = get_shape_transform(i); - Vector2 scale = mtx.get_scale(); - inertia += shape->get_moment_of_inertia(mass, scale) + mass * mtx.get_origin().length_squared(); + Transform2D mtx = get_shape_transform(i); + Vector2 scale = mtx.get_scale(); + Vector2 shape_origin = mtx.get_origin() - center_of_mass; + inertia += shape->get_moment_of_inertia(mass, scale) + mass * shape_origin.length_squared(); + } } - _inv_inertia = inertia > 0 ? (1.0 / inertia) : 0; + _inv_inertia = inertia > 0.0 ? (1.0 / inertia) : 0.0; if (mass) { _inv_mass = 1.0 / mass; @@ -93,9 +119,12 @@ void Body2DSW::update_inertias() { } break; } - //_update_inertia_tensor(); +} - //_update_shapes(); +void Body2DSW::reset_mass_properties() { + calculate_inertia = true; + calculate_center_of_mass = true; + _mass_properties_changed(); } void Body2DSW::set_active(bool p_active) { @@ -117,7 +146,7 @@ void Body2DSW::set_active(bool p_active) { } } -void Body2DSW::set_param(PhysicsServer2D::BodyParameter p_param, real_t p_value) { +void Body2DSW::set_param(PhysicsServer2D::BodyParameter p_param, const Variant &p_value) { switch (p_param) { case PhysicsServer2D::BODY_PARAM_BOUNCE: { bounce = p_value; @@ -126,21 +155,32 @@ void Body2DSW::set_param(PhysicsServer2D::BodyParameter p_param, real_t p_value) friction = p_value; } break; case PhysicsServer2D::BODY_PARAM_MASS: { - ERR_FAIL_COND(p_value <= 0); - mass = p_value; - _update_inertia(); - + real_t mass_value = p_value; + ERR_FAIL_COND(mass_value <= 0); + mass = mass_value; + if (mode >= PhysicsServer2D::BODY_MODE_DYNAMIC) { + _mass_properties_changed(); + } } break; case PhysicsServer2D::BODY_PARAM_INERTIA: { - if (p_value <= 0) { - user_inertia = false; - _update_inertia(); + real_t inertia_value = p_value; + if (inertia_value <= 0.0) { + calculate_inertia = true; + if (mode == PhysicsServer2D::BODY_MODE_DYNAMIC) { + _mass_properties_changed(); + } } else { - user_inertia = true; - inertia = p_value; - _inv_inertia = 1.0 / p_value; + calculate_inertia = false; + inertia = inertia_value; + if (mode == PhysicsServer2D::BODY_MODE_DYNAMIC) { + _inv_inertia = 1.0 / inertia; + } } } break; + case PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS: { + calculate_center_of_mass = false; + center_of_mass = p_value; + } break; case PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE: { gravity_scale = p_value; } break; @@ -155,7 +195,7 @@ void Body2DSW::set_param(PhysicsServer2D::BodyParameter p_param, real_t p_value) } } -real_t Body2DSW::get_param(PhysicsServer2D::BodyParameter p_param) const { +Variant Body2DSW::get_param(PhysicsServer2D::BodyParameter p_param) const { switch (p_param) { case PhysicsServer2D::BODY_PARAM_BOUNCE: { return bounce; @@ -169,6 +209,9 @@ real_t Body2DSW::get_param(PhysicsServer2D::BodyParameter p_param) const { case PhysicsServer2D::BODY_PARAM_INERTIA: { return inertia; } + case PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS: { + return center_of_mass; + } case PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE: { return gravity_scale; } @@ -206,7 +249,10 @@ void Body2DSW::set_mode(PhysicsServer2D::BodyMode p_mode) { } break; case PhysicsServer2D::BODY_MODE_DYNAMIC: { _inv_mass = mass > 0 ? (1.0 / mass) : 0; - _inv_inertia = inertia > 0 ? (1.0 / inertia) : 0; + if (!calculate_inertia) { + _inv_inertia = 1.0 / inertia; + } + _mass_properties_changed(); _set_static(false); set_active(true); @@ -214,18 +260,11 @@ void Body2DSW::set_mode(PhysicsServer2D::BodyMode p_mode) { case PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED: { _inv_mass = mass > 0 ? (1.0 / mass) : 0; _inv_inertia = 0; + angular_velocity = 0; _set_static(false); set_active(true); - angular_velocity = 0; - } break; - } - if (p_mode == PhysicsServer2D::BODY_MODE_DYNAMIC && _inv_inertia == 0) { - _update_inertia(); + } } - /* - if (get_space()) - _update_queries(); - */ } PhysicsServer2D::BodyMode Body2DSW::get_mode() const { @@ -233,7 +272,7 @@ PhysicsServer2D::BodyMode Body2DSW::get_mode() const { } void Body2DSW::_shapes_changed() { - _update_inertia(); + _mass_properties_changed(); wakeup_neighbours(); } @@ -268,11 +307,13 @@ void Body2DSW::set_state(PhysicsServer2D::BodyState p_state, const Variant &p_va } break; case PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY: { linear_velocity = p_variant; + constant_linear_velocity = linear_velocity; wakeup(); } break; case PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY: { angular_velocity = p_variant; + constant_angular_velocity = angular_velocity; wakeup(); } break; @@ -295,7 +336,7 @@ void Body2DSW::set_state(PhysicsServer2D::BodyState p_state, const Variant &p_va } break; case PhysicsServer2D::BODY_STATE_CAN_SLEEP: { can_sleep = p_variant; - if (mode == PhysicsServer2D::BODY_MODE_DYNAMIC && !active && !can_sleep) { + if (mode >= PhysicsServer2D::BODY_MODE_DYNAMIC && !active && !can_sleep) { set_active(true); } @@ -329,8 +370,8 @@ void Body2DSW::set_space(Space2DSW *p_space) { if (get_space()) { wakeup_neighbours(); - if (inertia_update_list.in_list()) { - get_space()->body_remove_from_inertia_update_list(&inertia_update_list); + if (mass_properties_update_list.in_list()) { + get_space()->body_remove_from_mass_properties_update_list(&mass_properties_update_list); } if (active_list.in_list()) { get_space()->body_remove_from_active_list(&active_list); @@ -343,26 +384,17 @@ void Body2DSW::set_space(Space2DSW *p_space) { _set_space(p_space); if (get_space()) { - _update_inertia(); + _mass_properties_changed(); if (active) { get_space()->body_add_to_active_list(&active_list); } } - - first_integration = false; } -void Body2DSW::_compute_area_gravity_and_dampenings(const Area2DSW *p_area) { - if (p_area->is_gravity_point()) { - if (p_area->get_gravity_distance_scale() > 0) { - Vector2 v = p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin(); - gravity += v.normalized() * (p_area->get_gravity() / Math::pow(v.length() * p_area->get_gravity_distance_scale() + 1, 2)); - } else { - gravity += (p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin()).normalized() * p_area->get_gravity(); - } - } else { - gravity += p_area->get_gravity_vector() * p_area->get_gravity(); - } +void Body2DSW::_compute_area_gravity_and_damping(const Area2DSW *p_area) { + Vector2 area_gravity; + p_area->compute_gravity(get_transform().get_origin(), area_gravity); + gravity += area_gravity; area_linear_damp += p_area->get_linear_damp(); area_angular_damp += p_area->get_angular_damp(); @@ -391,7 +423,7 @@ void Body2DSW::integrate_forces(real_t p_step) { switch (mode) { case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE: case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: { - _compute_area_gravity_and_dampenings(aa[i].area); + _compute_area_gravity_and_damping(aa[i].area); stopped = mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE; } break; case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE: @@ -399,7 +431,7 @@ void Body2DSW::integrate_forces(real_t p_step) { gravity = Vector2(0, 0); area_angular_damp = 0; area_linear_damp = 0; - _compute_area_gravity_and_dampenings(aa[i].area); + _compute_area_gravity_and_damping(aa[i].area); stopped = mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE; } break; default: { @@ -408,7 +440,7 @@ void Body2DSW::integrate_forces(real_t p_step) { } } if (!stopped) { - _compute_area_gravity_and_dampenings(def_area); + _compute_area_gravity_and_damping(def_area); } gravity *= gravity_scale; @@ -435,10 +467,10 @@ void Body2DSW::integrate_forces(real_t p_step) { if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC) { //compute motion, angular and etc. velocities from prev transform motion = new_transform.get_origin() - get_transform().get_origin(); - linear_velocity = motion / p_step; + linear_velocity = constant_linear_velocity + motion / p_step; real_t rot = new_transform.get_rotation() - get_transform().get_rotation(); - angular_velocity = remainder(rot, 2.0 * Math_PI) / p_step; + angular_velocity = constant_angular_velocity + remainder(rot, 2.0 * Math_PI) / p_step; do_motion = true; @@ -450,7 +482,7 @@ void Body2DSW::integrate_forces(real_t p_step) { */ } else { - if (!omit_force_integration && !first_integration) { + if (!omit_force_integration) { //overridden by direct state query Vector2 force = gravity * mass; @@ -484,7 +516,6 @@ void Body2DSW::integrate_forces(real_t p_step) { //motion=linear_velocity*p_step; - first_integration = false; biased_angular_velocity = 0; biased_linear_velocity = Vector2(); @@ -502,7 +533,7 @@ void Body2DSW::integrate_velocities(real_t p_step) { return; } - if (fi_callback) { + if (fi_callback_data || body_state_callback) { get_space()->body_add_to_state_query_list(&direct_state_query_list); } @@ -521,14 +552,22 @@ void Body2DSW::integrate_velocities(real_t p_step) { real_t angle = get_transform().get_rotation() + total_angular_velocity * p_step; Vector2 pos = get_transform().get_origin() + total_linear_velocity * p_step; + real_t center_of_mass_distance = center_of_mass.length(); + if (center_of_mass_distance > CMP_EPSILON) { + // Calculate displacement due to center of mass offset. + real_t prev_angle = get_transform().get_rotation(); + real_t angle_base = Math::atan2(center_of_mass.y, center_of_mass.x); + Vector2 point1(Math::cos(angle_base + prev_angle), Math::sin(angle_base + prev_angle)); + Vector2 point2(Math::cos(angle_base + angle), Math::sin(angle_base + angle)); + pos += center_of_mass_distance * (point1 - point2); + } + _set_transform(Transform2D(angle, pos), continuous_cd_mode == PhysicsServer2D::CCD_MODE_DISABLED); _set_inv_transform(get_transform().inverse()); if (continuous_cd_mode != PhysicsServer2D::CCD_MODE_DISABLED) { new_transform = get_transform(); } - - //_update_inertia_tensor(); } void Body2DSW::wakeup_neighbours() { @@ -542,7 +581,7 @@ void Body2DSW::wakeup_neighbours() { continue; } Body2DSW *b = n[i]; - if (b->mode != PhysicsServer2D::BODY_MODE_DYNAMIC) { + if (b->mode < PhysicsServer2D::BODY_MODE_DYNAMIC) { continue; } @@ -554,27 +593,27 @@ void Body2DSW::wakeup_neighbours() { } void Body2DSW::call_queries() { - if (fi_callback) { - PhysicsDirectBodyState2DSW *dbs = PhysicsDirectBodyState2DSW::singleton; - dbs->body = this; - - Variant v = dbs; - const Variant *vp[2] = { &v, &fi_callback->callback_udata }; - - Object *obj = fi_callback->callable.get_object(); - if (!obj) { + if (fi_callback_data) { + if (!fi_callback_data->callable.get_object()) { set_force_integration_callback(Callable()); } else { + Variant direct_state_variant = get_direct_state(); + const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata }; + Callable::CallError ce; Variant rv; - if (fi_callback->callback_udata.get_type() != Variant::NIL) { - fi_callback->callable.call(vp, 2, rv, ce); + if (fi_callback_data->udata.get_type() != Variant::NIL) { + fi_callback_data->callable.call(vp, 2, rv, ce); } else { - fi_callback->callable.call(vp, 1, rv, ce); + fi_callback_data->callable.call(vp, 1, rv, ce); } } } + + if (body_state_callback) { + (body_state_callback)(body_state_callback_instance, get_direct_state()); + } } bool Body2DSW::sleep_test(real_t p_step) { @@ -594,78 +633,45 @@ bool Body2DSW::sleep_test(real_t p_step) { } } +void Body2DSW::set_state_sync_callback(void *p_instance, PhysicsServer2D::BodyStateCallback p_callback) { + body_state_callback_instance = p_instance; + body_state_callback = p_callback; +} + void Body2DSW::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) { - if (fi_callback) { - memdelete(fi_callback); - fi_callback = nullptr; + if (p_callable.get_object()) { + if (!fi_callback_data) { + fi_callback_data = memnew(ForceIntegrationCallbackData); + } + fi_callback_data->callable = p_callable; + fi_callback_data->udata = p_udata; + } else if (fi_callback_data) { + memdelete(fi_callback_data); + fi_callback_data = nullptr; } +} - if (p_callable.get_object()) { - fi_callback = memnew(ForceIntegrationCallback); - fi_callback->callable = p_callable; - fi_callback->callback_udata = p_udata; +PhysicsDirectBodyState2DSW *Body2DSW::get_direct_state() { + if (!direct_state) { + direct_state = memnew(PhysicsDirectBodyState2DSW); + direct_state->body = this; } + return direct_state; } Body2DSW::Body2DSW() : CollisionObject2DSW(TYPE_BODY), active_list(this), - inertia_update_list(this), + mass_properties_update_list(this), direct_state_query_list(this) { - mode = PhysicsServer2D::BODY_MODE_DYNAMIC; - active = true; - angular_velocity = 0; - biased_angular_velocity = 0; - mass = 1; - inertia = 0; - user_inertia = false; - _inv_inertia = 0; - _inv_mass = 1; - bounce = 0; - friction = 1; - omit_force_integration = false; - applied_torque = 0; - island_step = 0; _set_static(false); - first_time_kinematic = false; - linear_damp = -1; - angular_damp = -1; - area_angular_damp = 0; - area_linear_damp = 0; - contact_count = 0; - gravity_scale = 1.0; - first_integration = false; - - still_time = 0; - continuous_cd_mode = PhysicsServer2D::CCD_MODE_DISABLED; - can_sleep = true; - fi_callback = nullptr; } Body2DSW::~Body2DSW() { - if (fi_callback) { - memdelete(fi_callback); - } -} - -PhysicsDirectBodyState2DSW *PhysicsDirectBodyState2DSW::singleton = nullptr; - -PhysicsDirectSpaceState2D *PhysicsDirectBodyState2DSW::get_space_state() { - return body->get_space()->get_direct_state(); -} - -Variant PhysicsDirectBodyState2DSW::get_contact_collider_shape_metadata(int p_contact_idx) const { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Variant()); - - if (!PhysicsServer2DSW::singletonsw->body_owner.owns(body->contacts[p_contact_idx].collider)) { - return Variant(); + if (fi_callback_data) { + memdelete(fi_callback_data); } - Body2DSW *other = PhysicsServer2DSW::singletonsw->body_owner.getornull(body->contacts[p_contact_idx].collider); - - int sidx = body->contacts[p_contact_idx].collider_shape; - if (sidx < 0 || sidx >= other->get_shape_count()) { - return Variant(); + if (direct_state) { + memdelete(direct_state); } - - return other->get_shape_metadata(sidx); } diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h index 74bef433dc..95e89786cd 100644 --- a/servers/physics_2d/body_2d_sw.h +++ b/servers/physics_2d/body_2d_sw.h @@ -38,50 +38,58 @@ #include "core/templates/vset.h" class Constraint2DSW; +class PhysicsDirectBodyState2DSW; class Body2DSW : public CollisionObject2DSW { - PhysicsServer2D::BodyMode mode; + PhysicsServer2D::BodyMode mode = PhysicsServer2D::BODY_MODE_DYNAMIC; Vector2 biased_linear_velocity; - real_t biased_angular_velocity; + real_t biased_angular_velocity = 0.0; Vector2 linear_velocity; - real_t angular_velocity; + real_t angular_velocity = 0.0; - real_t linear_damp; - real_t angular_damp; - real_t gravity_scale; + Vector2 constant_linear_velocity; + real_t constant_angular_velocity = 0.0; - real_t mass; - real_t inertia; - real_t bounce; - real_t friction; + real_t linear_damp = -1.0; + real_t angular_damp = -1.0; + real_t gravity_scale = 1.0; - real_t _inv_mass; - real_t _inv_inertia; - bool user_inertia; + real_t bounce = 0.0; + real_t friction = 1.0; + + real_t mass = 1.0; + real_t _inv_mass = 1.0; + + real_t inertia = 0.0; + real_t _inv_inertia = 0.0; + + Vector2 center_of_mass; + + bool calculate_inertia = true; + bool calculate_center_of_mass = true; Vector2 gravity; - real_t area_linear_damp; - real_t area_angular_damp; + real_t area_linear_damp = 0.0; + real_t area_angular_damp = 0.0; - real_t still_time; + real_t still_time = 0.0; Vector2 applied_force; - real_t applied_torque; + real_t applied_torque = 0.0; SelfList<Body2DSW> active_list; - SelfList<Body2DSW> inertia_update_list; + SelfList<Body2DSW> mass_properties_update_list; SelfList<Body2DSW> direct_state_query_list; VSet<RID> exceptions; - PhysicsServer2D::CCDMode continuous_cd_mode; - bool omit_force_integration; - bool active; - bool can_sleep; - bool first_time_kinematic; - bool first_integration; - void _update_inertia(); + PhysicsServer2D::CCDMode continuous_cd_mode = PhysicsServer2D::CCD_MODE_DISABLED; + bool omit_force_integration = false; + bool active = true; + bool can_sleep = true; + bool first_time_kinematic = false; + void _mass_properties_changed(); virtual void _shapes_changed(); Transform2D new_transform; @@ -114,24 +122,32 @@ class Body2DSW : public CollisionObject2DSW { }; Vector<Contact> contacts; //no contacts by default - int contact_count; + int contact_count = 0; + + void *body_state_callback_instance = nullptr; + PhysicsServer2D::BodyStateCallback body_state_callback = nullptr; - struct ForceIntegrationCallback { + struct ForceIntegrationCallbackData { Callable callable; - Variant callback_udata; + Variant udata; }; - ForceIntegrationCallback *fi_callback; + ForceIntegrationCallbackData *fi_callback_data = nullptr; - uint64_t island_step; + PhysicsDirectBodyState2DSW *direct_state = nullptr; - _FORCE_INLINE_ void _compute_area_gravity_and_dampenings(const Area2DSW *p_area); + uint64_t island_step = 0; + + _FORCE_INLINE_ void _compute_area_gravity_and_damping(const Area2DSW *p_area); friend class PhysicsDirectBodyState2DSW; // i give up, too many functions to expose public: + void set_state_sync_callback(void *p_instance, PhysicsServer2D::BodyStateCallback p_callback); void set_force_integration_callback(const Callable &p_callable, const Variant &p_udata = Variant()); + PhysicsDirectBodyState2DSW *get_direct_state(); + _FORCE_INLINE_ void add_area(Area2DSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { @@ -198,7 +214,7 @@ public: _FORCE_INLINE_ void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) { linear_velocity += p_impulse * _inv_mass; - angular_velocity += _inv_inertia * p_position.cross(p_impulse); + angular_velocity += _inv_inertia * (p_position - center_of_mass).cross(p_impulse); } _FORCE_INLINE_ void apply_torque_impulse(real_t p_torque) { @@ -207,7 +223,7 @@ public: _FORCE_INLINE_ void apply_bias_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) { biased_linear_velocity += p_impulse * _inv_mass; - biased_angular_velocity += _inv_inertia * p_position.cross(p_impulse); + biased_angular_velocity += _inv_inertia * (p_position - center_of_mass).cross(p_impulse); } void set_active(bool p_active); @@ -220,8 +236,8 @@ public: set_active(true); } - void set_param(PhysicsServer2D::BodyParameter p_param, real_t); - real_t get_param(PhysicsServer2D::BodyParameter p_param) const; + void set_param(PhysicsServer2D::BodyParameter p_param, const Variant &p_value); + Variant get_param(PhysicsServer2D::BodyParameter p_param) const; void set_mode(PhysicsServer2D::BodyMode p_mode); PhysicsServer2D::BodyMode get_mode() const; @@ -241,7 +257,7 @@ public: _FORCE_INLINE_ void add_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) { applied_force += p_force; - applied_torque += p_position.cross(p_force); + applied_torque += (p_position - center_of_mass).cross(p_force); } _FORCE_INLINE_ void add_torque(real_t p_torque) { @@ -253,8 +269,10 @@ public: void set_space(Space2DSW *p_space); - void update_inertias(); + void update_mass_properties(); + void reset_mass_properties(); + _FORCE_INLINE_ Vector2 get_center_of_mass() const { return center_of_mass; } _FORCE_INLINE_ real_t get_inv_mass() const { return _inv_mass; } _FORCE_INLINE_ real_t get_inv_inertia() const { return _inv_inertia; } _FORCE_INLINE_ real_t get_friction() const { return friction; } @@ -332,87 +350,4 @@ void Body2DSW::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_no c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos; } -class PhysicsDirectBodyState2DSW : public PhysicsDirectBodyState2D { - GDCLASS(PhysicsDirectBodyState2DSW, PhysicsDirectBodyState2D); - -public: - static PhysicsDirectBodyState2DSW *singleton; - Body2DSW *body; - real_t step; - - virtual Vector2 get_total_gravity() const override { return body->gravity; } // get gravity vector working on this body space/area - virtual real_t get_total_angular_damp() const override { return body->area_angular_damp; } // get density of this body space/area - virtual real_t get_total_linear_damp() const override { return body->area_linear_damp; } // get density of this body space/area - - virtual real_t get_inverse_mass() const override { return body->get_inv_mass(); } // get the mass - virtual real_t get_inverse_inertia() const override { return body->get_inv_inertia(); } // get density of this body space - - virtual void set_linear_velocity(const Vector2 &p_velocity) override { body->set_linear_velocity(p_velocity); } - virtual Vector2 get_linear_velocity() const override { return body->get_linear_velocity(); } - - virtual void set_angular_velocity(real_t p_velocity) override { body->set_angular_velocity(p_velocity); } - virtual real_t get_angular_velocity() const override { return body->get_angular_velocity(); } - - virtual void set_transform(const Transform2D &p_transform) override { body->set_state(PhysicsServer2D::BODY_STATE_TRANSFORM, p_transform); } - virtual Transform2D get_transform() const override { return body->get_transform(); } - - virtual Vector2 get_velocity_at_local_position(const Vector2 &p_position) const override { return body->get_velocity_in_local_point(p_position); } - - virtual void add_central_force(const Vector2 &p_force) override { body->add_central_force(p_force); } - virtual void add_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override { body->add_force(p_force, p_position); } - virtual void add_torque(real_t p_torque) override { body->add_torque(p_torque); } - virtual void apply_central_impulse(const Vector2 &p_impulse) override { body->apply_central_impulse(p_impulse); } - virtual void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override { body->apply_impulse(p_impulse, p_position); } - virtual void apply_torque_impulse(real_t p_torque) override { body->apply_torque_impulse(p_torque); } - - virtual void set_sleep_state(bool p_enable) override { body->set_active(!p_enable); } - virtual bool is_sleeping() const override { return !body->is_active(); } - - virtual int get_contact_count() const override { return body->contact_count; } - - virtual Vector2 get_contact_local_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].local_pos; - } - virtual Vector2 get_contact_local_normal(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].local_normal; - } - virtual int get_contact_local_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); - return body->contacts[p_contact_idx].local_shape; - } - - virtual RID get_contact_collider(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); - return body->contacts[p_contact_idx].collider; - } - virtual Vector2 get_contact_collider_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].collider_pos; - } - virtual ObjectID get_contact_collider_id(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); - return body->contacts[p_contact_idx].collider_instance_id; - } - virtual int get_contact_collider_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); - return body->contacts[p_contact_idx].collider_shape; - } - virtual Variant get_contact_collider_shape_metadata(int p_contact_idx) const override; - - virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].collider_velocity_at_pos; - } - - virtual PhysicsDirectSpaceState2D *get_space_state() override; - - virtual real_t get_step() const override { return step; } - PhysicsDirectBodyState2DSW() { - singleton = this; - body = nullptr; - } -}; - #endif // BODY_2D_SW_H diff --git a/servers/physics_2d/body_direct_state_2d_sw.cpp b/servers/physics_2d/body_direct_state_2d_sw.cpp new file mode 100644 index 0000000000..58250c3077 --- /dev/null +++ b/servers/physics_2d/body_direct_state_2d_sw.cpp @@ -0,0 +1,186 @@ +/*************************************************************************/ +/* body_direct_state_2d_sw.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "body_direct_state_2d_sw.h" + +#include "body_2d_sw.h" +#include "physics_server_2d_sw.h" +#include "space_2d_sw.h" + +Vector2 PhysicsDirectBodyState2DSW::get_total_gravity() const { + return body->gravity; +} + +real_t PhysicsDirectBodyState2DSW::get_total_angular_damp() const { + return body->area_angular_damp; +} + +real_t PhysicsDirectBodyState2DSW::get_total_linear_damp() const { + return body->area_linear_damp; +} + +Vector2 PhysicsDirectBodyState2DSW::get_center_of_mass() const { + return body->get_center_of_mass(); +} + +real_t PhysicsDirectBodyState2DSW::get_inverse_mass() const { + return body->get_inv_mass(); +} + +real_t PhysicsDirectBodyState2DSW::get_inverse_inertia() const { + return body->get_inv_inertia(); +} + +void PhysicsDirectBodyState2DSW::set_linear_velocity(const Vector2 &p_velocity) { + body->set_linear_velocity(p_velocity); +} + +Vector2 PhysicsDirectBodyState2DSW::get_linear_velocity() const { + return body->get_linear_velocity(); +} + +void PhysicsDirectBodyState2DSW::set_angular_velocity(real_t p_velocity) { + body->set_angular_velocity(p_velocity); +} + +real_t PhysicsDirectBodyState2DSW::get_angular_velocity() const { + return body->get_angular_velocity(); +} + +void PhysicsDirectBodyState2DSW::set_transform(const Transform2D &p_transform) { + body->set_state(PhysicsServer2D::BODY_STATE_TRANSFORM, p_transform); +} + +Transform2D PhysicsDirectBodyState2DSW::get_transform() const { + return body->get_transform(); +} + +Vector2 PhysicsDirectBodyState2DSW::get_velocity_at_local_position(const Vector2 &p_position) const { + return body->get_velocity_in_local_point(p_position); +} + +void PhysicsDirectBodyState2DSW::add_central_force(const Vector2 &p_force) { + body->add_central_force(p_force); +} + +void PhysicsDirectBodyState2DSW::add_force(const Vector2 &p_force, const Vector2 &p_position) { + body->add_force(p_force, p_position); +} + +void PhysicsDirectBodyState2DSW::add_torque(real_t p_torque) { + body->add_torque(p_torque); +} + +void PhysicsDirectBodyState2DSW::apply_central_impulse(const Vector2 &p_impulse) { + body->apply_central_impulse(p_impulse); +} + +void PhysicsDirectBodyState2DSW::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { + body->apply_impulse(p_impulse, p_position); +} + +void PhysicsDirectBodyState2DSW::apply_torque_impulse(real_t p_torque) { + body->apply_torque_impulse(p_torque); +} + +void PhysicsDirectBodyState2DSW::set_sleep_state(bool p_enable) { + body->set_active(!p_enable); +} + +bool PhysicsDirectBodyState2DSW::is_sleeping() const { + return !body->is_active(); +} + +int PhysicsDirectBodyState2DSW::get_contact_count() const { + return body->contact_count; +} + +Vector2 PhysicsDirectBodyState2DSW::get_contact_local_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].local_pos; +} + +Vector2 PhysicsDirectBodyState2DSW::get_contact_local_normal(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].local_normal; +} + +int PhysicsDirectBodyState2DSW::get_contact_local_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); + return body->contacts[p_contact_idx].local_shape; +} + +RID PhysicsDirectBodyState2DSW::get_contact_collider(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); + return body->contacts[p_contact_idx].collider; +} +Vector2 PhysicsDirectBodyState2DSW::get_contact_collider_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].collider_pos; +} + +ObjectID PhysicsDirectBodyState2DSW::get_contact_collider_id(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); + return body->contacts[p_contact_idx].collider_instance_id; +} + +int PhysicsDirectBodyState2DSW::get_contact_collider_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); + return body->contacts[p_contact_idx].collider_shape; +} + +Vector2 PhysicsDirectBodyState2DSW::get_contact_collider_velocity_at_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].collider_velocity_at_pos; +} + +Variant PhysicsDirectBodyState2DSW::get_contact_collider_shape_metadata(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Variant()); + + if (!PhysicsServer2DSW::singletonsw->body_owner.owns(body->contacts[p_contact_idx].collider)) { + return Variant(); + } + Body2DSW *other = PhysicsServer2DSW::singletonsw->body_owner.getornull(body->contacts[p_contact_idx].collider); + + int sidx = body->contacts[p_contact_idx].collider_shape; + if (sidx < 0 || sidx >= other->get_shape_count()) { + return Variant(); + } + + return other->get_shape_metadata(sidx); +} + +PhysicsDirectSpaceState2D *PhysicsDirectBodyState2DSW::get_space_state() { + return body->get_space()->get_direct_state(); +} + +real_t PhysicsDirectBodyState2DSW::get_step() const { + return body->get_space()->get_last_step(); +} diff --git a/servers/physics_2d/body_direct_state_2d_sw.h b/servers/physics_2d/body_direct_state_2d_sw.h new file mode 100644 index 0000000000..34faa174d8 --- /dev/null +++ b/servers/physics_2d/body_direct_state_2d_sw.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* body_direct_state_2d_sw.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 BODY_DIRECT_STATE_2D_SW_H +#define BODY_DIRECT_STATE_2D_SW_H + +#include "servers/physics_server_2d.h" + +class Body2DSW; + +class PhysicsDirectBodyState2DSW : public PhysicsDirectBodyState2D { + GDCLASS(PhysicsDirectBodyState2DSW, PhysicsDirectBodyState2D); + +public: + Body2DSW *body = nullptr; + + virtual Vector2 get_total_gravity() const override; + virtual real_t get_total_angular_damp() const override; + virtual real_t get_total_linear_damp() const override; + + virtual Vector2 get_center_of_mass() const override; + virtual real_t get_inverse_mass() const override; + virtual real_t get_inverse_inertia() const override; + + virtual void set_linear_velocity(const Vector2 &p_velocity) override; + virtual Vector2 get_linear_velocity() const override; + + virtual void set_angular_velocity(real_t p_velocity) override; + virtual real_t get_angular_velocity() const override; + + virtual void set_transform(const Transform2D &p_transform) override; + virtual Transform2D get_transform() const override; + + virtual Vector2 get_velocity_at_local_position(const Vector2 &p_position) const override; + + virtual void add_central_force(const Vector2 &p_force) override; + virtual void add_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override; + virtual void add_torque(real_t p_torque) override; + virtual void apply_central_impulse(const Vector2 &p_impulse) override; + virtual void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override; + virtual void apply_torque_impulse(real_t p_torque) override; + + virtual void set_sleep_state(bool p_enable) override; + virtual bool is_sleeping() const override; + + virtual int get_contact_count() const override; + + virtual Vector2 get_contact_local_position(int p_contact_idx) const override; + virtual Vector2 get_contact_local_normal(int p_contact_idx) const override; + virtual int get_contact_local_shape(int p_contact_idx) const override; + + virtual RID get_contact_collider(int p_contact_idx) const override; + virtual Vector2 get_contact_collider_position(int p_contact_idx) const override; + virtual ObjectID get_contact_collider_id(int p_contact_idx) const override; + virtual int get_contact_collider_shape(int p_contact_idx) const override; + virtual Variant get_contact_collider_shape_metadata(int p_contact_idx) const override; + + virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override; + + virtual PhysicsDirectSpaceState2D *get_space_state() override; + + virtual real_t get_step() const override; +}; + +#endif // BODY_2D_SW_H diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp index 91d747b492..8bcc4609f4 100644 --- a/servers/physics_2d/body_pair_2d_sw.cpp +++ b/servers/physics_2d/body_pair_2d_sw.cpp @@ -298,7 +298,7 @@ bool BodyPair2DSW::setup(real_t p_step) { } if (!prev_collided) { - if (A->is_shape_set_as_one_way_collision(shape_A)) { + if (shape_B_ptr->allows_one_way_collision() && A->is_shape_set_as_one_way_collision(shape_A)) { Vector2 direction = xform_A.get_axis(1).normalized(); bool valid = false; for (int i = 0; i < contact_count; i++) { @@ -319,7 +319,7 @@ bool BodyPair2DSW::setup(real_t p_step) { } } - if (B->is_shape_set_as_one_way_collision(shape_B)) { + if (shape_A_ptr->allows_one_way_collision() && B->is_shape_set_as_one_way_collision(shape_B)) { Vector2 direction = xform_B.get_axis(1).normalized(); bool valid = false; for (int i = 0; i < contact_count; i++) { diff --git a/servers/physics_2d/collision_solver_2d_sat.cpp b/servers/physics_2d/collision_solver_2d_sat.cpp index 30a99d3d74..b1aee01bde 100644 --- a/servers/physics_2d/collision_solver_2d_sat.cpp +++ b/servers/physics_2d/collision_solver_2d_sat.cpp @@ -1115,11 +1115,13 @@ bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Transform2D PhysicsServer2D::ShapeType type_A = p_shape_A->get_type(); ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_WORLD_MARGIN, false); + ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY, false); ERR_FAIL_COND_V(p_shape_A->is_concave(), false); PhysicsServer2D::ShapeType type_B = p_shape_B->get_type(); ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_WORLD_MARGIN, false); + ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_SEPARATION_RAY, false); ERR_FAIL_COND_V(p_shape_B->is_concave(), false); static const CollisionFunc collision_table[5][5] = { @@ -1382,23 +1384,23 @@ bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Transform2D if (p_margin_A || p_margin_B) { if (*motion_A == Vector2() && *motion_B == Vector2()) { - collision_func = collision_table_margin[type_A - 1][type_B - 1]; + collision_func = collision_table_margin[type_A - 2][type_B - 2]; } else if (*motion_A != Vector2() && *motion_B == Vector2()) { - collision_func = collision_table_castA_margin[type_A - 1][type_B - 1]; + collision_func = collision_table_castA_margin[type_A - 2][type_B - 2]; } else if (*motion_A == Vector2() && *motion_B != Vector2()) { - collision_func = collision_table_castB_margin[type_A - 1][type_B - 1]; + collision_func = collision_table_castB_margin[type_A - 2][type_B - 2]; } else { - collision_func = collision_table_castA_castB_margin[type_A - 1][type_B - 1]; + collision_func = collision_table_castA_castB_margin[type_A - 2][type_B - 2]; } } else { if (*motion_A == Vector2() && *motion_B == Vector2()) { - collision_func = collision_table[type_A - 1][type_B - 1]; + collision_func = collision_table[type_A - 2][type_B - 2]; } else if (*motion_A != Vector2() && *motion_B == Vector2()) { - collision_func = collision_table_castA[type_A - 1][type_B - 1]; + collision_func = collision_table_castA[type_A - 2][type_B - 2]; } else if (*motion_A == Vector2() && *motion_B != Vector2()) { - collision_func = collision_table_castB[type_A - 1][type_B - 1]; + collision_func = collision_table_castB[type_A - 2][type_B - 2]; } else { - collision_func = collision_table_castA_castB[type_A - 1][type_B - 1]; + collision_func = collision_table_castA_castB[type_A - 2][type_B - 2]; } } diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp index 8f8a4a862c..ae50615953 100644 --- a/servers/physics_2d/collision_solver_2d_sw.cpp +++ b/servers/physics_2d/collision_solver_2d_sw.cpp @@ -73,6 +73,65 @@ bool CollisionSolver2DSW::solve_static_world_margin(const Shape2DSW *p_shape_A, return found; } +bool CollisionSolver2DSW::solve_separation_ray(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis, real_t p_margin) { + const SeparationRayShape2DSW *ray = static_cast<const SeparationRayShape2DSW *>(p_shape_A); + if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY) { + return false; + } + + Vector2 from = p_transform_A.get_origin(); + Vector2 to = from + p_transform_A[1] * (ray->get_length() + p_margin); + if (p_motion_A != Vector2()) { + //not the best but should be enough + Vector2 normal = (to - from).normalized(); + to += normal * MAX(0.0, normal.dot(p_motion_A)); + } + Vector2 support_A = to; + + Transform2D invb = p_transform_B.affine_inverse(); + from = invb.xform(from); + to = invb.xform(to); + + Vector2 p, n; + if (!p_shape_B->intersect_segment(from, to, p, n)) { + if (r_sep_axis) { + *r_sep_axis = p_transform_A[1].normalized(); + } + return false; + } + + // Discard contacts when the ray is fully contained inside the shape. + if (n == Vector2()) { + if (r_sep_axis) { + *r_sep_axis = p_transform_A[1].normalized(); + } + return false; + } + + // Discard contacts in the wrong direction. + if (n.dot(from - to) < CMP_EPSILON) { + if (r_sep_axis) { + *r_sep_axis = p_transform_A[1].normalized(); + } + return false; + } + + Vector2 support_B = p_transform_B.xform(p); + if (ray->get_slide_on_slope()) { + Vector2 global_n = invb.basis_xform_inv(n).normalized(); + support_B = support_A + (support_B - support_A).length() * global_n; + } + + if (p_result_callback) { + if (p_swap_result) { + p_result_callback(support_B, support_A, p_userdata); + } else { + p_result_callback(support_A, support_B, p_userdata); + } + } + return true; +} + struct _ConcaveCollisionInfo2D { const Transform2D *transform_A; const Shape2DSW *shape_A; @@ -90,23 +149,23 @@ struct _ConcaveCollisionInfo2D { Vector2 *sep_axis; }; -void CollisionSolver2DSW::concave_callback(void *p_userdata, Shape2DSW *p_convex) { +bool CollisionSolver2DSW::concave_callback(void *p_userdata, Shape2DSW *p_convex) { _ConcaveCollisionInfo2D &cinfo = *(_ConcaveCollisionInfo2D *)(p_userdata); cinfo.aabb_tests++; - if (!cinfo.result_callback && cinfo.collided) { - return; //already collided and no contacts requested, don't test anymore - } bool collided = collision_solver(cinfo.shape_A, *cinfo.transform_A, cinfo.motion_A, p_convex, *cinfo.transform_B, cinfo.motion_B, cinfo.result_callback, cinfo.userdata, cinfo.swap_result, cinfo.sep_axis, cinfo.margin_A, cinfo.margin_B); if (!collided) { - return; + return false; } cinfo.collided = true; cinfo.collisions++; + + // Stop at first collision if contacts are not needed. + return !cinfo.result_callback; } -bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis, real_t p_margin_A, real_t p_margin_B) { +bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis, real_t p_margin_A, real_t p_margin_B) { const ConcaveShape2DSW *concave_B = static_cast<const ConcaveShape2DSW *>(p_shape_B); _ConcaveCollisionInfo2D cinfo; @@ -119,7 +178,7 @@ bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transf cinfo.swap_result = p_swap_result; cinfo.collided = false; cinfo.collisions = 0; - cinfo.sep_axis = sep_axis; + cinfo.sep_axis = r_sep_axis; cinfo.margin_A = p_margin_A; cinfo.margin_B = p_margin_B; @@ -150,7 +209,7 @@ bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transf return cinfo.collided; } -bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *sep_axis, real_t p_margin_A, real_t p_margin_B) { +bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *r_sep_axis, real_t p_margin_A, real_t p_margin_B) { PhysicsServer2D::ShapeType type_A = p_shape_A->get_type(); PhysicsServer2D::ShapeType type_B = p_shape_B->get_type(); bool concave_A = p_shape_A->is_concave(); @@ -177,18 +236,29 @@ bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p return solve_static_world_margin(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false); } + } else if (type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY) { + if (type_B == PhysicsServer2D::SHAPE_SEPARATION_RAY) { + return false; //no ray-ray + } + + if (swap) { + return solve_separation_ray(p_shape_B, p_motion_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, r_sep_axis, p_margin_B); + } else { + return solve_separation_ray(p_shape_A, p_motion_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, r_sep_axis, p_margin_A); + } + } else if (concave_B) { if (concave_A) { return false; } if (!swap) { - return solve_concave(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, sep_axis, margin_A, margin_B); + return solve_concave(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, r_sep_axis, margin_A, margin_B); } else { - return solve_concave(p_shape_B, p_transform_B, p_motion_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, sep_axis, margin_A, margin_B); + return solve_concave(p_shape_B, p_transform_B, p_motion_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, r_sep_axis, margin_A, margin_B); } } else { - return collision_solver(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, sep_axis, margin_A, margin_B); + return collision_solver(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, r_sep_axis, margin_A, margin_B); } } diff --git a/servers/physics_2d/collision_solver_2d_sw.h b/servers/physics_2d/collision_solver_2d_sw.h index 17c0c2fe70..62fccc4ff3 100644 --- a/servers/physics_2d/collision_solver_2d_sw.h +++ b/servers/physics_2d/collision_solver_2d_sw.h @@ -39,12 +39,12 @@ public: private: static bool solve_static_world_margin(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result); - static void concave_callback(void *p_userdata, Shape2DSW *p_convex); - static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0); - static bool solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = nullptr); + static bool concave_callback(void *p_userdata, Shape2DSW *p_convex); + static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0); + static bool solve_separation_ray(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin = 0); public: - static bool solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0); + static bool solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0); }; #endif // COLLISION_SOLVER_2D_SW_H diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp index 5a0a628fbc..fa8499a81d 100644 --- a/servers/physics_2d/joints_2d_sw.cpp +++ b/servers/physics_2d/joints_2d_sw.cpp @@ -68,13 +68,13 @@ static inline real_t k_scalar(Body2DSW *a, Body2DSW *b, const Vector2 &rA, const { value += a->get_inv_mass(); - real_t rcn = rA.cross(n); + real_t rcn = (rA - a->get_center_of_mass()).cross(n); value += a->get_inv_inertia() * rcn * rcn; } if (b) { value += b->get_inv_mass(); - real_t rcn = rB.cross(n); + real_t rcn = (rB - b->get_center_of_mass()).cross(n); value += b->get_inv_inertia() * rcn * rcn; } @@ -83,9 +83,9 @@ static inline real_t k_scalar(Body2DSW *a, Body2DSW *b, const Vector2 &rA, const static inline Vector2 relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB) { - Vector2 sum = a->get_linear_velocity() - rA.orthogonal() * a->get_angular_velocity(); + Vector2 sum = a->get_linear_velocity() - (rA - a->get_center_of_mass()).orthogonal() * a->get_angular_velocity(); if (b) { - return (b->get_linear_velocity() - rB.orthogonal() * b->get_angular_velocity()) - sum; + return (b->get_linear_velocity() - (rB - b->get_center_of_mass()).orthogonal() * b->get_angular_velocity()) - sum; } else { return -sum; } @@ -172,11 +172,11 @@ bool PinJoint2DSW::pre_solve(real_t p_step) { void PinJoint2DSW::solve(real_t p_step) { // compute relative velocity - Vector2 vA = A->get_linear_velocity() - custom_cross(rA, A->get_angular_velocity()); + Vector2 vA = A->get_linear_velocity() - custom_cross(rA - A->get_center_of_mass(), A->get_angular_velocity()); Vector2 rel_vel; if (B) { - rel_vel = B->get_linear_velocity() - custom_cross(rB, B->get_angular_velocity()) - vA; + rel_vel = B->get_linear_velocity() - custom_cross(rB - B->get_center_of_mass(), B->get_angular_velocity()) - vA; } else { rel_vel = -vA; } @@ -238,6 +238,9 @@ k_tensor(Body2DSW *a, Body2DSW *b, Vector2 r1, Vector2 r2, Vector2 *k1, Vector2 k21 = 0.0f; k22 = m_sum; + r1 -= a->get_center_of_mass(); + r2 -= b->get_center_of_mass(); + // add the influence from r1 real_t a_i_inv = a->get_inv_inertia(); real_t r1xsq = r1.x * r1.x * a_i_inv; diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp index 88c097453e..d0a42ca95b 100644 --- a/servers/physics_2d/physics_server_2d_sw.cpp +++ b/servers/physics_2d/physics_server_2d_sw.cpp @@ -30,6 +30,7 @@ #include "physics_server_2d_sw.h" +#include "body_direct_state_2d_sw.h" #include "broad_phase_2d_bvh.h" #include "collision_solver_2d_sw.h" #include "core/config/project_settings.h" @@ -45,6 +46,9 @@ RID PhysicsServer2DSW::_shape_create(ShapeType p_shape) { case SHAPE_WORLD_MARGIN: { shape = memnew(WorldMarginShape2DSW); } break; + case SHAPE_SEPARATION_RAY: { + shape = memnew(SeparationRayShape2DSW); + } break; case SHAPE_SEGMENT: { shape = memnew(SegmentShape2DSW); } break; @@ -79,6 +83,10 @@ RID PhysicsServer2DSW::world_margin_shape_create() { return _shape_create(SHAPE_WORLD_MARGIN); } +RID PhysicsServer2DSW::separation_ray_shape_create() { + return _shape_create(SHAPE_SEPARATION_RAY); +} + RID PhysicsServer2DSW::segment_shape_create() { return _shape_create(SHAPE_SEGMENT); } @@ -735,20 +743,27 @@ uint32_t PhysicsServer2DSW::body_get_collision_mask(RID p_body) const { return body->get_collision_mask(); }; -void PhysicsServer2DSW::body_set_param(RID p_body, BodyParameter p_param, real_t p_value) { +void PhysicsServer2DSW::body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) { Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); body->set_param(p_param, p_value); }; -real_t PhysicsServer2DSW::body_get_param(RID p_body, BodyParameter p_param) const { +Variant PhysicsServer2DSW::body_get_param(RID p_body, BodyParameter p_param) const { Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, 0); return body->get_param(p_param); }; +void PhysicsServer2DSW::body_reset_mass_properties(RID p_body) { + Body2DSW *body = body_owner.getornull(p_body); + ERR_FAIL_COND(!body); + + return body->reset_mass_properties(); +} + void PhysicsServer2DSW::body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) { Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); @@ -919,6 +934,12 @@ int PhysicsServer2DSW::body_get_max_contacts_reported(RID p_body) const { return body->get_max_contacts_reported(); } +void PhysicsServer2DSW::body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) { + Body2DSW *body = body_owner.getornull(p_body); + ERR_FAIL_COND(!body); + body->set_state_sync_callback(p_instance, p_callback); +} + void PhysicsServer2DSW::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata) { Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); @@ -939,7 +960,7 @@ void PhysicsServer2DSW::body_set_pickable(RID p_body, bool p_pickable) { body->set_pickable(p_pickable); } -bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, MotionResult *r_result, const Set<RID> &p_exclude) { +bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) { Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); @@ -947,23 +968,19 @@ bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, _update_shapes(); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_exclude); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude); } PhysicsDirectBodyState2D *PhysicsServer2DSW::body_get_direct_state(RID p_body) { ERR_FAIL_COND_V_MSG((using_threads && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); - if (!body_owner.owns(p_body)) { - return nullptr; - } - Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, nullptr); + ERR_FAIL_COND_V(!body->get_space(), nullptr); ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); - direct_state->body = body; - return direct_state; + return body->get_direct_state(); } /* JOINT API */ @@ -1227,10 +1244,8 @@ void PhysicsServer2DSW::set_collision_iterations(int p_iterations) { void PhysicsServer2DSW::init() { doing_sync = false; - last_step = 0.001; iterations = 8; // 8? stepper = memnew(Step2DSW); - direct_state = memnew(PhysicsDirectBodyState2DSW); }; void PhysicsServer2DSW::step(real_t p_step) { @@ -1240,8 +1255,6 @@ void PhysicsServer2DSW::step(real_t p_step) { _update_shapes(); - last_step = p_step; - PhysicsDirectBodyState2DSW::singleton->step = p_step; island_count = 0; active_objects = 0; collision_pairs = 0; @@ -1313,7 +1326,6 @@ void PhysicsServer2DSW::end_sync() { void PhysicsServer2DSW::finish() { memdelete(stepper); - memdelete(direct_state); }; void PhysicsServer2DSW::_update_shapes() { diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h index 3610f43f93..6a2d9e37e0 100644 --- a/servers/physics_2d/physics_server_2d_sw.h +++ b/servers/physics_2d/physics_server_2d_sw.h @@ -46,7 +46,6 @@ class PhysicsServer2DSW : public PhysicsServer2D { bool active; int iterations; bool doing_sync; - real_t last_step; int island_count; int active_objects; @@ -59,8 +58,6 @@ class PhysicsServer2DSW : public PhysicsServer2D { Step2DSW *stepper; Set<const Space2DSW *> active_spaces; - PhysicsDirectBodyState2DSW *direct_state; - mutable RID_PtrOwner<Shape2DSW, true> shape_owner; mutable RID_PtrOwner<Space2DSW, true> space_owner; mutable RID_PtrOwner<Area2DSW, true> area_owner; @@ -88,6 +85,7 @@ public: }; virtual RID world_margin_shape_create() override; + virtual RID separation_ray_shape_create() override; virtual RID segment_shape_create() override; virtual RID circle_shape_create() override; virtual RID rectangle_shape_create() override; @@ -207,8 +205,10 @@ public: virtual void body_set_collision_mask(RID p_body, uint32_t p_mask) override; virtual uint32_t body_get_collision_mask(RID p_body) const override; - virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value) override; - virtual real_t body_get_param(RID p_body, BodyParameter p_param) const override; + virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) override; + virtual Variant body_get_param(RID p_body, BodyParameter p_param) const override; + + virtual void body_reset_mass_properties(RID p_body) override; virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override; virtual Variant body_get_state(RID p_body, BodyState p_state) const override; @@ -241,12 +241,14 @@ public: virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override; virtual int body_get_max_contacts_reported(RID p_body) const override; + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) override; virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override; + virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override; virtual void body_set_pickable(RID p_body, bool p_pickable) override; - virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override; + virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override; // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override; diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h index b93178919d..e65c4f5f3a 100644 --- a/servers/physics_2d/physics_server_2d_wrap_mt.h +++ b/servers/physics_2d/physics_server_2d_wrap_mt.h @@ -80,6 +80,7 @@ public: //FUNC1RID(shape,ShapeType); todo fix FUNCRID(world_margin_shape) + FUNCRID(separation_ray_shape) FUNCRID(segment_shape) FUNCRID(circle_shape) FUNCRID(rectangle_shape) @@ -211,8 +212,10 @@ public: FUNC2(body_set_collision_mask, RID, uint32_t); FUNC1RC(uint32_t, body_get_collision_mask, RID); - FUNC3(body_set_param, RID, BodyParameter, real_t); - FUNC2RC(real_t, body_get_param, RID, BodyParameter); + FUNC3(body_set_param, RID, BodyParameter, const Variant &); + FUNC2RC(Variant, body_get_param, RID, BodyParameter); + + FUNC1(body_reset_mass_properties, RID); FUNC3(body_set_state, RID, BodyState, const Variant &); FUNC2RC(Variant, body_get_state, RID, BodyState); @@ -244,6 +247,7 @@ public: FUNC2(body_set_omit_force_integration, RID, bool); FUNC1RC(bool, body_is_omitting_force_integration, RID); + FUNC3(body_set_state_sync_callback, RID, void *, BodyStateCallback); FUNC3(body_set_force_integration_callback, RID, const Callable &, const Variant &); bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override { @@ -252,9 +256,9 @@ public: FUNC2(body_set_pickable, RID, bool); - bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override { + bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override { ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); - return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_exclude); + return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude); } // this function only works on physics process, errors and returns null otherwise diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp index b3e4ca84c3..064c4afe52 100644 --- a/servers/physics_2d/shape_2d_sw.cpp +++ b/servers/physics_2d/shape_2d_sw.cpp @@ -143,6 +143,46 @@ Variant WorldMarginShape2DSW::get_data() const { /*********************************************************/ /*********************************************************/ +void SeparationRayShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const { + r_amount = 1; + + if (p_normal.y > 0) { + *r_supports = Vector2(0, length); + } else { + *r_supports = Vector2(); + } +} + +bool SeparationRayShape2DSW::contains_point(const Vector2 &p_point) const { + return false; +} + +bool SeparationRayShape2DSW::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const { + return false; //rays can't be intersected +} + +real_t SeparationRayShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const { + return 0; //rays are mass-less +} + +void SeparationRayShape2DSW::set_data(const Variant &p_data) { + Dictionary d = p_data; + length = d["length"]; + slide_on_slope = d["slide_on_slope"]; + configure(Rect2(0, 0, 0.001, length)); +} + +Variant SeparationRayShape2DSW::get_data() const { + Dictionary d; + d["length"] = length; + d["slide_on_slope"] = slide_on_slope; + return d; +} + +/*********************************************************/ +/*********************************************************/ +/*********************************************************/ + void SegmentShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const { if (Math::abs(p_normal.dot(n)) > _SEGMENT_IS_VALID_SUPPORT_THRESHOLD) { r_supports[0] = a; @@ -530,14 +570,7 @@ bool ConvexPolygonShape2DSW::intersect_segment(const Vector2 &p_begin, const Vec } } - if (inters) { - if (n.dot(r_normal) > 0) { - r_normal = -r_normal; - } - } - - //return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal); - return inters; //todo + return inters; } real_t ConvexPolygonShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const { @@ -888,7 +921,7 @@ Variant ConcavePolygonShape2DSW::get_data() const { return rsegments; } -void ConcavePolygonShape2DSW::cull(const Rect2 &p_local_aabb, Callback p_callback, void *p_userdata) const { +void ConcavePolygonShape2DSW::cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const { uint32_t *stack = (uint32_t *)alloca(sizeof(int) * bvh_depth); enum { @@ -936,7 +969,9 @@ void ConcavePolygonShape2DSW::cull(const Rect2 &p_local_aabb, Callback p_callbac SegmentShape2DSW ss(a, b, (b - a).orthogonal().normalized()); - p_callback(p_userdata, &ss); + if (p_callback(p_userdata, &ss)) { + return; + } stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { diff --git a/servers/physics_2d/shape_2d_sw.h b/servers/physics_2d/shape_2d_sw.h index 45d3379dfa..1185d343ee 100644 --- a/servers/physics_2d/shape_2d_sw.h +++ b/servers/physics_2d/shape_2d_sw.h @@ -64,6 +64,8 @@ public: _FORCE_INLINE_ Rect2 get_aabb() const { return aabb; } _FORCE_INLINE_ bool is_configured() const { return configured; } + virtual bool allows_one_way_collision() const { return true; } + virtual bool is_concave() const { return false; } virtual bool contains_point(const Vector2 &p_point) const = 0; @@ -177,6 +179,43 @@ public: } }; +class SeparationRayShape2DSW : public Shape2DSW { + real_t length; + bool slide_on_slope; + +public: + _FORCE_INLINE_ real_t get_length() const { return length; } + _FORCE_INLINE_ bool get_slide_on_slope() const { return slide_on_slope; } + + virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_SEPARATION_RAY; } + + virtual bool allows_one_way_collision() const override { return false; } + + virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); } + virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override; + + virtual bool contains_point(const Vector2 &p_point) const override; + virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override; + virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override; + + virtual void set_data(const Variant &p_data) override; + virtual Variant get_data() const override; + + _FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const { + //real large + r_max = p_normal.dot(p_transform.get_origin()); + r_min = p_normal.dot(p_transform.xform(Vector2(0, length))); + if (r_max < r_min) { + SWAP(r_max, r_min); + } + } + + DEFAULT_PROJECT_RANGE_CAST + + _FORCE_INLINE_ SeparationRayShape2DSW() {} + _FORCE_INLINE_ SeparationRayShape2DSW(real_t p_length) { length = p_length; } +}; + class SegmentShape2DSW : public Shape2DSW { Vector2 a; Vector2 b; @@ -426,9 +465,11 @@ public: class ConcaveShape2DSW : public Shape2DSW { public: virtual bool is_concave() const override { return true; } - typedef void (*Callback)(void *p_userdata, Shape2DSW *p_convex); - virtual void cull(const Rect2 &p_local_aabb, Callback p_callback, void *p_userdata) const = 0; + // Returns true to stop the query. + typedef bool (*QueryCallback)(void *p_userdata, Shape2DSW *p_convex); + + virtual void cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const = 0; }; class ConcavePolygonShape2DSW : public ConcaveShape2DSW { @@ -486,7 +527,7 @@ public: virtual void set_data(const Variant &p_data) override; virtual Variant get_data() const override; - virtual void cull(const Rect2 &p_local_aabb, Callback p_callback, void *p_userdata) const override; + virtual void cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const override; DEFAULT_PROJECT_RANGE_CAST }; diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index f04f3ab583..7dbd1243cc 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -482,7 +482,7 @@ bool PhysicsDirectSpaceState2DSW::rest_info(RID p_shape, const Transform2D &p_sh r_info->metadata = rcd.best_object->get_shape_metadata(rcd.best_shape); if (rcd.best_object->get_type() == CollisionObject2DSW::TYPE_BODY) { const Body2DSW *body = static_cast<const Body2DSW *>(rcd.best_object); - Vector2 rel_vec = r_info->point - body->get_transform().get_origin(); + Vector2 rel_vec = r_info->point - (body->get_transform().get_origin() + body->get_center_of_mass()); r_info->linear_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity(); } else { @@ -528,7 +528,7 @@ int Space2DSW::_cull_aabb_for_body(Body2DSW *p_body, const Rect2 &p_aabb) { return amount; } -bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, const Set<RID> &p_exclude) { +bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) { //give me back regular physics engine logic //this is madness //and most people using this function will think @@ -621,7 +621,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); - if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) { + if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(shape_idx)) { cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized(); real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx); @@ -634,7 +634,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co //fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction Vector2 lv = b->get_linear_velocity(); //compute displacement from linear velocity - Vector2 motion = lv * PhysicsDirectBodyState2DSW::singleton->step; + Vector2 motion = lv * last_step; real_t motion_len = motion.length(); motion.normalize(); cbk.valid_depth += motion_len * MAX(motion.dot(-cbk.valid_dir), 0.0); @@ -726,6 +726,16 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co } Shape2DSW *body_shape = p_body->get_shape(body_shape_idx); + + // Colliding separation rays allows to properly snap to the ground, + // otherwise it's not needed in regular motion. + if (!p_collide_separation_ray && (body_shape->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY)) { + // When slide on slope is on, separation ray shape acts like a regular shape. + if (!static_cast<SeparationRayShape2DSW *>(body_shape)->get_slide_on_slope()) { + continue; + } + } + Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx); bool stuck = false; @@ -762,7 +772,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co //test initial overlap if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, nullptr, 0)) { - if (col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) { + if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) { Vector2 direction = col_obj_shape_xform.get_axis(1).normalized(); if (motion_normal.dot(direction) < 0) { continue; @@ -806,7 +816,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co } } - if (col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) { + if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) { Vector2 cd[2]; PhysicsServer2DSW::CollCbkData cbk; cbk.max = 1; @@ -904,7 +914,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); - if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) { + if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(shape_idx)) { rcd.valid_dir = col_obj_shape_xform.get_axis(1).normalized(); real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx); @@ -916,7 +926,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co //fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction Vector2 lv = b->get_linear_velocity(); //compute displacement from linear velocity - Vector2 motion = lv * PhysicsDirectBodyState2DSW::singleton->step; + Vector2 motion = lv * last_step; real_t motion_len = motion.length(); motion.normalize(); rcd.valid_depth += motion_len * MAX(motion.dot(-rcd.valid_dir), 0.0); @@ -951,7 +961,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co r_result->collider_metadata = rcd.best_object->get_shape_metadata(rcd.best_shape); const Body2DSW *body = static_cast<const Body2DSW *>(rcd.best_object); - Vector2 rel_vec = r_result->collision_point - body->get_transform().get_origin(); + Vector2 rel_vec = r_result->collision_point - (body->get_transform().get_origin() + body->get_center_of_mass()); r_result->collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity(); r_result->travel = safe * p_motion; @@ -1031,12 +1041,12 @@ void Space2DSW::body_remove_from_active_list(SelfList<Body2DSW> *p_body) { active_list.remove(p_body); } -void Space2DSW::body_add_to_inertia_update_list(SelfList<Body2DSW> *p_body) { - inertia_update_list.add(p_body); +void Space2DSW::body_add_to_mass_properties_update_list(SelfList<Body2DSW> *p_body) { + mass_properties_update_list.add(p_body); } -void Space2DSW::body_remove_from_inertia_update_list(SelfList<Body2DSW> *p_body) { - inertia_update_list.remove(p_body); +void Space2DSW::body_remove_from_mass_properties_update_list(SelfList<Body2DSW> *p_body) { + mass_properties_update_list.remove(p_body); } BroadPhase2DSW *Space2DSW::get_broadphase() { @@ -1102,9 +1112,9 @@ void Space2DSW::call_queries() { void Space2DSW::setup() { contact_debug_count = 0; - while (inertia_update_list.first()) { - inertia_update_list.first()->self()->update_inertias(); - inertia_update_list.remove(inertia_update_list.first()); + while (mass_properties_update_list.first()) { + mass_properties_update_list.first()->self()->update_mass_properties(); + mass_properties_update_list.remove(mass_properties_update_list.first()); } } diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 3be36852b0..ad82a14af5 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -49,13 +49,13 @@ class PhysicsDirectSpaceState2DSW : public PhysicsDirectSpaceState2D { public: Space2DSW *space; - virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) override; - virtual int intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) override; - virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) override; + virtual int intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) override; + virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; PhysicsDirectSpaceState2DSW(); }; @@ -86,7 +86,7 @@ private: BroadPhase2DSW *broadphase; SelfList<Body2DSW>::List active_list; - SelfList<Body2DSW>::List inertia_update_list; + SelfList<Body2DSW>::List mass_properties_update_list; SelfList<Body2DSW>::List state_query_list; SelfList<Area2DSW>::List monitor_query_list; SelfList<Area2DSW>::List area_moved_list; @@ -117,6 +117,8 @@ private: bool locked; + real_t last_step = 0.001; + int island_count; int active_objects; int collision_pairs; @@ -138,8 +140,8 @@ public: const SelfList<Body2DSW>::List &get_active_body_list() const; void body_add_to_active_list(SelfList<Body2DSW> *p_body); void body_remove_from_active_list(SelfList<Body2DSW> *p_body); - void body_add_to_inertia_update_list(SelfList<Body2DSW> *p_body); - void body_remove_from_inertia_update_list(SelfList<Body2DSW> *p_body); + void body_add_to_mass_properties_update_list(SelfList<Body2DSW> *p_body); + void body_remove_from_mass_properties_update_list(SelfList<Body2DSW> *p_body); void area_add_to_moved_list(SelfList<Area2DSW> *p_area); void area_remove_from_moved_list(SelfList<Area2DSW> *p_area); const SelfList<Area2DSW>::List &get_moved_area_list() const; @@ -172,6 +174,9 @@ public: void lock(); void unlock(); + real_t get_last_step() const { return last_step; } + void set_last_step(real_t p_step) { last_step = p_step; } + void set_param(PhysicsServer2D::SpaceParameter p_param, real_t p_value); real_t get_param(PhysicsServer2D::SpaceParameter p_param) const; @@ -183,7 +188,7 @@ public: int get_collision_pairs() const { return collision_pairs; } - bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, const Set<RID> &p_exclude = Set<RID>()); + bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); } _FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.is_empty(); } diff --git a/servers/physics_2d/step_2d_sw.cpp b/servers/physics_2d/step_2d_sw.cpp index 8b30160cc1..0306ec5050 100644 --- a/servers/physics_2d/step_2d_sw.cpp +++ b/servers/physics_2d/step_2d_sw.cpp @@ -129,6 +129,8 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) { p_space->setup(); //update inertias, etc + p_space->set_last_step(p_delta); + iterations = p_iterations; delta = p_delta; diff --git a/servers/physics_3d/area_3d_sw.cpp b/servers/physics_3d/area_3d_sw.cpp index c292abad48..c9e8bcb8ca 100644 --- a/servers/physics_3d/area_3d_sw.cpp +++ b/servers/physics_3d/area_3d_sw.cpp @@ -304,6 +304,26 @@ void Area3DSW::call_queries() { } } +void Area3DSW::compute_gravity(const Vector3 &p_position, Vector3 &r_gravity) const { + if (is_gravity_point()) { + const real_t gravity_distance_scale = get_gravity_distance_scale(); + Vector3 v = get_transform().xform(get_gravity_vector()) - p_position; + if (gravity_distance_scale > 0) { + const real_t v_length = v.length(); + if (v_length > 0) { + const real_t v_scaled = v_length * gravity_distance_scale; + r_gravity = (v.normalized() * (get_gravity() / (v_scaled * v_scaled))); + } else { + r_gravity = Vector3(); + } + } else { + r_gravity = v.normalized() * get_gravity(); + } + } else { + r_gravity = get_gravity_vector() * get_gravity(); + } +} + Area3DSW::Area3DSW() : CollisionObject3DSW(TYPE_AREA), monitor_query_list(this), diff --git a/servers/physics_3d/area_3d_sw.h b/servers/physics_3d/area_3d_sw.h index 814472885c..d5f1e60119 100644 --- a/servers/physics_3d/area_3d_sw.h +++ b/servers/physics_3d/area_3d_sw.h @@ -34,7 +34,6 @@ #include "collision_object_3d_sw.h" #include "core/templates/self_list.h" #include "servers/physics_server_3d.h" -//#include "servers/physics_3d/query_sw.h" class Space3DSW; class Body3DSW; @@ -101,18 +100,12 @@ class Area3DSW : public CollisionObject3DSW { Map<BodyKey, BodyState> monitored_bodies; Map<BodyKey, BodyState> monitored_areas; - //virtual void shape_changed_notify(ShapeSW *p_shape); - //virtual void shape_deleted_notify(ShapeSW *p_shape); - Set<Constraint3DSW *> constraints; virtual void _shapes_changed(); void _queue_monitor_update(); public: - //_FORCE_INLINE_ const Transform& get_inverse_transform() const { return inverse_transform; } - //_FORCE_INLINE_ SpaceSW* get_owner() { return owner; } - void set_monitor_callback(ObjectID p_id, const StringName &p_method); _FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback_id.is_valid(); } @@ -184,6 +177,8 @@ public: void call_queries(); + void compute_gravity(const Vector3 &p_position, Vector3 &r_gravity) const; + Area3DSW(); ~Area3DSW(); }; diff --git a/servers/physics_3d/area_pair_3d_sw.cpp b/servers/physics_3d/area_pair_3d_sw.cpp index e740565da6..bf4f0035b4 100644 --- a/servers/physics_3d/area_pair_3d_sw.cpp +++ b/servers/physics_3d/area_pair_3d_sw.cpp @@ -33,7 +33,7 @@ bool AreaPair3DSW::setup(real_t p_step) { bool result = false; - if (area->interacts_with(body) && CollisionSolver3DSW::solve_static(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), nullptr, this)) { + if (area->collides_with(body) && CollisionSolver3DSW::solve_static(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), nullptr, this)) { result = true; } @@ -109,46 +109,51 @@ AreaPair3DSW::~AreaPair3DSW() { //////////////////////////////////////////////////// bool Area2Pair3DSW::setup(real_t p_step) { - bool result = false; - if (area_a->interacts_with(area_b) && CollisionSolver3DSW::solve_static(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), nullptr, this)) { - result = true; + bool result_a = area_a->collides_with(area_b); + bool result_b = area_b->collides_with(area_a); + if ((result_a || result_b) && !CollisionSolver3DSW::solve_static(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), nullptr, this)) { + result_a = false; + result_b = false; } - process_collision = false; - if (result != colliding) { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { - process_collision = true; - } else if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { + bool process_collision = false; + + process_collision_a = false; + if (result_a != colliding_a) { + if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { + process_collision_a = true; process_collision = true; } + colliding_a = result_a; + } - colliding = result; + process_collision_b = false; + if (result_b != colliding_b) { + if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { + process_collision_b = true; + process_collision = true; + } + colliding_b = result_b; } return process_collision; } bool Area2Pair3DSW::pre_solve(real_t p_step) { - if (!process_collision) { - return false; + if (process_collision_a) { + if (colliding_a) { + area_a->add_area_to_query(area_b, shape_b, shape_a); + } else { + area_a->remove_area_from_query(area_b, shape_b, shape_a); + } } - if (colliding) { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { + if (process_collision_b) { + if (colliding_b) { area_b->add_area_to_query(area_a, shape_a, shape_b); - } - - if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { - area_a->add_area_to_query(area_b, shape_b, shape_a); - } - } else { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) { + } else { area_b->remove_area_from_query(area_a, shape_a, shape_b); } - - if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) { - area_a->remove_area_from_query(area_b, shape_b, shape_a); - } } return false; // Never do any post solving. @@ -168,16 +173,18 @@ Area2Pair3DSW::Area2Pair3DSW(Area3DSW *p_area_a, int p_shape_a, Area3DSW *p_area } Area2Pair3DSW::~Area2Pair3DSW() { - if (colliding) { - if (area_b->has_area_monitor_callback()) { - area_b->remove_area_from_query(area_a, shape_a, shape_b); - } - + if (colliding_a) { if (area_a->has_area_monitor_callback()) { area_a->remove_area_from_query(area_b, shape_b, shape_a); } } + if (colliding_b) { + if (area_b->has_area_monitor_callback()) { + area_b->remove_area_from_query(area_a, shape_a, shape_b); + } + } + area_a->remove_constraint(this); area_b->remove_constraint(this); } @@ -187,7 +194,7 @@ Area2Pair3DSW::~Area2Pair3DSW() { bool AreaSoftBodyPair3DSW::setup(real_t p_step) { bool result = false; if ( - area->interacts_with(soft_body) && + area->collides_with(soft_body) && CollisionSolver3DSW::solve_static( soft_body->get_shape(soft_body_shape), soft_body->get_transform() * soft_body->get_shape_transform(soft_body_shape), diff --git a/servers/physics_3d/area_pair_3d_sw.h b/servers/physics_3d/area_pair_3d_sw.h index 8cc9e9ad63..4572dcbb23 100644 --- a/servers/physics_3d/area_pair_3d_sw.h +++ b/servers/physics_3d/area_pair_3d_sw.h @@ -58,8 +58,10 @@ class Area2Pair3DSW : public Constraint3DSW { Area3DSW *area_b; int shape_a; int shape_b; - bool colliding = false; - bool process_collision = false; + bool colliding_a = false; + bool colliding_b = false; + bool process_collision_a = false; + bool process_collision_b = false; public: virtual bool setup(real_t p_step) override; diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp index 0c4079332d..41745545d8 100644 --- a/servers/physics_3d/body_3d_sw.cpp +++ b/servers/physics_3d/body_3d_sw.cpp @@ -29,12 +29,14 @@ /*************************************************************************/ #include "body_3d_sw.h" + #include "area_3d_sw.h" +#include "body_direct_state_3d_sw.h" #include "space_3d_sw.h" -void Body3DSW::_update_inertia() { - if (get_space() && !inertia_update_list.in_list()) { - get_space()->body_add_to_inertia_update_list(&inertia_update_list); +void Body3DSW::_mass_properties_changed() { + if (get_space() && !mass_properties_update_list.in_list() && (calculate_inertia || calculate_center_of_mass)) { + get_space()->body_add_to_mass_properties_update_list(&mass_properties_update_list); } } @@ -42,7 +44,7 @@ void Body3DSW::_update_transform_dependant() { center_of_mass = get_transform().basis.xform(center_of_mass_local); principal_inertia_axes = get_transform().basis * principal_inertia_axes_local; - // update inertia tensor + // Update inertia tensor. Basis tb = principal_inertia_axes; Basis tbt = tb.transposed(); Basis diag; @@ -50,74 +52,95 @@ void Body3DSW::_update_transform_dependant() { _inv_inertia_tensor = tb * diag * tbt; } -void Body3DSW::update_inertias() { +void Body3DSW::update_mass_properties() { // Update shapes and motions. switch (mode) { case PhysicsServer3D::BODY_MODE_DYNAMIC: { - // Update tensor for all shapes, not the best way but should be somehow OK. (inspired from bullet) real_t total_area = 0; - for (int i = 0; i < get_shape_count(); i++) { + if (is_shape_disabled(i)) { + continue; + } + total_area += get_shape_area(i); } - // We have to recompute the center of mass. - center_of_mass_local.zero(); + if (calculate_center_of_mass) { + // We have to recompute the center of mass. + center_of_mass_local.zero(); - if (total_area != 0.0) { - for (int i = 0; i < get_shape_count(); i++) { - real_t area = get_shape_area(i); + if (total_area != 0.0) { + for (int i = 0; i < get_shape_count(); i++) { + if (is_shape_disabled(i)) { + continue; + } - real_t mass = area * this->mass / total_area; + real_t area = get_shape_area(i); - // NOTE: we assume that the shape origin is also its center of mass. - center_of_mass_local += mass * get_shape_transform(i).origin; - } + real_t mass = area * this->mass / total_area; + + // NOTE: we assume that the shape origin is also its center of mass. + center_of_mass_local += mass * get_shape_transform(i).origin; + } - center_of_mass_local /= mass; + center_of_mass_local /= mass; + } } - // Recompute the inertia tensor. - Basis inertia_tensor; - inertia_tensor.set_zero(); - bool inertia_set = false; + if (calculate_inertia) { + // Recompute the inertia tensor. + Basis inertia_tensor; + inertia_tensor.set_zero(); + bool inertia_set = false; - for (int i = 0; i < get_shape_count(); i++) { - if (is_shape_disabled(i)) { - continue; - } + for (int i = 0; i < get_shape_count(); i++) { + if (is_shape_disabled(i)) { + continue; + } - real_t area = get_shape_area(i); - if (area == 0.0) { - continue; - } + real_t area = get_shape_area(i); + if (area == 0.0) { + continue; + } + + inertia_set = true; - inertia_set = true; + const Shape3DSW *shape = get_shape(i); - const Shape3DSW *shape = get_shape(i); + real_t mass = area * this->mass / total_area; - real_t mass = area * this->mass / total_area; + Basis shape_inertia_tensor = shape->get_moment_of_inertia(mass).to_diagonal_matrix(); + Transform3D shape_transform = get_shape_transform(i); + Basis shape_basis = shape_transform.basis.orthonormalized(); - Basis shape_inertia_tensor = shape->get_moment_of_inertia(mass).to_diagonal_matrix(); - Transform3D shape_transform = get_shape_transform(i); - Basis shape_basis = shape_transform.basis.orthonormalized(); + // NOTE: we don't take the scale of collision shapes into account when computing the inertia tensor! + shape_inertia_tensor = shape_basis * shape_inertia_tensor * shape_basis.transposed(); - // NOTE: we don't take the scale of collision shapes into account when computing the inertia tensor! - shape_inertia_tensor = shape_basis * shape_inertia_tensor * shape_basis.transposed(); + Vector3 shape_origin = shape_transform.origin - center_of_mass_local; + inertia_tensor += shape_inertia_tensor + (Basis() * shape_origin.dot(shape_origin) - shape_origin.outer(shape_origin)) * mass; + } - Vector3 shape_origin = shape_transform.origin - center_of_mass_local; - inertia_tensor += shape_inertia_tensor + (Basis() * shape_origin.dot(shape_origin) - shape_origin.outer(shape_origin)) * mass; - } + // Set the inertia to a valid value when there are no valid shapes. + if (!inertia_set) { + inertia_tensor.set_diagonal(Vector3(1.0, 1.0, 1.0)); + } - // Set the inertia to a valid value when there are no valid shapes. - if (!inertia_set) { - inertia_tensor.set_diagonal(Vector3(1.0, 1.0, 1.0)); - } + // Handle partial custom inertia. + if (inertia.x > 0.0) { + inertia_tensor[0][0] = inertia.x; + } + if (inertia.y > 0.0) { + inertia_tensor[1][1] = inertia.y; + } + if (inertia.z > 0.0) { + inertia_tensor[2][2] = inertia.z; + } - // Compute the principal axes of inertia. - principal_inertia_axes_local = inertia_tensor.diagonalize().transposed(); - _inv_inertia = inertia_tensor.get_main_diagonal().inverse(); + // Compute the principal axes of inertia. + principal_inertia_axes_local = inertia_tensor.diagonalize().transposed(); + _inv_inertia = inertia_tensor.get_main_diagonal().inverse(); + } if (mass) { _inv_mass = 1.0 / mass; @@ -126,10 +149,9 @@ void Body3DSW::update_inertias() { } } break; - case PhysicsServer3D::BODY_MODE_KINEMATIC: case PhysicsServer3D::BODY_MODE_STATIC: { - _inv_inertia_tensor.set_zero(); + _inv_inertia = Vector3(); _inv_mass = 0; } break; case PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED: { @@ -139,11 +161,15 @@ void Body3DSW::update_inertias() { } break; } - //_update_shapes(); - _update_transform_dependant(); } +void Body3DSW::reset_mass_properties() { + calculate_inertia = true; + calculate_center_of_mass = true; + _mass_properties_changed(); +} + void Body3DSW::set_active(bool p_active) { if (active == p_active) { return; @@ -163,7 +189,7 @@ void Body3DSW::set_active(bool p_active) { } } -void Body3DSW::set_param(PhysicsServer3D::BodyParameter p_param, real_t p_value) { +void Body3DSW::set_param(PhysicsServer3D::BodyParameter p_param, const Variant &p_value) { switch (p_param) { case PhysicsServer3D::BODY_PARAM_BOUNCE: { bounce = p_value; @@ -172,10 +198,33 @@ void Body3DSW::set_param(PhysicsServer3D::BodyParameter p_param, real_t p_value) friction = p_value; } break; case PhysicsServer3D::BODY_PARAM_MASS: { - ERR_FAIL_COND(p_value <= 0); - mass = p_value; - _update_inertia(); - + real_t mass_value = p_value; + ERR_FAIL_COND(mass_value <= 0); + mass = mass_value; + if (mode >= PhysicsServer3D::BODY_MODE_DYNAMIC) { + _mass_properties_changed(); + } + } break; + case PhysicsServer3D::BODY_PARAM_INERTIA: { + inertia = p_value; + if ((inertia.x <= 0.0) || (inertia.y <= 0.0) || (inertia.z <= 0.0)) { + calculate_inertia = true; + if (mode == PhysicsServer3D::BODY_MODE_DYNAMIC) { + _mass_properties_changed(); + } + } else { + calculate_inertia = false; + if (mode == PhysicsServer3D::BODY_MODE_DYNAMIC) { + principal_inertia_axes_local.set_diagonal(Vector3(1.0, 1.0, 1.0)); + _inv_inertia = inertia.inverse(); + _update_transform_dependant(); + } + } + } break; + case PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS: { + calculate_center_of_mass = false; + center_of_mass_local = p_value; + _update_transform_dependant(); } break; case PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE: { gravity_scale = p_value; @@ -191,7 +240,7 @@ void Body3DSW::set_param(PhysicsServer3D::BodyParameter p_param, real_t p_value) } } -real_t Body3DSW::get_param(PhysicsServer3D::BodyParameter p_param) const { +Variant Body3DSW::get_param(PhysicsServer3D::BodyParameter p_param) const { switch (p_param) { case PhysicsServer3D::BODY_PARAM_BOUNCE: { return bounce; @@ -202,6 +251,16 @@ real_t Body3DSW::get_param(PhysicsServer3D::BodyParameter p_param) const { case PhysicsServer3D::BODY_PARAM_MASS: { return mass; } break; + case PhysicsServer3D::BODY_PARAM_INERTIA: { + if (mode == PhysicsServer3D::BODY_MODE_DYNAMIC) { + return _inv_inertia.inverse(); + } else { + return Vector3(); + } + } break; + case PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS: { + return center_of_mass; + } break; case PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE: { return gravity_scale; } break; @@ -224,40 +283,42 @@ void Body3DSW::set_mode(PhysicsServer3D::BodyMode p_mode) { mode = p_mode; switch (p_mode) { - //CLEAR UP EVERYTHING IN CASE IT NOT WORKS! case PhysicsServer3D::BODY_MODE_STATIC: case PhysicsServer3D::BODY_MODE_KINEMATIC: { _set_inv_transform(get_transform().affine_inverse()); _inv_mass = 0; + _inv_inertia = Vector3(); _set_static(p_mode == PhysicsServer3D::BODY_MODE_STATIC); - //set_active(p_mode==PhysicsServer3D::BODY_MODE_KINEMATIC); set_active(p_mode == PhysicsServer3D::BODY_MODE_KINEMATIC && contacts.size()); linear_velocity = Vector3(); angular_velocity = Vector3(); if (mode == PhysicsServer3D::BODY_MODE_KINEMATIC && prev != mode) { first_time_kinematic = true; } + _update_transform_dependant(); } break; case PhysicsServer3D::BODY_MODE_DYNAMIC: { _inv_mass = mass > 0 ? (1.0 / mass) : 0; + if (!calculate_inertia) { + principal_inertia_axes_local.set_diagonal(Vector3(1.0, 1.0, 1.0)); + _inv_inertia = inertia.inverse(); + _update_transform_dependant(); + } + _mass_properties_changed(); _set_static(false); set_active(true); } break; case PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED: { _inv_mass = mass > 0 ? (1.0 / mass) : 0; + _inv_inertia = Vector3(); + angular_velocity = Vector3(); + _update_transform_dependant(); _set_static(false); set_active(true); - angular_velocity = Vector3(); - } break; + } } - - _update_inertia(); - /* - if (get_space()) - _update_queries(); - */ } PhysicsServer3D::BodyMode Body3DSW::get_mode() const { @@ -265,7 +326,7 @@ PhysicsServer3D::BodyMode Body3DSW::get_mode() const { } void Body3DSW::_shapes_changed() { - _update_inertia(); + _mass_properties_changed(); } void Body3DSW::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_variant) { @@ -300,10 +361,12 @@ void Body3DSW::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_va } break; case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: { linear_velocity = p_variant; + constant_linear_velocity = linear_velocity; wakeup(); } break; case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: { angular_velocity = p_variant; + constant_angular_velocity = angular_velocity; wakeup(); } break; @@ -324,7 +387,7 @@ void Body3DSW::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_va } break; case PhysicsServer3D::BODY_STATE_CAN_SLEEP: { can_sleep = p_variant; - if (mode == PhysicsServer3D::BODY_MODE_DYNAMIC && !active && !can_sleep) { + if (mode >= PhysicsServer3D::BODY_MODE_DYNAMIC && !active && !can_sleep) { set_active(true); } @@ -356,8 +419,8 @@ Variant Body3DSW::get_state(PhysicsServer3D::BodyState p_state) const { void Body3DSW::set_space(Space3DSW *p_space) { if (get_space()) { - if (inertia_update_list.in_list()) { - get_space()->body_remove_from_inertia_update_list(&inertia_update_list); + if (mass_properties_update_list.in_list()) { + get_space()->body_remove_from_mass_properties_update_list(&mass_properties_update_list); } if (active_list.in_list()) { get_space()->body_remove_from_active_list(&active_list); @@ -370,26 +433,17 @@ void Body3DSW::set_space(Space3DSW *p_space) { _set_space(p_space); if (get_space()) { - _update_inertia(); + _mass_properties_changed(); if (active) { get_space()->body_add_to_active_list(&active_list); } } - - first_integration = true; } -void Body3DSW::_compute_area_gravity_and_dampenings(const Area3DSW *p_area) { - if (p_area->is_gravity_point()) { - if (p_area->get_gravity_distance_scale() > 0) { - Vector3 v = p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin(); - gravity += v.normalized() * (p_area->get_gravity() / Math::pow(v.length() * p_area->get_gravity_distance_scale() + 1, 2)); - } else { - gravity += (p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin()).normalized() * p_area->get_gravity(); - } - } else { - gravity += p_area->get_gravity_vector() * p_area->get_gravity(); - } +void Body3DSW::_compute_area_gravity_and_damping(const Area3DSW *p_area) { + Vector3 area_gravity; + p_area->compute_gravity(get_transform().get_origin(), area_gravity); + gravity += area_gravity; area_linear_damp += p_area->get_linear_damp(); area_angular_damp += p_area->get_angular_damp(); @@ -431,7 +485,7 @@ void Body3DSW::integrate_forces(real_t p_step) { switch (mode) { case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE: case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: { - _compute_area_gravity_and_dampenings(aa[i].area); + _compute_area_gravity_and_damping(aa[i].area); stopped = mode == PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE; } break; case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE: @@ -439,7 +493,7 @@ void Body3DSW::integrate_forces(real_t p_step) { gravity = Vector3(0, 0, 0); area_angular_damp = 0; area_linear_damp = 0; - _compute_area_gravity_and_dampenings(aa[i].area); + _compute_area_gravity_and_damping(aa[i].area); stopped = mode == PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE; } break; default: { @@ -449,7 +503,7 @@ void Body3DSW::integrate_forces(real_t p_step) { } if (!stopped) { - _compute_area_gravity_and_dampenings(def_area); + _compute_area_gravity_and_damping(def_area); } gravity *= gravity_scale; @@ -478,7 +532,7 @@ void Body3DSW::integrate_forces(real_t p_step) { //compute motion, angular and etc. velocities from prev transform motion = new_transform.origin - get_transform().origin; do_motion = true; - linear_velocity = motion / p_step; + linear_velocity = constant_linear_velocity + motion / p_step; //compute a FAKE angular velocity, not so easy Basis rot = new_transform.basis.orthonormalized() * get_transform().basis.orthonormalized().transposed(); @@ -487,9 +541,9 @@ void Body3DSW::integrate_forces(real_t p_step) { rot.get_axis_angle(axis, angle); axis.normalize(); - angular_velocity = axis * (angle / p_step); + angular_velocity = constant_angular_velocity + axis * (angle / p_step); } else { - if (!omit_force_integration && !first_integration) { + if (!omit_force_integration) { //overridden by direct state query Vector3 force = gravity * mass; @@ -523,7 +577,6 @@ void Body3DSW::integrate_forces(real_t p_step) { applied_force = Vector3(); applied_torque = Vector3(); - first_integration = false; //motion=linear_velocity*p_step; @@ -543,7 +596,7 @@ void Body3DSW::integrate_velocities(real_t p_step) { return; } - if (fi_callback) { + if (fi_callback_data || body_state_callback) { get_space()->body_add_to_state_query_list(&direct_state_query_list); } @@ -600,11 +653,6 @@ void Body3DSW::integrate_velocities(real_t p_step) { _set_inv_transform(get_transform().inverse()); _update_transform_dependant(); - - /* - if (fi_callback) { - get_space()->body_add_to_state_query_list(&direct_state_query_list); - */ } /* @@ -650,7 +698,7 @@ void Body3DSW::wakeup_neighbours() { continue; } Body3DSW *b = n[i]; - if (b->mode != PhysicsServer3D::BODY_MODE_DYNAMIC) { + if (b->mode < PhysicsServer3D::BODY_MODE_DYNAMIC) { continue; } @@ -662,24 +710,23 @@ void Body3DSW::wakeup_neighbours() { } void Body3DSW::call_queries() { - if (fi_callback) { - PhysicsDirectBodyState3DSW *dbs = PhysicsDirectBodyState3DSW::singleton; - dbs->body = this; - - Variant v = dbs; - - Object *obj = fi_callback->callable.get_object(); - if (!obj) { + if (fi_callback_data) { + if (!fi_callback_data->callable.get_object()) { set_force_integration_callback(Callable()); } else { - const Variant *vp[2] = { &v, &fi_callback->udata }; + Variant direct_state_variant = get_direct_state(); + const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata }; Callable::CallError ce; - int argc = (fi_callback->udata.get_type() == Variant::NIL) ? 1 : 2; + int argc = (fi_callback_data->udata.get_type() == Variant::NIL) ? 1 : 2; Variant rv; - fi_callback->callable.call(vp, argc, rv, ce); + fi_callback_data->callable.call(vp, argc, rv, ce); } } + + if (body_state_callback_instance) { + (body_state_callback)(body_state_callback_instance, get_direct_state()); + } } bool Body3DSW::sleep_test(real_t p_step) { @@ -699,60 +746,45 @@ bool Body3DSW::sleep_test(real_t p_step) { } } +void Body3DSW::set_state_sync_callback(void *p_instance, PhysicsServer3D::BodyStateCallback p_callback) { + body_state_callback_instance = p_instance; + body_state_callback = p_callback; +} + void Body3DSW::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) { - if (fi_callback) { - memdelete(fi_callback); - fi_callback = nullptr; + if (p_callable.get_object()) { + if (!fi_callback_data) { + fi_callback_data = memnew(ForceIntegrationCallbackData); + } + fi_callback_data->callable = p_callable; + fi_callback_data->udata = p_udata; + } else if (fi_callback_data) { + memdelete(fi_callback_data); + fi_callback_data = nullptr; } +} - if (p_callable.get_object()) { - fi_callback = memnew(ForceIntegrationCallback); - fi_callback->callable = p_callable; - fi_callback->udata = p_udata; +PhysicsDirectBodyState3DSW *Body3DSW::get_direct_state() { + if (!direct_state) { + direct_state = memnew(PhysicsDirectBodyState3DSW); + direct_state->body = this; } + return direct_state; } Body3DSW::Body3DSW() : CollisionObject3DSW(TYPE_BODY), - active_list(this), - inertia_update_list(this), + mass_properties_update_list(this), direct_state_query_list(this) { - mode = PhysicsServer3D::BODY_MODE_DYNAMIC; - active = true; - - mass = 1; - _inv_mass = 1; - bounce = 0; - friction = 1; - omit_force_integration = false; - //applied_torque=0; - island_step = 0; - first_time_kinematic = false; - first_integration = false; _set_static(false); - - contact_count = 0; - gravity_scale = 1.0; - linear_damp = -1; - angular_damp = -1; - area_angular_damp = 0; - area_linear_damp = 0; - - still_time = 0; - continuous_cd = false; - can_sleep = true; - fi_callback = nullptr; } Body3DSW::~Body3DSW() { - if (fi_callback) { - memdelete(fi_callback); + if (fi_callback_data) { + memdelete(fi_callback_data); + } + if (direct_state) { + memdelete(direct_state); } -} - -PhysicsDirectBodyState3DSW *PhysicsDirectBodyState3DSW::singleton = nullptr; - -PhysicsDirectSpaceState3D *PhysicsDirectBodyState3DSW::get_space_state() { - return body->get_space()->get_direct_state(); } diff --git a/servers/physics_3d/body_3d_sw.h b/servers/physics_3d/body_3d_sw.h index efb114a325..8b74c7e5b9 100644 --- a/servers/physics_3d/body_3d_sw.h +++ b/servers/physics_3d/body_3d_sw.h @@ -28,34 +28,39 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BODY_SW_H -#define BODY_SW_H +#ifndef BODY_3D_SW_H +#define BODY_3D_SW_H #include "area_3d_sw.h" #include "collision_object_3d_sw.h" #include "core/templates/vset.h" class Constraint3DSW; +class PhysicsDirectBodyState3DSW; class Body3DSW : public CollisionObject3DSW { - PhysicsServer3D::BodyMode mode; + PhysicsServer3D::BodyMode mode = PhysicsServer3D::BODY_MODE_DYNAMIC; Vector3 linear_velocity; Vector3 angular_velocity; + Vector3 constant_linear_velocity; + Vector3 constant_angular_velocity; + Vector3 biased_linear_velocity; Vector3 biased_angular_velocity; - real_t mass; - real_t bounce; - real_t friction; + real_t mass = 1.0; + real_t bounce = 0.0; + real_t friction = 1.0; + Vector3 inertia; - real_t linear_damp; - real_t angular_damp; - real_t gravity_scale; + real_t linear_damp = -1.0; + real_t angular_damp = -1.0; + real_t gravity_scale = 1.0; uint16_t locked_axis = 0; - real_t _inv_mass; + real_t _inv_mass = 1.0; Vector3 _inv_inertia; // Relative to the principal axes of inertia // Relative to the local frame of reference @@ -67,30 +72,32 @@ class Body3DSW : public CollisionObject3DSW { Basis principal_inertia_axes; Vector3 center_of_mass; + bool calculate_inertia = true; + bool calculate_center_of_mass = true; + Vector3 gravity; - real_t still_time; + real_t still_time = 0.0; Vector3 applied_force; Vector3 applied_torque; - real_t area_angular_damp; - real_t area_linear_damp; + real_t area_angular_damp = 0.0; + real_t area_linear_damp = 0.0; SelfList<Body3DSW> active_list; - SelfList<Body3DSW> inertia_update_list; + SelfList<Body3DSW> mass_properties_update_list; SelfList<Body3DSW> direct_state_query_list; VSet<RID> exceptions; - bool omit_force_integration; - bool active; + bool omit_force_integration = false; + bool active = true; - bool first_integration; + bool continuous_cd = false; + bool can_sleep = true; + bool first_time_kinematic = false; - bool continuous_cd; - bool can_sleep; - bool first_time_kinematic; - void _update_inertia(); + void _mass_properties_changed(); virtual void _shapes_changed(); Transform3D new_transform; @@ -111,26 +118,34 @@ class Body3DSW : public CollisionObject3DSW { }; Vector<Contact> contacts; //no contacts by default - int contact_count; + int contact_count = 0; + + void *body_state_callback_instance = nullptr; + PhysicsServer3D::BodyStateCallback body_state_callback = nullptr; - struct ForceIntegrationCallback { + struct ForceIntegrationCallbackData { Callable callable; Variant udata; }; - ForceIntegrationCallback *fi_callback; + ForceIntegrationCallbackData *fi_callback_data = nullptr; - uint64_t island_step; + PhysicsDirectBodyState3DSW *direct_state = nullptr; - _FORCE_INLINE_ void _compute_area_gravity_and_dampenings(const Area3DSW *p_area); + uint64_t island_step = 0; + + _FORCE_INLINE_ void _compute_area_gravity_and_damping(const Area3DSW *p_area); _FORCE_INLINE_ void _update_transform_dependant(); friend class PhysicsDirectBodyState3DSW; // i give up, too many functions to expose public: + void set_state_sync_callback(void *p_instance, PhysicsServer3D::BodyStateCallback p_callback); void set_force_integration_callback(const Callable &p_callable, const Variant &p_udata = Variant()); + PhysicsDirectBodyState3DSW *get_direct_state(); + _FORCE_INLINE_ void add_area(Area3DSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { @@ -242,8 +257,8 @@ public: set_active(true); } - void set_param(PhysicsServer3D::BodyParameter p_param, real_t); - real_t get_param(PhysicsServer3D::BodyParameter p_param) const; + void set_param(PhysicsServer3D::BodyParameter p_param, const Variant &p_value); + Variant get_param(PhysicsServer3D::BodyParameter p_param) const; void set_mode(PhysicsServer3D::BodyMode p_mode); PhysicsServer3D::BodyMode get_mode() const; @@ -262,7 +277,8 @@ public: void set_space(Space3DSW *p_space); - void update_inertias(); + void update_mass_properties(); + void reset_mass_properties(); _FORCE_INLINE_ real_t get_inv_mass() const { return _inv_mass; } _FORCE_INLINE_ const Vector3 &get_inv_inertia() const { return _inv_inertia; } @@ -349,96 +365,4 @@ void Body3DSW::add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_no c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos; } -class PhysicsDirectBodyState3DSW : public PhysicsDirectBodyState3D { - GDCLASS(PhysicsDirectBodyState3DSW, PhysicsDirectBodyState3D); - -public: - static PhysicsDirectBodyState3DSW *singleton; - Body3DSW *body; - real_t step; - - virtual Vector3 get_total_gravity() const override { return body->gravity; } // get gravity vector working on this body space/area - virtual real_t get_total_angular_damp() const override { return body->area_angular_damp; } // get density of this body space/area - virtual real_t get_total_linear_damp() const override { return body->area_linear_damp; } // get density of this body space/area - - virtual Vector3 get_center_of_mass() const override { return body->get_center_of_mass(); } - virtual Basis get_principal_inertia_axes() const override { return body->get_principal_inertia_axes(); } - - virtual real_t get_inverse_mass() const override { return body->get_inv_mass(); } // get the mass - virtual Vector3 get_inverse_inertia() const override { return body->get_inv_inertia(); } // get density of this body space - virtual Basis get_inverse_inertia_tensor() const override { return body->get_inv_inertia_tensor(); } // get density of this body space - - virtual void set_linear_velocity(const Vector3 &p_velocity) override { body->set_linear_velocity(p_velocity); } - virtual Vector3 get_linear_velocity() const override { return body->get_linear_velocity(); } - - virtual void set_angular_velocity(const Vector3 &p_velocity) override { body->set_angular_velocity(p_velocity); } - virtual Vector3 get_angular_velocity() const override { return body->get_angular_velocity(); } - - virtual void set_transform(const Transform3D &p_transform) override { body->set_state(PhysicsServer3D::BODY_STATE_TRANSFORM, p_transform); } - virtual Transform3D get_transform() const override { return body->get_transform(); } - - virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override { return body->get_velocity_in_local_point(p_position); } - - virtual void add_central_force(const Vector3 &p_force) override { body->add_central_force(p_force); } - virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override { - body->add_force(p_force, p_position); - } - virtual void add_torque(const Vector3 &p_torque) override { body->add_torque(p_torque); } - virtual void apply_central_impulse(const Vector3 &p_impulse) override { body->apply_central_impulse(p_impulse); } - virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override { - body->apply_impulse(p_impulse, p_position); - } - virtual void apply_torque_impulse(const Vector3 &p_impulse) override { body->apply_torque_impulse(p_impulse); } - - virtual void set_sleep_state(bool p_sleep) override { body->set_active(!p_sleep); } - virtual bool is_sleeping() const override { return !body->is_active(); } - - virtual int get_contact_count() const override { return body->contact_count; } - - virtual Vector3 get_contact_local_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].local_pos; - } - virtual Vector3 get_contact_local_normal(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].local_normal; - } - virtual real_t get_contact_impulse(int p_contact_idx) const override { - return 0.0f; // Only implemented for bullet - } - virtual int get_contact_local_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); - return body->contacts[p_contact_idx].local_shape; - } - - virtual RID get_contact_collider(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); - return body->contacts[p_contact_idx].collider; - } - virtual Vector3 get_contact_collider_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].collider_pos; - } - virtual ObjectID get_contact_collider_id(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); - return body->contacts[p_contact_idx].collider_instance_id; - } - virtual int get_contact_collider_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); - return body->contacts[p_contact_idx].collider_shape; - } - virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].collider_velocity_at_pos; - } - - virtual PhysicsDirectSpaceState3D *get_space_state() override; - - virtual real_t get_step() const override { return step; } - PhysicsDirectBodyState3DSW() { - singleton = this; - body = nullptr; - } -}; - -#endif // BODY__SW_H +#endif // BODY_3D_SW_H diff --git a/servers/physics_3d/body_direct_state_3d_sw.cpp b/servers/physics_3d/body_direct_state_3d_sw.cpp new file mode 100644 index 0000000000..d197dd288d --- /dev/null +++ b/servers/physics_3d/body_direct_state_3d_sw.cpp @@ -0,0 +1,182 @@ +/*************************************************************************/ +/* body_direct_state_3d_sw.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "body_direct_state_3d_sw.h" + +#include "body_3d_sw.h" +#include "space_3d_sw.h" + +Vector3 PhysicsDirectBodyState3DSW::get_total_gravity() const { + return body->gravity; +} + +real_t PhysicsDirectBodyState3DSW::get_total_angular_damp() const { + return body->area_angular_damp; +} + +real_t PhysicsDirectBodyState3DSW::get_total_linear_damp() const { + return body->area_linear_damp; +} + +Vector3 PhysicsDirectBodyState3DSW::get_center_of_mass() const { + return body->get_center_of_mass(); +} + +Basis PhysicsDirectBodyState3DSW::get_principal_inertia_axes() const { + return body->get_principal_inertia_axes(); +} + +real_t PhysicsDirectBodyState3DSW::get_inverse_mass() const { + return body->get_inv_mass(); +} + +Vector3 PhysicsDirectBodyState3DSW::get_inverse_inertia() const { + return body->get_inv_inertia(); +} + +Basis PhysicsDirectBodyState3DSW::get_inverse_inertia_tensor() const { + return body->get_inv_inertia_tensor(); +} + +void PhysicsDirectBodyState3DSW::set_linear_velocity(const Vector3 &p_velocity) { + body->set_linear_velocity(p_velocity); +} + +Vector3 PhysicsDirectBodyState3DSW::get_linear_velocity() const { + return body->get_linear_velocity(); +} + +void PhysicsDirectBodyState3DSW::set_angular_velocity(const Vector3 &p_velocity) { + body->set_angular_velocity(p_velocity); +} + +Vector3 PhysicsDirectBodyState3DSW::get_angular_velocity() const { + return body->get_angular_velocity(); +} + +void PhysicsDirectBodyState3DSW::set_transform(const Transform3D &p_transform) { + body->set_state(PhysicsServer3D::BODY_STATE_TRANSFORM, p_transform); +} + +Transform3D PhysicsDirectBodyState3DSW::get_transform() const { + return body->get_transform(); +} + +Vector3 PhysicsDirectBodyState3DSW::get_velocity_at_local_position(const Vector3 &p_position) const { + return body->get_velocity_in_local_point(p_position); +} + +void PhysicsDirectBodyState3DSW::add_central_force(const Vector3 &p_force) { + body->add_central_force(p_force); +} + +void PhysicsDirectBodyState3DSW::add_force(const Vector3 &p_force, const Vector3 &p_position) { + body->add_force(p_force, p_position); +} + +void PhysicsDirectBodyState3DSW::add_torque(const Vector3 &p_torque) { + body->add_torque(p_torque); +} + +void PhysicsDirectBodyState3DSW::apply_central_impulse(const Vector3 &p_impulse) { + body->apply_central_impulse(p_impulse); +} + +void PhysicsDirectBodyState3DSW::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { + body->apply_impulse(p_impulse, p_position); +} + +void PhysicsDirectBodyState3DSW::apply_torque_impulse(const Vector3 &p_impulse) { + body->apply_torque_impulse(p_impulse); +} + +void PhysicsDirectBodyState3DSW::set_sleep_state(bool p_sleep) { + body->set_active(!p_sleep); +} + +bool PhysicsDirectBodyState3DSW::is_sleeping() const { + return !body->is_active(); +} + +int PhysicsDirectBodyState3DSW::get_contact_count() const { + return body->contact_count; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_local_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].local_pos; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_local_normal(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].local_normal; +} + +real_t PhysicsDirectBodyState3DSW::get_contact_impulse(int p_contact_idx) const { + return 0.0f; // Only implemented for bullet +} + +int PhysicsDirectBodyState3DSW::get_contact_local_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); + return body->contacts[p_contact_idx].local_shape; +} + +RID PhysicsDirectBodyState3DSW::get_contact_collider(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); + return body->contacts[p_contact_idx].collider; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_collider_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].collider_pos; +} + +ObjectID PhysicsDirectBodyState3DSW::get_contact_collider_id(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); + return body->contacts[p_contact_idx].collider_instance_id; +} + +int PhysicsDirectBodyState3DSW::get_contact_collider_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); + return body->contacts[p_contact_idx].collider_shape; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_collider_velocity_at_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].collider_velocity_at_pos; +} + +PhysicsDirectSpaceState3D *PhysicsDirectBodyState3DSW::get_space_state() { + return body->get_space()->get_direct_state(); +} + +real_t PhysicsDirectBodyState3DSW::get_step() const { + return body->get_space()->get_last_step(); +} diff --git a/servers/physics_3d/body_direct_state_3d_sw.h b/servers/physics_3d/body_direct_state_3d_sw.h new file mode 100644 index 0000000000..5132376715 --- /dev/null +++ b/servers/physics_3d/body_direct_state_3d_sw.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* body_direct_state_3d_sw.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 BODY_DIRECT_STATE_3D_SW_H +#define BODY_DIRECT_STATE_3D_SW_H + +#include "servers/physics_server_3d.h" + +class Body3DSW; + +class PhysicsDirectBodyState3DSW : public PhysicsDirectBodyState3D { + GDCLASS(PhysicsDirectBodyState3DSW, PhysicsDirectBodyState3D); + +public: + Body3DSW *body = nullptr; + + virtual Vector3 get_total_gravity() const override; + virtual real_t get_total_angular_damp() const override; + virtual real_t get_total_linear_damp() const override; + + virtual Vector3 get_center_of_mass() const override; + virtual Basis get_principal_inertia_axes() const override; + + virtual real_t get_inverse_mass() const override; + virtual Vector3 get_inverse_inertia() const override; + virtual Basis get_inverse_inertia_tensor() const override; + + virtual void set_linear_velocity(const Vector3 &p_velocity) override; + virtual Vector3 get_linear_velocity() const override; + + virtual void set_angular_velocity(const Vector3 &p_velocity) override; + virtual Vector3 get_angular_velocity() const override; + + virtual void set_transform(const Transform3D &p_transform) override; + virtual Transform3D get_transform() const override; + + virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override; + + virtual void add_central_force(const Vector3 &p_force) override; + virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override; + virtual void add_torque(const Vector3 &p_torque) override; + virtual void apply_central_impulse(const Vector3 &p_impulse) override; + virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override; + virtual void apply_torque_impulse(const Vector3 &p_impulse) override; + + virtual void set_sleep_state(bool p_sleep) override; + virtual bool is_sleeping() const override; + + virtual int get_contact_count() const override; + + virtual Vector3 get_contact_local_position(int p_contact_idx) const override; + virtual Vector3 get_contact_local_normal(int p_contact_idx) const override; + virtual real_t get_contact_impulse(int p_contact_idx) const override; + virtual int get_contact_local_shape(int p_contact_idx) const override; + + virtual RID get_contact_collider(int p_contact_idx) const override; + virtual Vector3 get_contact_collider_position(int p_contact_idx) const override; + virtual ObjectID get_contact_collider_id(int p_contact_idx) const override; + virtual int get_contact_collider_shape(int p_contact_idx) const override; + virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override; + + virtual PhysicsDirectSpaceState3D *get_space_state() override; + + virtual real_t get_step() const override; +}; + +#endif // BODY_DIRECT_STATE_3D_SW_H diff --git a/servers/physics_3d/collision_solver_3d_sat.cpp b/servers/physics_3d/collision_solver_3d_sat.cpp index 6e6a2cb9e7..de81348b4e 100644 --- a/servers/physics_3d/collision_solver_3d_sat.cpp +++ b/servers/physics_3d/collision_solver_3d_sat.cpp @@ -2273,11 +2273,13 @@ bool sat_calculate_penetration(const Shape3DSW *p_shape_A, const Transform3D &p_ PhysicsServer3D::ShapeType type_A = p_shape_A->get_type(); ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_PLANE, false); + ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_SEPARATION_RAY, false); ERR_FAIL_COND_V(p_shape_A->is_concave(), false); PhysicsServer3D::ShapeType type_B = p_shape_B->get_type(); ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_PLANE, false); + ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY, false); ERR_FAIL_COND_V(p_shape_B->is_concave(), false); static const CollisionFunc collision_table[6][6] = { @@ -2382,10 +2384,10 @@ bool sat_calculate_penetration(const Shape3DSW *p_shape_A, const Transform3D &p_ CollisionFunc collision_func; if (margin_A != 0.0 || margin_B != 0.0) { - collision_func = collision_table_margin[type_A - 1][type_B - 1]; + collision_func = collision_table_margin[type_A - 2][type_B - 2]; } else { - collision_func = collision_table[type_A - 1][type_B - 1]; + collision_func = collision_table[type_A - 2][type_B - 2]; } ERR_FAIL_COND_V(!collision_func, false); diff --git a/servers/physics_3d/collision_solver_3d_sw.cpp b/servers/physics_3d/collision_solver_3d_sw.cpp index dcecac1c73..4a4a8164d3 100644 --- a/servers/physics_3d/collision_solver_3d_sw.cpp +++ b/servers/physics_3d/collision_solver_3d_sw.cpp @@ -89,6 +89,49 @@ bool CollisionSolver3DSW::solve_static_plane(const Shape3DSW *p_shape_A, const T return found; } +bool CollisionSolver3DSW::solve_separation_ray(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin) { + const SeparationRayShape3DSW *ray = static_cast<const SeparationRayShape3DSW *>(p_shape_A); + + Vector3 from = p_transform_A.origin; + Vector3 to = from + p_transform_A.basis.get_axis(2) * (ray->get_length() + p_margin); + Vector3 support_A = to; + + Transform3D ai = p_transform_B.affine_inverse(); + + from = ai.xform(from); + to = ai.xform(to); + + Vector3 p, n; + if (!p_shape_B->intersect_segment(from, to, p, n)) { + return false; + } + + // Discard contacts when the ray is fully contained inside the shape. + if (n == Vector3()) { + return false; + } + + // Discard contacts in the wrong direction. + if (n.dot(from - to) < CMP_EPSILON) { + return false; + } + + Vector3 support_B = p_transform_B.xform(p); + if (ray->get_slide_on_slope()) { + Vector3 global_n = ai.basis.xform_inv(n).normalized(); + support_B = support_A + (support_B - support_A).length() * global_n; + } + + if (p_result_callback) { + if (p_swap_result) { + p_result_callback(support_B, 0, support_A, 0, p_userdata); + } else { + p_result_callback(support_A, 0, support_B, 0, p_userdata); + } + } + return true; +} + struct _SoftBodyContactCollisionInfo { int node_index = 0; CollisionSolver3DSW::CallbackResult result_callback = nullptr; @@ -135,17 +178,17 @@ bool CollisionSolver3DSW::soft_body_query_callback(uint32_t p_node_index, void * transform_B.origin = query_cinfo.node_transform.xform(node_position); query_cinfo.contact_info.node_index = p_node_index; - solve_static(query_cinfo.shape_A, query_cinfo.transform_A, query_cinfo.shape_B, transform_B, soft_body_contact_callback, &query_cinfo.contact_info); + bool collided = solve_static(query_cinfo.shape_A, query_cinfo.transform_A, query_cinfo.shape_B, transform_B, soft_body_contact_callback, &query_cinfo.contact_info); #ifdef DEBUG_ENABLED ++query_cinfo.node_query_count; #endif - // Continue with the query. - return false; + // Stop at first collision if contacts are not needed. + return (collided && !query_cinfo.contact_info.result_callback); } -void CollisionSolver3DSW::soft_body_concave_callback(void *p_userdata, Shape3DSW *p_convex) { +bool CollisionSolver3DSW::soft_body_concave_callback(void *p_userdata, Shape3DSW *p_convex) { _SoftBodyQueryInfo &query_cinfo = *(_SoftBodyQueryInfo *)(p_userdata); query_cinfo.shape_A = p_convex; @@ -167,9 +210,14 @@ void CollisionSolver3DSW::soft_body_concave_callback(void *p_userdata, Shape3DSW query_cinfo.soft_body->query_aabb(shape_aabb, soft_body_query_callback, &query_cinfo); + bool collided = (query_cinfo.contact_info.contact_count > 0); + #ifdef DEBUG_ENABLED ++query_cinfo.convex_query_count; #endif + + // Stop at first collision if contacts are not needed. + return (collided && !query_cinfo.contact_info.result_callback); } bool CollisionSolver3DSW::solve_soft_body(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result) { @@ -243,17 +291,20 @@ struct _ConcaveCollisionInfo { Vector3 close_A, close_B; }; -void CollisionSolver3DSW::concave_callback(void *p_userdata, Shape3DSW *p_convex) { +bool CollisionSolver3DSW::concave_callback(void *p_userdata, Shape3DSW *p_convex) { _ConcaveCollisionInfo &cinfo = *(_ConcaveCollisionInfo *)(p_userdata); cinfo.aabb_tests++; bool collided = collision_solver(cinfo.shape_A, *cinfo.transform_A, p_convex, *cinfo.transform_B, cinfo.result_callback, cinfo.userdata, cinfo.swap_result, nullptr, cinfo.margin_A, cinfo.margin_B); if (!collided) { - return; + return false; } cinfo.collided = true; cinfo.collisions++; + + // Stop at first collision if contacts are not needed. + return !cinfo.result_callback; } bool CollisionSolver3DSW::solve_concave(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin_A, real_t p_margin_B) { @@ -318,6 +369,9 @@ bool CollisionSolver3DSW::solve_static(const Shape3DSW *p_shape_A, const Transfo if (type_B == PhysicsServer3D::SHAPE_PLANE) { return false; } + if (type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY) { + return false; + } if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) { return false; } @@ -328,6 +382,17 @@ bool CollisionSolver3DSW::solve_static(const Shape3DSW *p_shape_A, const Transfo return solve_static_plane(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false); } + } else if (type_A == PhysicsServer3D::SHAPE_SEPARATION_RAY) { + if (type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY) { + return false; + } + + if (swap) { + return solve_separation_ray(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, p_margin_B); + } else { + return solve_separation_ray(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, p_margin_A); + } + } else if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) { if (type_A == PhysicsServer3D::SHAPE_SOFT_BODY) { // Soft Body / Soft Body not supported. @@ -356,19 +421,18 @@ bool CollisionSolver3DSW::solve_static(const Shape3DSW *p_shape_A, const Transfo } } -void CollisionSolver3DSW::concave_distance_callback(void *p_userdata, Shape3DSW *p_convex) { +bool CollisionSolver3DSW::concave_distance_callback(void *p_userdata, Shape3DSW *p_convex) { _ConcaveCollisionInfo &cinfo = *(_ConcaveCollisionInfo *)(p_userdata); cinfo.aabb_tests++; - if (cinfo.collided) { - return; - } Vector3 close_A, close_B; cinfo.collided = !gjk_epa_calculate_distance(cinfo.shape_A, *cinfo.transform_A, p_convex, *cinfo.transform_B, close_A, close_B); if (cinfo.collided) { - return; + // No need to process any more result. + return true; } + if (!cinfo.tested || close_A.distance_squared_to(close_B) < cinfo.close_A.distance_squared_to(cinfo.close_B)) { cinfo.close_A = close_A; cinfo.close_B = close_B; @@ -376,6 +440,7 @@ void CollisionSolver3DSW::concave_distance_callback(void *p_userdata, Shape3DSW } cinfo.collisions++; + return false; } bool CollisionSolver3DSW::solve_distance_plane(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, Vector3 &r_point_A, Vector3 &r_point_B) { diff --git a/servers/physics_3d/collision_solver_3d_sw.h b/servers/physics_3d/collision_solver_3d_sw.h index a5dd7d48eb..c13614ab3e 100644 --- a/servers/physics_3d/collision_solver_3d_sw.h +++ b/servers/physics_3d/collision_solver_3d_sw.h @@ -40,13 +40,13 @@ public: private: static bool soft_body_query_callback(uint32_t p_node_index, void *p_userdata); static void soft_body_contact_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata); - static void soft_body_concave_callback(void *p_userdata, Shape3DSW *p_convex); - static void concave_callback(void *p_userdata, Shape3DSW *p_convex); + static bool soft_body_concave_callback(void *p_userdata, Shape3DSW *p_convex); + static bool concave_callback(void *p_userdata, Shape3DSW *p_convex); static bool solve_static_plane(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result); - static bool solve_ray(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result); + static bool solve_separation_ray(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin = 0); static bool solve_soft_body(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result); static bool solve_concave(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin_A = 0, real_t p_margin_B = 0); - static void concave_distance_callback(void *p_userdata, Shape3DSW *p_convex); + static bool concave_distance_callback(void *p_userdata, Shape3DSW *p_convex); static bool solve_distance_plane(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, Vector3 &r_point_A, Vector3 &r_point_B); public: diff --git a/servers/physics_3d/physics_server_3d_sw.cpp b/servers/physics_3d/physics_server_3d_sw.cpp index fbb374bd74..8de54c5c48 100644 --- a/servers/physics_3d/physics_server_3d_sw.cpp +++ b/servers/physics_3d/physics_server_3d_sw.cpp @@ -30,6 +30,7 @@ #include "physics_server_3d_sw.h" +#include "body_direct_state_3d_sw.h" #include "broad_phase_3d_bvh.h" #include "core/debugger/engine_debugger.h" #include "core/os/os.h" @@ -48,6 +49,12 @@ RID PhysicsServer3DSW::plane_shape_create() { shape->set_self(rid); return rid; } +RID PhysicsServer3DSW::separation_ray_shape_create() { + Shape3DSW *shape = memnew(SeparationRayShape3DSW); + RID rid = shape_owner.make_rid(shape); + shape->set_self(rid); + return rid; +} RID PhysicsServer3DSW::sphere_shape_create() { Shape3DSW *shape = memnew(SphereShape3DSW); RID rid = shape_owner.make_rid(shape); @@ -636,20 +643,27 @@ uint32_t PhysicsServer3DSW::body_get_user_flags(RID p_body) const { return 0; }; -void PhysicsServer3DSW::body_set_param(RID p_body, BodyParameter p_param, real_t p_value) { +void PhysicsServer3DSW::body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); body->set_param(p_param, p_value); }; -real_t PhysicsServer3DSW::body_get_param(RID p_body, BodyParameter p_param) const { +Variant PhysicsServer3DSW::body_get_param(RID p_body, BodyParameter p_param) const { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, 0); return body->get_param(p_param); }; +void PhysicsServer3DSW::body_reset_mass_properties(RID p_body) { + Body3DSW *body = body_owner.getornull(p_body); + ERR_FAIL_COND(!body); + + return body->reset_mass_properties(); +} + void PhysicsServer3DSW::body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); @@ -836,6 +850,12 @@ int PhysicsServer3DSW::body_get_max_contacts_reported(RID p_body) const { return body->get_max_contacts_reported(); } +void PhysicsServer3DSW::body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) { + Body3DSW *body = body_owner.getornull(p_body); + ERR_FAIL_COND(!body); + body->set_state_sync_callback(p_instance, p_callback); +} + void PhysicsServer3DSW::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); @@ -848,7 +868,7 @@ void PhysicsServer3DSW::body_set_ray_pickable(RID p_body, bool p_enable) { body->set_ray_pickable(p_enable); } -bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, MotionResult *r_result, const Set<RID> &p_exclude) { +bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); @@ -856,18 +876,19 @@ bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, _update_shapes(); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_exclude); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude); } PhysicsDirectBodyState3D *PhysicsServer3DSW::body_get_direct_state(RID p_body) { ERR_FAIL_COND_V_MSG((using_threads && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); Body3DSW *body = body_owner.getornull(p_body); - ERR_FAIL_COND_V(!body, nullptr); + ERR_FAIL_NULL_V(body, nullptr); + + ERR_FAIL_NULL_V(body->get_space(), nullptr); ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); - direct_state->body = body; - return direct_state; + return body->get_direct_state(); } /* SOFT BODY */ @@ -1572,10 +1593,8 @@ void PhysicsServer3DSW::set_collision_iterations(int p_iterations) { }; void PhysicsServer3DSW::init() { - last_step = 0.001; iterations = 8; // 8? stepper = memnew(Step3DSW); - direct_state = memnew(PhysicsDirectBodyState3DSW); }; void PhysicsServer3DSW::step(real_t p_step) { @@ -1587,9 +1606,6 @@ void PhysicsServer3DSW::step(real_t p_step) { _update_shapes(); - last_step = p_step; - PhysicsDirectBodyState3DSW::singleton->step = p_step; - island_count = 0; active_objects = 0; collision_pairs = 0; @@ -1665,7 +1681,6 @@ void PhysicsServer3DSW::end_sync() { void PhysicsServer3DSW::finish() { memdelete(stepper); - memdelete(direct_state); }; int PhysicsServer3DSW::get_process_info(ProcessInfo p_info) { diff --git a/servers/physics_3d/physics_server_3d_sw.h b/servers/physics_3d/physics_server_3d_sw.h index 6e59a77e89..071ad0a694 100644 --- a/servers/physics_3d/physics_server_3d_sw.h +++ b/servers/physics_3d/physics_server_3d_sw.h @@ -44,7 +44,6 @@ class PhysicsServer3DSW : public PhysicsServer3D { friend class PhysicsDirectSpaceState3DSW; bool active; int iterations; - real_t last_step; int island_count; int active_objects; @@ -57,8 +56,6 @@ class PhysicsServer3DSW : public PhysicsServer3D { Step3DSW *stepper; Set<const Space3DSW *> active_spaces; - PhysicsDirectBodyState3DSW *direct_state; - mutable RID_PtrOwner<Shape3DSW, true> shape_owner; mutable RID_PtrOwner<Space3DSW, true> space_owner; mutable RID_PtrOwner<Area3DSW, true> area_owner; @@ -83,6 +80,7 @@ public: static void _shape_col_cbk(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata); virtual RID plane_shape_create() override; + virtual RID separation_ray_shape_create() override; virtual RID sphere_shape_create() override; virtual RID box_shape_create() override; virtual RID capsule_shape_create() override; @@ -200,8 +198,10 @@ public: virtual void body_set_user_flags(RID p_body, uint32_t p_flags) override; virtual uint32_t body_get_user_flags(RID p_body) const override; - virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value) override; - virtual real_t body_get_param(RID p_body, BodyParameter p_param) const override; + virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) override; + virtual Variant body_get_param(RID p_body, BodyParameter p_param) const override; + + virtual void body_reset_mass_properties(RID p_body) override; virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override; virtual Variant body_get_state(RID p_body, BodyState p_state) const override; @@ -237,11 +237,12 @@ public: virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override; virtual int body_get_max_contacts_reported(RID p_body) const override; + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) override; virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override; virtual void body_set_ray_pickable(RID p_body, bool p_enable) override; - virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override; + virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override; // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override; diff --git a/servers/physics_3d/physics_server_3d_wrap_mt.h b/servers/physics_3d/physics_server_3d_wrap_mt.h index 75174628bf..58986969d4 100644 --- a/servers/physics_3d/physics_server_3d_wrap_mt.h +++ b/servers/physics_3d/physics_server_3d_wrap_mt.h @@ -79,6 +79,7 @@ public: //FUNC1RID(shape,ShapeType); todo fix FUNCRID(plane_shape) + FUNCRID(separation_ray_shape) FUNCRID(sphere_shape) FUNCRID(box_shape) FUNCRID(capsule_shape) @@ -209,8 +210,10 @@ public: FUNC2(body_set_user_flags, RID, uint32_t); FUNC1RC(uint32_t, body_get_user_flags, RID); - FUNC3(body_set_param, RID, BodyParameter, real_t); - FUNC2RC(real_t, body_get_param, RID, BodyParameter); + FUNC3(body_set_param, RID, BodyParameter, const Variant &); + FUNC2RC(Variant, body_get_param, RID, BodyParameter); + + FUNC1(body_reset_mass_properties, RID); FUNC3(body_set_state, RID, BodyState, const Variant &); FUNC2RC(Variant, body_get_state, RID, BodyState); @@ -245,13 +248,14 @@ public: FUNC2(body_set_omit_force_integration, RID, bool); FUNC1RC(bool, body_is_omitting_force_integration, RID); + FUNC3(body_set_state_sync_callback, RID, void *, BodyStateCallback); FUNC3(body_set_force_integration_callback, RID, const Callable &, const Variant &); FUNC2(body_set_ray_pickable, RID, bool); - bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override { + bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override { ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); - return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_exclude); + return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude); } // this function only works on physics process, errors and returns null otherwise diff --git a/servers/physics_3d/shape_3d_sw.cpp b/servers/physics_3d/shape_3d_sw.cpp index 664308ed7b..945d0120be 100644 --- a/servers/physics_3d/shape_3d_sw.cpp +++ b/servers/physics_3d/shape_3d_sw.cpp @@ -164,6 +164,91 @@ Variant PlaneShape3DSW::get_data() const { PlaneShape3DSW::PlaneShape3DSW() { } +// + +real_t SeparationRayShape3DSW::get_length() const { + return length; +} + +bool SeparationRayShape3DSW::get_slide_on_slope() const { + return slide_on_slope; +} + +void SeparationRayShape3DSW::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const { + // don't think this will be even used + r_min = 0; + r_max = 1; +} + +Vector3 SeparationRayShape3DSW::get_support(const Vector3 &p_normal) const { + if (p_normal.z > 0) { + return Vector3(0, 0, length); + } else { + return Vector3(0, 0, 0); + } +} + +void SeparationRayShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const { + if (Math::abs(p_normal.z) < _EDGE_IS_VALID_SUPPORT_THRESHOLD) { + r_amount = 2; + r_type = FEATURE_EDGE; + r_supports[0] = Vector3(0, 0, 0); + r_supports[1] = Vector3(0, 0, length); + } else if (p_normal.z > 0) { + r_amount = 1; + r_type = FEATURE_POINT; + *r_supports = Vector3(0, 0, length); + } else { + r_amount = 1; + r_type = FEATURE_POINT; + *r_supports = Vector3(0, 0, 0); + } +} + +bool SeparationRayShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const { + return false; //simply not possible +} + +bool SeparationRayShape3DSW::intersect_point(const Vector3 &p_point) const { + return false; //simply not possible +} + +Vector3 SeparationRayShape3DSW::get_closest_point_to(const Vector3 &p_point) const { + Vector3 s[2] = { + Vector3(0, 0, 0), + Vector3(0, 0, length) + }; + + return Geometry3D::get_closest_point_to_segment(p_point, s); +} + +Vector3 SeparationRayShape3DSW::get_moment_of_inertia(real_t p_mass) const { + return Vector3(); +} + +void SeparationRayShape3DSW::_setup(real_t p_length, bool p_slide_on_slope) { + length = p_length; + slide_on_slope = p_slide_on_slope; + configure(AABB(Vector3(0, 0, 0), Vector3(0.1, 0.1, length))); +} + +void SeparationRayShape3DSW::set_data(const Variant &p_data) { + Dictionary d = p_data; + _setup(d["length"], d["slide_on_slope"]); +} + +Variant SeparationRayShape3DSW::get_data() const { + Dictionary d; + d["length"] = length; + d["slide_on_slope"] = slide_on_slope; + return d; +} + +SeparationRayShape3DSW::SeparationRayShape3DSW() { + length = 1; + slide_on_slope = false; +} + /********** SPHERE *************/ real_t SphereShape3DSW::get_radius() const { @@ -1297,11 +1382,11 @@ Vector3 ConcavePolygonShape3DSW::get_closest_point_to(const Vector3 &p_point) co return Vector3(); } -void ConcavePolygonShape3DSW::_cull(int p_idx, _CullParams *p_params) const { +bool ConcavePolygonShape3DSW::_cull(int p_idx, _CullParams *p_params) const { const BVH *bvh = &p_params->bvh[p_idx]; if (!p_params->aabb.intersects(bvh->aabb)) { - return; + return false; } if (bvh->face_index >= 0) { @@ -1311,20 +1396,27 @@ void ConcavePolygonShape3DSW::_cull(int p_idx, _CullParams *p_params) const { face->vertex[0] = p_params->vertices[f->indices[0]]; face->vertex[1] = p_params->vertices[f->indices[1]]; face->vertex[2] = p_params->vertices[f->indices[2]]; - p_params->callback(p_params->userdata, face); - + if (p_params->callback(p_params->userdata, face)) { + return true; + } } else { if (bvh->left >= 0) { - _cull(bvh->left, p_params); + if (_cull(bvh->left, p_params)) { + return true; + } } if (bvh->right >= 0) { - _cull(bvh->right, p_params); + if (_cull(bvh->right, p_params)) { + return true; + } } } + + return false; } -void ConcavePolygonShape3DSW::cull(const AABB &p_local_aabb, Callback p_callback, void *p_userdata) const { +void ConcavePolygonShape3DSW::cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata) const { // make matrix local to concave if (faces.size() == 0) { return; @@ -1584,6 +1676,17 @@ struct _HeightmapSegmentCullParams { FaceShape3DSW *face = nullptr; }; +struct _HeightmapGridCullState { + real_t length = 0.0; + real_t length_flat = 0.0; + + real_t dist = 0.0; + real_t prev_dist = 0.0; + + int x = 0; + int z = 0; +}; + _FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) { Vector3 res; Vector3 normal; @@ -1596,11 +1699,11 @@ _FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_ return false; } -_FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_params, int p_x, int p_z) { +_FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_params, const _HeightmapGridCullState &p_state) { // First triangle. - p_params.heightmap->_get_point(p_x, p_z, p_params.face->vertex[0]); - p_params.heightmap->_get_point(p_x + 1, p_z, p_params.face->vertex[1]); - p_params.heightmap->_get_point(p_x, p_z + 1, p_params.face->vertex[2]); + p_params.heightmap->_get_point(p_state.x, p_state.z, p_params.face->vertex[0]); + p_params.heightmap->_get_point(p_state.x + 1, p_state.z, p_params.face->vertex[1]); + p_params.heightmap->_get_point(p_state.x, p_state.z + 1, p_params.face->vertex[2]); p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal; if (_heightmap_face_cull_segment(p_params)) { return true; @@ -1608,7 +1711,7 @@ _FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_ // Second triangle. p_params.face->vertex[0] = p_params.face->vertex[1]; - p_params.heightmap->_get_point(p_x + 1, p_z + 1, p_params.face->vertex[1]); + p_params.heightmap->_get_point(p_state.x + 1, p_state.z + 1, p_params.face->vertex[1]); p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal; if (_heightmap_face_cull_segment(p_params)) { return true; @@ -1617,13 +1720,51 @@ _FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_ return false; } -bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const { - if (heights.is_empty()) { +_FORCE_INLINE_ bool _heightmap_chunk_cull_segment(_HeightmapSegmentCullParams &p_params, const _HeightmapGridCullState &p_state) { + const HeightMapShape3DSW::Range &chunk = p_params.heightmap->_get_bounds_chunk(p_state.x, p_state.z); + + Vector3 enter_pos; + Vector3 exit_pos; + + if (p_state.length_flat > CMP_EPSILON) { + real_t flat_to_3d = p_state.length / p_state.length_flat; + real_t enter_param = p_state.prev_dist * flat_to_3d; + real_t exit_param = p_state.dist * flat_to_3d; + enter_pos = p_params.from + p_params.dir * enter_param; + exit_pos = p_params.from + p_params.dir * exit_param; + } else { + // Consider the ray vertical. + // (though we shouldn't reach this often because there is an early check up-front) + enter_pos = p_params.from; + exit_pos = p_params.to; + } + + // Transform positions to heightmap space. + enter_pos *= HeightMapShape3DSW::BOUNDS_CHUNK_SIZE; + exit_pos *= HeightMapShape3DSW::BOUNDS_CHUNK_SIZE; + + // We did enter the flat projection of the AABB, + // but we have to check if we intersect it on the vertical axis. + if ((enter_pos.y > chunk.max) && (exit_pos.y > chunk.max)) { + return false; + } + if ((enter_pos.y < chunk.min) && (exit_pos.y < chunk.min)) { return false; } - Vector3 local_begin = p_begin + local_origin; - Vector3 local_end = p_end + local_origin; + return p_params.heightmap->_intersect_grid_segment(_heightmap_cell_cull_segment, enter_pos, exit_pos, p_params.heightmap->width, p_params.heightmap->depth, p_params.heightmap->local_origin, p_params.result, p_params.normal); +} + +template <typename ProcessFunction> +bool HeightMapShape3DSW::_intersect_grid_segment(ProcessFunction &p_process, const Vector3 &p_begin, const Vector3 &p_end, int p_width, int p_depth, const Vector3 &offset, Vector3 &r_point, Vector3 &r_normal) const { + Vector3 delta = (p_end - p_begin); + real_t length = delta.length(); + + if (length < CMP_EPSILON) { + return false; + } + + Vector3 local_begin = p_begin + offset; FaceShape3DSW face; face.backface_collision = false; @@ -1631,136 +1772,181 @@ bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 _HeightmapSegmentCullParams params; params.from = p_begin; params.to = p_end; - params.dir = (p_end - p_begin).normalized(); + params.dir = delta / length; params.heightmap = this; params.face = &face; - // Quantize the ray begin/end. - int begin_x = floor(local_begin.x); - int begin_z = floor(local_begin.z); - int end_x = floor(local_end.x); - int end_z = floor(local_end.z); + _HeightmapGridCullState state; - if ((begin_x == end_x) && (begin_z == end_z)) { - // Simple case for rays that don't traverse the grid horizontally. - // Just perform a test on the given cell. - int x = CLAMP(begin_x, 0, width - 2); - int z = CLAMP(begin_z, 0, depth - 2); - if (_heightmap_cell_cull_segment(params, x, z)) { - r_point = params.result; - r_normal = params.normal; - return true; - } - } else { - // Perform grid query from projected ray. - Vector2 ray_dir_proj(local_end.x - local_begin.x, local_end.z - local_begin.z); - real_t ray_dist_proj = ray_dir_proj.length(); + // Perform grid query from projected ray. + Vector2 ray_dir_flat(delta.x, delta.z); + state.length = length; + state.length_flat = ray_dir_flat.length(); - if (ray_dist_proj < CMP_EPSILON) { - ray_dir_proj = Vector2(); - } else { - ray_dir_proj /= ray_dist_proj; - } + if (state.length_flat < CMP_EPSILON) { + ray_dir_flat = Vector2(); + } else { + ray_dir_flat /= state.length_flat; + } - const int x_step = (ray_dir_proj.x > CMP_EPSILON) ? 1 : ((ray_dir_proj.x < -CMP_EPSILON) ? -1 : 0); - const int z_step = (ray_dir_proj.y > CMP_EPSILON) ? 1 : ((ray_dir_proj.y < -CMP_EPSILON) ? -1 : 0); + const int x_step = (ray_dir_flat.x > CMP_EPSILON) ? 1 : ((ray_dir_flat.x < -CMP_EPSILON) ? -1 : 0); + const int z_step = (ray_dir_flat.y > CMP_EPSILON) ? 1 : ((ray_dir_flat.y < -CMP_EPSILON) ? -1 : 0); - const real_t infinite = 1e20; - const real_t delta_x = (x_step != 0) ? 1.f / Math::abs(ray_dir_proj.x) : infinite; - const real_t delta_z = (z_step != 0) ? 1.f / Math::abs(ray_dir_proj.y) : infinite; + const real_t infinite = 1e20; + const real_t delta_x = (x_step != 0) ? 1.f / Math::abs(ray_dir_flat.x) : infinite; + const real_t delta_z = (z_step != 0) ? 1.f / Math::abs(ray_dir_flat.y) : infinite; - real_t cross_x; // At which value of `param` we will cross a x-axis lane? - real_t cross_z; // At which value of `param` we will cross a z-axis lane? + real_t cross_x; // At which value of `param` we will cross a x-axis lane? + real_t cross_z; // At which value of `param` we will cross a z-axis lane? - // X initialization. - if (x_step != 0) { - if (x_step == 1) { - cross_x = (ceil(local_begin.x) - local_begin.x) * delta_x; - } else { - cross_x = (local_begin.x - floor(local_begin.x)) * delta_x; - } + // X initialization. + if (x_step != 0) { + if (x_step == 1) { + cross_x = (Math::ceil(local_begin.x) - local_begin.x) * delta_x; } else { - cross_x = infinite; // Will never cross on X. + cross_x = (local_begin.x - Math::floor(local_begin.x)) * delta_x; } + } else { + cross_x = infinite; // Will never cross on X. + } - // Z initialization. - if (z_step != 0) { - if (z_step == 1) { - cross_z = (ceil(local_begin.z) - local_begin.z) * delta_z; - } else { - cross_z = (local_begin.z - floor(local_begin.z)) * delta_z; - } + // Z initialization. + if (z_step != 0) { + if (z_step == 1) { + cross_z = (Math::ceil(local_begin.z) - local_begin.z) * delta_z; } else { - cross_z = infinite; // Will never cross on Z. + cross_z = (local_begin.z - Math::floor(local_begin.z)) * delta_z; } + } else { + cross_z = infinite; // Will never cross on Z. + } - int x = floor(local_begin.x); - int z = floor(local_begin.z); + int x = Math::floor(local_begin.x); + int z = Math::floor(local_begin.z); - // Workaround cases where the ray starts at an integer position. - if (Math::is_zero_approx(cross_x)) { - cross_x += delta_x; - // If going backwards, we should ignore the position we would get by the above flooring, - // because the ray is not heading in that direction. - if (x_step == -1) { - x -= 1; - } + // Workaround cases where the ray starts at an integer position. + if (Math::is_zero_approx(cross_x)) { + cross_x += delta_x; + // If going backwards, we should ignore the position we would get by the above flooring, + // because the ray is not heading in that direction. + if (x_step == -1) { + x -= 1; } + } - if (Math::is_zero_approx(cross_z)) { - cross_z += delta_z; - if (z_step == -1) { - z -= 1; - } + if (Math::is_zero_approx(cross_z)) { + cross_z += delta_z; + if (z_step == -1) { + z -= 1; } + } + + // Start inside the grid. + int x_start = MAX(MIN(x, p_width - 2), 0); + int z_start = MAX(MIN(z, p_depth - 2), 0); - // Start inside the grid. - int x_start = CLAMP(x, 0, width - 2); - int z_start = CLAMP(z, 0, depth - 2); + // Adjust initial cross values. + cross_x += delta_x * x_step * (x_start - x); + cross_z += delta_z * z_step * (z_start - z); - // Adjust initial cross values. - cross_x += delta_x * x_step * (x_start - x); - cross_z += delta_z * z_step * (z_start - z); + x = x_start; + z = z_start; - x = x_start; - z = z_start; + while (true) { + state.prev_dist = state.dist; + state.x = x; + state.z = z; - if (_heightmap_cell_cull_segment(params, x, z)) { + if (cross_x < cross_z) { + // X lane. + x += x_step; + // Assign before advancing the param, + // to be in sync with the initialization step. + state.dist = cross_x; + cross_x += delta_x; + } else { + // Z lane. + z += z_step; + state.dist = cross_z; + cross_z += delta_z; + } + + if (state.dist > state.length_flat) { + state.dist = state.length_flat; + if (p_process(params, state)) { + r_point = params.result; + r_normal = params.normal; + return true; + } + break; + } + + if (p_process(params, state)) { r_point = params.result; r_normal = params.normal; return true; } - real_t dist = 0.0; - while (true) { - if (cross_x < cross_z) { - // X lane. - x += x_step; - // Assign before advancing the param, - // to be in sync with the initialization step. - dist = cross_x; - cross_x += delta_x; - } else { - // Z lane. - z += z_step; - dist = cross_z; - cross_z += delta_z; - } + // Stop when outside the grid. + if ((x < 0) || (z < 0) || (x >= p_width - 1) || (z >= p_depth - 1)) { + break; + } + } - // Stop when outside the grid. - if ((x < 0) || (z < 0) || (x >= width - 1) || (z >= depth - 1)) { - break; - } + return false; +} - if (_heightmap_cell_cull_segment(params, x, z)) { - r_point = params.result; - r_normal = params.normal; - return true; - } +bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const { + if (heights.is_empty()) { + return false; + } - if (dist > ray_dist_proj) { - break; - } + Vector3 local_begin = p_begin + local_origin; + Vector3 local_end = p_end + local_origin; + + // Quantize the ray begin/end. + int begin_x = Math::floor(local_begin.x); + int begin_z = Math::floor(local_begin.z); + int end_x = Math::floor(local_end.x); + int end_z = Math::floor(local_end.z); + + if ((begin_x == end_x) && (begin_z == end_z)) { + // Simple case for rays that don't traverse the grid horizontally. + // Just perform a test on the given cell. + FaceShape3DSW face; + face.backface_collision = false; + + _HeightmapSegmentCullParams params; + params.from = p_begin; + params.to = p_end; + params.dir = (p_end - p_begin).normalized(); + + params.heightmap = this; + params.face = &face; + + _HeightmapGridCullState state; + state.x = MAX(MIN(begin_x, width - 2), 0); + state.z = MAX(MIN(begin_z, depth - 2), 0); + if (_heightmap_cell_cull_segment(params, state)) { + r_point = params.result; + r_normal = params.normal; + return true; + } + } else if (bounds_grid.is_empty()) { + // Process all cells intersecting the flat projection of the ray. + return _intersect_grid_segment(_heightmap_cell_cull_segment, p_begin, p_end, width, depth, local_origin, r_point, r_normal); + } else { + Vector3 ray_diff = (p_end - p_begin); + real_t length_flat_sqr = ray_diff.x * ray_diff.x + ray_diff.z * ray_diff.z; + if (length_flat_sqr < BOUNDS_CHUNK_SIZE * BOUNDS_CHUNK_SIZE) { + // Don't use chunks, the ray is too short in the plane. + return _intersect_grid_segment(_heightmap_cell_cull_segment, p_begin, p_end, width, depth, local_origin, r_point, r_normal); + } else { + // The ray is long, run raycast on a higher-level grid. + Vector3 bounds_from = p_begin / BOUNDS_CHUNK_SIZE; + Vector3 bounds_to = p_end / BOUNDS_CHUNK_SIZE; + Vector3 bounds_offset = local_origin / BOUNDS_CHUNK_SIZE; + return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_grid_width, bounds_grid_depth, bounds_offset, r_point, r_normal); } } @@ -1790,7 +1976,7 @@ void HeightMapShape3DSW::_get_cell(const Vector3 &p_point, int &r_x, int &r_y, i r_z = (clamped_point.z < 0.0) ? (clamped_point.z - 0.5) : (clamped_point.z + 0.5); } -void HeightMapShape3DSW::cull(const AABB &p_local_aabb, Callback p_callback, void *p_userdata) const { +void HeightMapShape3DSW::cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata) const { if (heights.is_empty()) { return; } @@ -1825,14 +2011,18 @@ void HeightMapShape3DSW::cull(const AABB &p_local_aabb, Callback p_callback, voi _get_point(x, z, face.vertex[0]); _get_point(x + 1, z, face.vertex[1]); _get_point(x, z + 1, face.vertex[2]); - face.normal = Plane(face.vertex[0], face.vertex[2], face.vertex[1]).normal; - p_callback(p_userdata, &face); + face.normal = Plane(face.vertex[0], face.vertex[1], face.vertex[2]).normal; + if (p_callback(p_userdata, &face)) { + return; + } // Second triangle. face.vertex[0] = face.vertex[1]; _get_point(x + 1, z + 1, face.vertex[1]); - face.normal = Plane(face.vertex[0], face.vertex[2], face.vertex[1]).normal; - p_callback(p_userdata, &face); + face.normal = Plane(face.vertex[0], face.vertex[1], face.vertex[2]).normal; + if (p_callback(p_userdata, &face)) { + return; + } } } } @@ -1847,6 +2037,75 @@ Vector3 HeightMapShape3DSW::get_moment_of_inertia(real_t p_mass) const { (p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y)); } +void HeightMapShape3DSW::_build_accelerator() { + bounds_grid.clear(); + + bounds_grid_width = width / BOUNDS_CHUNK_SIZE; + bounds_grid_depth = depth / BOUNDS_CHUNK_SIZE; + + if (width % BOUNDS_CHUNK_SIZE > 0) { + ++bounds_grid_width; // In case terrain size isn't dividable by chunk size. + } + + if (depth % BOUNDS_CHUNK_SIZE > 0) { + ++bounds_grid_depth; + } + + uint32_t bound_grid_size = (uint32_t)(bounds_grid_width * bounds_grid_depth); + + if (bound_grid_size < 2) { + // Grid is empty or just one chunk. + return; + } + + bounds_grid.resize(bound_grid_size); + + // Compute min and max height for all chunks. + for (int cz = 0; cz < bounds_grid_depth; ++cz) { + int z0 = cz * BOUNDS_CHUNK_SIZE; + + for (int cx = 0; cx < bounds_grid_width; ++cx) { + int x0 = cx * BOUNDS_CHUNK_SIZE; + + Range r; + + r.min = _get_height(x0, z0); + r.max = r.min; + + // Compute min and max height for this chunk. + // We have to include one extra cell to account for neighbors. + // Here is why: + // Say we have a flat terrain, and a plateau that fits a chunk perfectly. + // + // Left Right + // 0---0---0---1---1---1 + // | | | | | | + // 0---0---0---1---1---1 + // | | | | | | + // 0---0---0---1---1---1 + // x + // + // If the AABB for the Left chunk did not share vertices with the Right, + // then we would fail collision tests at x due to a gap. + // + int z_max = MIN(z0 + BOUNDS_CHUNK_SIZE + 1, depth); + int x_max = MIN(x0 + BOUNDS_CHUNK_SIZE + 1, width); + for (int z = z0; z < z_max; ++z) { + for (int x = x0; x < x_max; ++x) { + real_t height = _get_height(x, z); + if (height < r.min) { + r.min = height; + } else if (height > r.max) { + r.max = height; + } + } + } + + bounds_grid[cx + cz * bounds_grid_width] = r; + } + } +} + void HeightMapShape3DSW::_setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { heights = p_heights; width = p_width; @@ -1863,6 +2122,8 @@ void HeightMapShape3DSW::_setup(const Vector<real_t> &p_heights, int p_width, in aabb.position -= local_origin; + _build_accelerator(); + configure(aabb); } @@ -1921,7 +2182,7 @@ void HeightMapShape3DSW::set_data(const Variant &p_data) { } else { int heights_size = heights.size(); for (int i = 0; i < heights_size; ++i) { - float h = heights[i]; + real_t h = heights[i]; if (h < min_height) { min_height = h; } else if (h > max_height) { diff --git a/servers/physics_3d/shape_3d_sw.h b/servers/physics_3d/shape_3d_sw.h index ca58900111..73eddc469c 100644 --- a/servers/physics_3d/shape_3d_sw.h +++ b/servers/physics_3d/shape_3d_sw.h @@ -32,6 +32,7 @@ #define SHAPE_SW_H #include "core/math/geometry_3d.h" +#include "core/templates/local_vector.h" #include "servers/physics_server_3d.h" class Shape3DSW; @@ -101,10 +102,12 @@ public: class ConcaveShape3DSW : public Shape3DSW { public: virtual bool is_concave() const override { return true; } - typedef void (*Callback)(void *p_userdata, Shape3DSW *p_convex); virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; } - virtual void cull(const AABB &p_local_aabb, Callback p_callback, void *p_userdata) const = 0; + // Returns true to stop the query. + typedef bool (*QueryCallback)(void *p_userdata, Shape3DSW *p_convex); + + virtual void cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata) const = 0; ConcaveShape3DSW() {} }; @@ -117,23 +120,51 @@ class PlaneShape3DSW : public Shape3DSW { public: Plane get_plane() const; - virtual real_t get_area() const { return INFINITY; } - virtual PhysicsServer3D::ShapeType get_type() const { return PhysicsServer3D::SHAPE_PLANE; } - virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const; - virtual Vector3 get_support(const Vector3 &p_normal) const; - virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const { r_amount = 0; } + virtual real_t get_area() const override { return INFINITY; } + virtual PhysicsServer3D::ShapeType get_type() const override { return PhysicsServer3D::SHAPE_PLANE; } + virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; + virtual Vector3 get_support(const Vector3 &p_normal) const override; + virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; } - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const; - virtual bool intersect_point(const Vector3 &p_point) const; - virtual Vector3 get_closest_point_to(const Vector3 &p_point) const; - virtual Vector3 get_moment_of_inertia(real_t p_mass) const; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const override; + virtual bool intersect_point(const Vector3 &p_point) const override; + virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; + virtual Vector3 get_moment_of_inertia(real_t p_mass) const override; - virtual void set_data(const Variant &p_data); - virtual Variant get_data() const; + virtual void set_data(const Variant &p_data) override; + virtual Variant get_data() const override; PlaneShape3DSW(); }; +class SeparationRayShape3DSW : public Shape3DSW { + real_t length; + bool slide_on_slope; + + void _setup(real_t p_length, bool p_slide_on_slope); + +public: + real_t get_length() const; + bool get_slide_on_slope() const; + + virtual real_t get_area() const override { return 0.0; } + virtual PhysicsServer3D::ShapeType get_type() const override { return PhysicsServer3D::SHAPE_SEPARATION_RAY; } + virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; + virtual Vector3 get_support(const Vector3 &p_normal) const override; + virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; + + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const override; + virtual bool intersect_point(const Vector3 &p_point) const override; + virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; + + virtual Vector3 get_moment_of_inertia(real_t p_mass) const override; + + virtual void set_data(const Variant &p_data) override; + virtual Variant get_data() const override; + + SeparationRayShape3DSW(); +}; + class SphereShape3DSW : public Shape3DSW { real_t radius; @@ -295,7 +326,7 @@ struct ConcavePolygonShape3DSW : public ConcaveShape3DSW { struct _CullParams { AABB aabb; - Callback callback = nullptr; + QueryCallback callback = nullptr; void *userdata = nullptr; const Face *faces = nullptr; const Vector3 *vertices = nullptr; @@ -321,7 +352,7 @@ struct ConcavePolygonShape3DSW : public ConcaveShape3DSW { bool backface_collision = false; void _cull_segment(int p_idx, _SegmentCullParams *p_params) const; - void _cull(int p_idx, _CullParams *p_params) const; + bool _cull(int p_idx, _CullParams *p_params) const; void _fill_bvh(_VolumeSW_BVH *p_bvh_tree, BVH *p_bvh_array, int &p_idx); @@ -339,7 +370,7 @@ public: virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; - virtual void cull(const AABB &p_local_aabb, Callback p_callback, void *p_userdata) const override; + virtual void cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata) const override; virtual Vector3 get_moment_of_inertia(real_t p_mass) const override; @@ -355,6 +386,21 @@ struct HeightMapShape3DSW : public ConcaveShape3DSW { int depth = 0; Vector3 local_origin; + // Accelerator. + struct Range { + real_t min = 0.0; + real_t max = 0.0; + }; + LocalVector<Range> bounds_grid; + int bounds_grid_width = 0; + int bounds_grid_depth = 0; + + static const int BOUNDS_CHUNK_SIZE = 16; + + _FORCE_INLINE_ const Range &_get_bounds_chunk(int p_x, int p_z) const { + return bounds_grid[(p_z * bounds_grid_width) + p_x]; + } + _FORCE_INLINE_ real_t _get_height(int p_x, int p_z) const { return heights[(p_z * width) + p_x]; } @@ -367,6 +413,11 @@ struct HeightMapShape3DSW : public ConcaveShape3DSW { void _get_cell(const Vector3 &p_point, int &r_x, int &r_y, int &r_z) const; + void _build_accelerator(); + + template <typename ProcessFunction> + bool _intersect_grid_segment(ProcessFunction &p_process, const Vector3 &p_begin, const Vector3 &p_end, int p_width, int p_depth, const Vector3 &offset, Vector3 &r_point, Vector3 &r_normal) const; + void _setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height); public: @@ -382,7 +433,7 @@ public: virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; - virtual void cull(const AABB &p_local_aabb, Callback p_callback, void *p_userdata) const override; + virtual void cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata) const override; virtual Vector3 get_moment_of_inertia(real_t p_mass) const override; diff --git a/servers/physics_3d/soft_body_3d_sw.cpp b/servers/physics_3d/soft_body_3d_sw.cpp index ead6497f1f..d7e13867bf 100644 --- a/servers/physics_3d/soft_body_3d_sw.cpp +++ b/servers/physics_3d/soft_body_3d_sw.cpp @@ -964,16 +964,9 @@ void SoftBody3DSW::apply_forces(bool p_has_wind_forces) { } void SoftBody3DSW::_compute_area_gravity(const Area3DSW *p_area) { - if (p_area->is_gravity_point()) { - if (p_area->get_gravity_distance_scale() > 0) { - Vector3 v = p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin(); - gravity += v.normalized() * (p_area->get_gravity() / Math::pow(v.length() * p_area->get_gravity_distance_scale() + 1, 2)); - } else { - gravity += (p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin()).normalized() * p_area->get_gravity(); - } - } else { - gravity += p_area->get_gravity_vector() * p_area->get_gravity(); - } + Vector3 area_gravity; + p_area->compute_gravity(get_transform().get_origin(), area_gravity); + gravity += area_gravity; } Vector3 SoftBody3DSW::_compute_area_windforce(const Area3DSW *p_area, const Face *p_face) { diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp index f9e55ad525..3b08184e00 100644 --- a/servers/physics_3d/space_3d_sw.cpp +++ b/servers/physics_3d/space_3d_sw.cpp @@ -569,7 +569,7 @@ int Space3DSW::_cull_aabb_for_body(Body3DSW *p_body, const AABB &p_aabb) { return amount; } -bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, const Set<RID> &p_exclude) { +bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) { //give me back regular physics engine logic //this is madness //and most people using this function will think @@ -714,9 +714,19 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co continue; } - Transform3D body_shape_xform = body_transform * p_body->get_shape_transform(j); Shape3DSW *body_shape = p_body->get_shape(j); + // Colliding separation rays allows to properly snap to the ground, + // otherwise it's not needed in regular motion. + if (!p_collide_separation_ray && (body_shape->get_type() == PhysicsServer3D::SHAPE_SEPARATION_RAY)) { + // When slide on slope is on, separation ray shape acts like a regular shape. + if (!static_cast<SeparationRayShape3DSW *>(body_shape)->get_slide_on_slope()) { + continue; + } + } + + Transform3D body_shape_xform = body_transform * p_body->get_shape_transform(j); + Transform3D body_shape_xform_inv = body_shape_xform.affine_inverse(); MotionShape3DSW mshape; mshape.shape = body_shape; @@ -967,12 +977,12 @@ void Space3DSW::body_remove_from_active_list(SelfList<Body3DSW> *p_body) { active_list.remove(p_body); } -void Space3DSW::body_add_to_inertia_update_list(SelfList<Body3DSW> *p_body) { - inertia_update_list.add(p_body); +void Space3DSW::body_add_to_mass_properties_update_list(SelfList<Body3DSW> *p_body) { + mass_properties_update_list.add(p_body); } -void Space3DSW::body_remove_from_inertia_update_list(SelfList<Body3DSW> *p_body) { - inertia_update_list.remove(p_body); +void Space3DSW::body_remove_from_mass_properties_update_list(SelfList<Body3DSW> *p_body) { + mass_properties_update_list.remove(p_body); } BroadPhase3DSW *Space3DSW::get_broadphase() { @@ -1049,9 +1059,9 @@ void Space3DSW::call_queries() { void Space3DSW::setup() { contact_debug_count = 0; - while (inertia_update_list.first()) { - inertia_update_list.first()->self()->update_inertias(); - inertia_update_list.remove(inertia_update_list.first()); + while (mass_properties_update_list.first()) { + mass_properties_update_list.first()->self()->update_mass_properties(); + mass_properties_update_list.remove(mass_properties_update_list.first()); } } diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h index 9b5b4de069..98c335cb80 100644 --- a/servers/physics_3d/space_3d_sw.h +++ b/servers/physics_3d/space_3d_sw.h @@ -48,12 +48,12 @@ class PhysicsDirectSpaceState3DSW : public PhysicsDirectSpaceState3D { public: Space3DSW *space; - virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override; - virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override; - virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override; + virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override; + virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override; PhysicsDirectSpaceState3DSW(); @@ -79,7 +79,7 @@ private: BroadPhase3DSW *broadphase; SelfList<Body3DSW>::List active_list; - SelfList<Body3DSW>::List inertia_update_list; + SelfList<Body3DSW>::List mass_properties_update_list; SelfList<Body3DSW>::List state_query_list; SelfList<Area3DSW>::List monitor_query_list; SelfList<Area3DSW>::List area_moved_list; @@ -112,6 +112,8 @@ private: bool locked; + real_t last_step = 0.001; + int island_count; int active_objects; int collision_pairs; @@ -135,8 +137,8 @@ public: const SelfList<Body3DSW>::List &get_active_body_list() const; void body_add_to_active_list(SelfList<Body3DSW> *p_body); void body_remove_from_active_list(SelfList<Body3DSW> *p_body); - void body_add_to_inertia_update_list(SelfList<Body3DSW> *p_body); - void body_remove_from_inertia_update_list(SelfList<Body3DSW> *p_body); + void body_add_to_mass_properties_update_list(SelfList<Body3DSW> *p_body); + void body_remove_from_mass_properties_update_list(SelfList<Body3DSW> *p_body); void body_add_to_state_query_list(SelfList<Body3DSW> *p_body); void body_remove_from_state_query_list(SelfList<Body3DSW> *p_body); @@ -174,6 +176,9 @@ public: void lock(); void unlock(); + real_t get_last_step() const { return last_step; } + void set_last_step(real_t p_step) { last_step = p_step; } + void set_param(PhysicsServer3D::SpaceParameter p_param, real_t p_value); real_t get_param(PhysicsServer3D::SpaceParameter p_param) const; @@ -203,7 +208,7 @@ public: void set_elapsed_time(ElapsedTime p_time, uint64_t p_msec) { elapsed_time[p_time] = p_msec; } uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; } - bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, const Set<RID> &p_exclude = Set<RID>()); + bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); Space3DSW(); ~Space3DSW(); diff --git a/servers/physics_3d/step_3d_sw.cpp b/servers/physics_3d/step_3d_sw.cpp index ba18bac611..d0604b0aa0 100644 --- a/servers/physics_3d/step_3d_sw.cpp +++ b/servers/physics_3d/step_3d_sw.cpp @@ -185,6 +185,8 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) { p_space->setup(); //update inertias, etc + p_space->set_last_step(p_delta); + iterations = p_iterations; delta = p_delta; diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index c96f85446c..2656ef1d6d 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -77,6 +77,7 @@ void PhysicsDirectBodyState2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_total_linear_damp"), &PhysicsDirectBodyState2D::get_total_linear_damp); ClassDB::bind_method(D_METHOD("get_total_angular_damp"), &PhysicsDirectBodyState2D::get_total_angular_damp); + ClassDB::bind_method(D_METHOD("get_center_of_mass"), &PhysicsDirectBodyState2D::get_center_of_mass); ClassDB::bind_method(D_METHOD("get_inverse_mass"), &PhysicsDirectBodyState2D::get_inverse_mass); ClassDB::bind_method(D_METHOD("get_inverse_inertia"), &PhysicsDirectBodyState2D::get_inverse_inertia); @@ -123,6 +124,7 @@ void PhysicsDirectBodyState2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_angular_damp"), "", "get_total_angular_damp"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_linear_damp"), "", "get_total_linear_damp"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "total_gravity"), "", "get_total_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass"), "", "get_center_of_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleep_state", "is_sleeping"); @@ -257,13 +259,6 @@ void PhysicsShapeQueryParameters2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas"), "set_collide_with_areas", "is_collide_with_areas_enabled"); } -PhysicsShapeQueryParameters2D::PhysicsShapeQueryParameters2D() { - margin = 0; - collision_mask = 0x7FFFFFFF; - collide_with_bodies = true; - collide_with_areas = false; -} - Dictionary PhysicsDirectSpaceState2D::_intersect_ray(const Vector2 &p_from, const Vector2 &p_to, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas) { RayResult inters; Set<RID> exclude; @@ -411,9 +406,9 @@ PhysicsDirectSpaceState2D::PhysicsDirectSpaceState2D() { } void PhysicsDirectSpaceState2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(UINT32_MAX), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(UINT32_MAX), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_ray, DEFVAL(Array()), DEFVAL(UINT32_MAX), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &PhysicsDirectSpaceState2D::_intersect_shape, DEFVAL(32)); ClassDB::bind_method(D_METHOD("cast_motion", "shape"), &PhysicsDirectSpaceState2D::_cast_motion); ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &PhysicsDirectSpaceState2D::_collide_shape, DEFVAL(32)); @@ -500,7 +495,7 @@ void PhysicsTestMotionResult2D::_bind_methods() { /////////////////////////////////////// -bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result, const Vector<RID> &p_exclude) { +bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result, bool p_collide_separation_ray, const Vector<RID> &p_exclude) { MotionResult *r = nullptr; if (p_result.is_valid()) { r = p_result->get_result_ptr(); @@ -509,11 +504,12 @@ bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, c for (int i = 0; i < p_exclude.size(); i++) { exclude.insert(p_exclude[i]); } - return body_test_motion(p_body, p_from, p_motion, p_margin, r, exclude); + return body_test_motion(p_body, p_from, p_motion, p_margin, r, p_collide_separation_ray, exclude); } void PhysicsServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("world_margin_shape_create"), &PhysicsServer2D::world_margin_shape_create); + ClassDB::bind_method(D_METHOD("separation_ray_shape_create"), &PhysicsServer2D::separation_ray_shape_create); ClassDB::bind_method(D_METHOD("segment_shape_create"), &PhysicsServer2D::segment_shape_create); ClassDB::bind_method(D_METHOD("circle_shape_create"), &PhysicsServer2D::circle_shape_create); ClassDB::bind_method(D_METHOD("rectangle_shape_create"), &PhysicsServer2D::rectangle_shape_create); @@ -613,6 +609,8 @@ void PhysicsServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_param", "body", "param", "value"), &PhysicsServer2D::body_set_param); ClassDB::bind_method(D_METHOD("body_get_param", "body", "param"), &PhysicsServer2D::body_get_param); + ClassDB::bind_method(D_METHOD("body_reset_mass_properties", "body"), &PhysicsServer2D::body_reset_mass_properties); + ClassDB::bind_method(D_METHOD("body_set_state", "body", "state", "value"), &PhysicsServer2D::body_set_state); ClassDB::bind_method(D_METHOD("body_get_state", "body", "state"), &PhysicsServer2D::body_get_state); @@ -635,7 +633,7 @@ void PhysicsServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_force_integration_callback", "body", "callable", "userdata"), &PhysicsServer2D::body_set_force_integration_callback, DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "exclude"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "collide_separation_ray", "exclude"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(false), DEFVAL(Array())); ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer2D::body_get_direct_state); @@ -675,6 +673,7 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH); BIND_ENUM_CONSTANT(SHAPE_WORLD_MARGIN); + BIND_ENUM_CONSTANT(SHAPE_SEPARATION_RAY); BIND_ENUM_CONSTANT(SHAPE_SEGMENT); BIND_ENUM_CONSTANT(SHAPE_CIRCLE); BIND_ENUM_CONSTANT(SHAPE_RECTANGLE); @@ -707,6 +706,7 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(BODY_PARAM_FRICTION); BIND_ENUM_CONSTANT(BODY_PARAM_MASS); BIND_ENUM_CONSTANT(BODY_PARAM_INERTIA); + BIND_ENUM_CONSTANT(BODY_PARAM_CENTER_OF_MASS); BIND_ENUM_CONSTANT(BODY_PARAM_GRAVITY_SCALE); BIND_ENUM_CONSTANT(BODY_PARAM_LINEAR_DAMP); BIND_ENUM_CONSTANT(BODY_PARAM_ANGULAR_DAMP); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 4c559dd7bd..1145bb8b91 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -48,6 +48,7 @@ public: virtual real_t get_total_linear_damp() const = 0; // get density of this body space/area virtual real_t get_total_angular_damp() const = 0; // get density of this body space/area + virtual Vector2 get_center_of_mass() const = 0; virtual real_t get_inverse_mass() const = 0; // get the mass virtual real_t get_inverse_inertia() const = 0; // get density of this body space @@ -103,12 +104,12 @@ class PhysicsShapeQueryParameters2D : public RefCounted { RID shape; Transform2D transform; Vector2 motion; - real_t margin; + real_t margin = 0.0; Set<RID> exclude; - uint32_t collision_mask; + uint32_t collision_mask = UINT32_MAX; - bool collide_with_bodies; - bool collide_with_areas; + bool collide_with_bodies = true; + bool collide_with_areas = false; protected: static void _bind_methods(); @@ -139,8 +140,6 @@ public: void set_exclude(const Vector<RID> &p_exclude); Vector<RID> get_exclude() const; - - PhysicsShapeQueryParameters2D(); }; class PhysicsDirectSpaceState2D : public Object { @@ -169,7 +168,7 @@ public: Variant metadata; }; - virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; struct ShapeResult { RID rid; @@ -179,14 +178,14 @@ public: Variant metadata; }; - virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0; - virtual int intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0; + virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0; + virtual int intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0; - virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; - virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; - virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; struct ShapeRestInfo { Vector2 point; @@ -198,7 +197,7 @@ public: Variant metadata; }; - virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; PhysicsDirectSpaceState2D(); }; @@ -210,7 +209,7 @@ class PhysicsServer2D : public Object { static PhysicsServer2D *singleton; - virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>(), const Vector<RID> &p_exclude = Vector<RID>()); + virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>(), bool p_collide_separation_ray = false, const Vector<RID> &p_exclude = Vector<RID>()); protected: static void _bind_methods(); @@ -220,6 +219,7 @@ public: enum ShapeType { SHAPE_WORLD_MARGIN, ///< plane:"plane" + SHAPE_SEPARATION_RAY, ///< float:"length" SHAPE_SEGMENT, ///< float:"length" SHAPE_CIRCLE, ///< float:"radius" SHAPE_RECTANGLE, ///< vec3:"extents" @@ -230,6 +230,7 @@ public: }; virtual RID world_margin_shape_create() = 0; + virtual RID separation_ray_shape_create() = 0; virtual RID segment_shape_create() = 0; virtual RID circle_shape_create() = 0; virtual RID rectangle_shape_create() = 0; @@ -402,15 +403,18 @@ public: BODY_PARAM_BOUNCE, BODY_PARAM_FRICTION, BODY_PARAM_MASS, ///< unused for static, always infinite - BODY_PARAM_INERTIA, // read-only: computed from mass & shapes + BODY_PARAM_INERTIA, + BODY_PARAM_CENTER_OF_MASS, BODY_PARAM_GRAVITY_SCALE, BODY_PARAM_LINEAR_DAMP, BODY_PARAM_ANGULAR_DAMP, BODY_PARAM_MAX, }; - virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value) = 0; - virtual real_t body_get_param(RID p_body, BodyParameter p_param) const = 0; + virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) = 0; + virtual Variant body_get_param(RID p_body, BodyParameter p_param) const = 0; + + virtual void body_reset_mass_properties(RID p_body) = 0; //state enum BodyState { @@ -455,6 +459,10 @@ public: virtual void body_set_omit_force_integration(RID p_body, bool p_omit) = 0; virtual bool body_is_omitting_force_integration(RID p_body) const = 0; + // Callback for C++ use only. + typedef void (*BodyStateCallback)(void *p_instance, PhysicsDirectBodyState2D *p_state); + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) = 0; + virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) = 0; virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) = 0; @@ -485,7 +493,7 @@ public: } }; - virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) = 0; + virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) = 0; struct SeparationResult { real_t collision_depth; diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index 1f46a96b27..0c487b83ea 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -249,13 +249,6 @@ void PhysicsShapeQueryParameters3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas"), "set_collide_with_areas", "is_collide_with_areas_enabled"); } -PhysicsShapeQueryParameters3D::PhysicsShapeQueryParameters3D() { - margin = 0; - collision_mask = 0x7FFFFFFF; - collide_with_bodies = true; - collide_with_areas = false; -} - ///////////////////////////////////// Dictionary PhysicsDirectSpaceState3D::_intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { @@ -360,7 +353,7 @@ PhysicsDirectSpaceState3D::PhysicsDirectSpaceState3D() { } void PhysicsDirectSpaceState3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState3D::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState3D::_intersect_ray, DEFVAL(Array()), DEFVAL(UINT32_MAX), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &PhysicsDirectSpaceState3D::_intersect_shape, DEFVAL(32)); ClassDB::bind_method(D_METHOD("cast_motion", "shape", "motion"), &PhysicsDirectSpaceState3D::_cast_motion); ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &PhysicsDirectSpaceState3D::_collide_shape, DEFVAL(32)); @@ -447,7 +440,7 @@ void PhysicsTestMotionResult3D::_bind_methods() { /////////////////////////////////////// -bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult3D> &p_result, const Vector<RID> &p_exclude) { +bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult3D> &p_result, bool p_collide_separation_ray, const Vector<RID> &p_exclude) { MotionResult *r = nullptr; if (p_result.is_valid()) { r = p_result->get_result_ptr(); @@ -456,13 +449,15 @@ bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, c for (int i = 0; i < p_exclude.size(); i++) { exclude.insert(p_exclude[i]); } - return body_test_motion(p_body, p_from, p_motion, p_margin, r, exclude); + return body_test_motion(p_body, p_from, p_motion, p_margin, r, p_collide_separation_ray, exclude); } RID PhysicsServer3D::shape_create(ShapeType p_shape) { switch (p_shape) { case SHAPE_PLANE: return plane_shape_create(); + case SHAPE_SEPARATION_RAY: + return separation_ray_shape_create(); case SHAPE_SPHERE: return sphere_shape_create(); case SHAPE_BOX: @@ -488,6 +483,7 @@ void PhysicsServer3D::_bind_methods() { #ifndef _3D_DISABLED ClassDB::bind_method(D_METHOD("plane_shape_create"), &PhysicsServer3D::plane_shape_create); + ClassDB::bind_method(D_METHOD("separation_ray_shape_create"), &PhysicsServer3D::separation_ray_shape_create); ClassDB::bind_method(D_METHOD("sphere_shape_create"), &PhysicsServer3D::sphere_shape_create); ClassDB::bind_method(D_METHOD("box_shape_create"), &PhysicsServer3D::box_shape_create); ClassDB::bind_method(D_METHOD("capsule_shape_create"), &PhysicsServer3D::capsule_shape_create); @@ -581,6 +577,8 @@ void PhysicsServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_param", "body", "param", "value"), &PhysicsServer3D::body_set_param); ClassDB::bind_method(D_METHOD("body_get_param", "body", "param"), &PhysicsServer3D::body_get_param); + ClassDB::bind_method(D_METHOD("body_reset_mass_properties", "body"), &PhysicsServer3D::body_reset_mass_properties); + ClassDB::bind_method(D_METHOD("body_set_state", "body", "state", "value"), &PhysicsServer3D::body_set_state); ClassDB::bind_method(D_METHOD("body_get_state", "body", "state"), &PhysicsServer3D::body_get_state); @@ -609,7 +607,7 @@ void PhysicsServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_ray_pickable", "body", "enable"), &PhysicsServer3D::body_set_ray_pickable); - ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "exclude"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant()), DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "collide_separation_ray", "exclude"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant()), DEFVAL(false), DEFVAL(Array())); ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer3D::body_get_direct_state); @@ -748,6 +746,7 @@ void PhysicsServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &PhysicsServer3D::get_process_info); BIND_ENUM_CONSTANT(SHAPE_PLANE); + BIND_ENUM_CONSTANT(SHAPE_SEPARATION_RAY); BIND_ENUM_CONSTANT(SHAPE_SPHERE); BIND_ENUM_CONSTANT(SHAPE_BOX); BIND_ENUM_CONSTANT(SHAPE_CAPSULE); @@ -785,6 +784,8 @@ void PhysicsServer3D::_bind_methods() { BIND_ENUM_CONSTANT(BODY_PARAM_BOUNCE); BIND_ENUM_CONSTANT(BODY_PARAM_FRICTION); BIND_ENUM_CONSTANT(BODY_PARAM_MASS); + BIND_ENUM_CONSTANT(BODY_PARAM_INERTIA); + BIND_ENUM_CONSTANT(BODY_PARAM_CENTER_OF_MASS); BIND_ENUM_CONSTANT(BODY_PARAM_GRAVITY_SCALE); BIND_ENUM_CONSTANT(BODY_PARAM_LINEAR_DAMP); BIND_ENUM_CONSTANT(BODY_PARAM_ANGULAR_DAMP); diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 5201e76498..5677604682 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -103,12 +103,12 @@ class PhysicsShapeQueryParameters3D : public RefCounted { RES shape_ref; RID shape; Transform3D transform; - real_t margin; + real_t margin = 0.0; Set<RID> exclude; - uint32_t collision_mask; + uint32_t collision_mask = UINT32_MAX; - bool collide_with_bodies; - bool collide_with_areas; + bool collide_with_bodies = true; + bool collide_with_areas = false; protected: static void _bind_methods(); @@ -136,15 +136,13 @@ public: void set_collide_with_areas(bool p_enable); bool is_collide_with_areas_enabled() const; - - PhysicsShapeQueryParameters3D(); }; class PhysicsDirectSpaceState3D : public Object { GDCLASS(PhysicsDirectSpaceState3D, Object); private: - Dictionary _intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_collision_mask = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); + Dictionary _intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); Array _intersect_shape(const Ref<PhysicsShapeQueryParameters3D> &p_shape_query, int p_max_results = 32); Array _cast_motion(const Ref<PhysicsShapeQueryParameters3D> &p_shape_query, const Vector3 &p_motion); Array _collide_shape(const Ref<PhysicsShapeQueryParameters3D> &p_shape_query, int p_max_results = 32); @@ -161,7 +159,7 @@ public: int shape = 0; }; - virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; struct RayResult { Vector3 position; @@ -172,9 +170,9 @@ public: int shape = 0; }; - virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) = 0; + virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) = 0; - virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; struct ShapeRestInfo { Vector3 point; @@ -185,11 +183,11 @@ public: Vector3 linear_velocity; //velocity at contact point }; - virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) = 0; + virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) = 0; - virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; - virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; + virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0; virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const = 0; @@ -212,7 +210,7 @@ class PhysicsServer3D : public Object { static PhysicsServer3D *singleton; - virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, const Ref<PhysicsTestMotionResult3D> &p_result = Ref<PhysicsTestMotionResult3D>(), const Vector<RID> &p_exclude = Vector<RID>()); + virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, const Ref<PhysicsTestMotionResult3D> &p_result = Ref<PhysicsTestMotionResult3D>(), bool p_collide_separation_ray = false, const Vector<RID> &p_exclude = Vector<RID>()); protected: static void _bind_methods(); @@ -222,6 +220,7 @@ public: enum ShapeType { SHAPE_PLANE, ///< plane:"plane" + SHAPE_SEPARATION_RAY, ///< float:"length" SHAPE_SPHERE, ///< float:"radius" SHAPE_BOX, ///< vec3:"extents" SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule @@ -236,6 +235,7 @@ public: RID shape_create(ShapeType p_shape); virtual RID plane_shape_create() = 0; + virtual RID separation_ray_shape_create() = 0; virtual RID sphere_shape_create() = 0; virtual RID box_shape_create() = 0; virtual RID capsule_shape_create() = 0; @@ -405,14 +405,18 @@ public: BODY_PARAM_BOUNCE, BODY_PARAM_FRICTION, BODY_PARAM_MASS, ///< unused for static, always infinite + BODY_PARAM_INERTIA, + BODY_PARAM_CENTER_OF_MASS, BODY_PARAM_GRAVITY_SCALE, BODY_PARAM_LINEAR_DAMP, BODY_PARAM_ANGULAR_DAMP, BODY_PARAM_MAX, }; - virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value) = 0; - virtual real_t body_get_param(RID p_body, BodyParameter p_param) const = 0; + virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) = 0; + virtual Variant body_get_param(RID p_body, BodyParameter p_param) const = 0; + + virtual void body_reset_mass_properties(RID p_body) = 0; //state enum BodyState { @@ -469,6 +473,10 @@ public: virtual void body_set_omit_force_integration(RID p_body, bool p_omit) = 0; virtual bool body_is_omitting_force_integration(RID p_body) const = 0; + // Callback for C++ use only. + typedef void (*BodyStateCallback)(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) = 0; + virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) = 0; virtual void body_set_ray_pickable(RID p_body, bool p_enable) = 0; @@ -497,7 +505,7 @@ public: } }; - virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) = 0; + virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) = 0; /* SOFT BODY */ diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 8e86957d9b..41c8b45113 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -72,6 +72,7 @@ #include "servers/rendering/shader_types.h" #include "text_server.h" #include "xr/xr_interface.h" +#include "xr/xr_interface_extension.h" #include "xr/xr_positional_tracker.h" #include "xr_server.h" @@ -138,6 +139,7 @@ void register_server_types() { GDREGISTER_VIRTUAL_CLASS(RenderingDevice); GDREGISTER_VIRTUAL_CLASS(XRInterface); + GDREGISTER_CLASS(XRInterfaceExtension); // can't register this as virtual because we need a creation function for our extensions. GDREGISTER_CLASS(XRPositionalTracker); GDREGISTER_CLASS(AudioStream); diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index fd7d5b91fa..efa3a457d3 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -806,6 +806,40 @@ void RendererCanvasCull::canvas_item_add_texture_rect(RID p_item, const Rect2 &p rect->texture = p_texture; } +void RendererCanvasCull::canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, int p_outline_size, float p_px_range) { + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandRect *rect = canvas_item->alloc_command<Item::CommandRect>(); + ERR_FAIL_COND(!rect); + rect->modulate = p_modulate; + rect->rect = p_rect; + + rect->texture = p_texture; + + rect->source = p_src_rect; + rect->flags = RendererCanvasRender::CANVAS_RECT_REGION | RendererCanvasRender::CANVAS_RECT_MSDF; + + if (p_rect.size.x < 0) { + rect->flags |= RendererCanvasRender::CANVAS_RECT_FLIP_H; + rect->rect.size.x = -rect->rect.size.x; + } + if (p_src_rect.size.x < 0) { + rect->flags ^= RendererCanvasRender::CANVAS_RECT_FLIP_H; + rect->source.size.x = -rect->source.size.x; + } + if (p_rect.size.y < 0) { + rect->flags |= RendererCanvasRender::CANVAS_RECT_FLIP_V; + rect->rect.size.y = -rect->rect.size.y; + } + if (p_src_rect.size.y < 0) { + rect->flags ^= RendererCanvasRender::CANVAS_RECT_FLIP_V; + rect->source.size.y = -rect->source.size.y; + } + rect->outline = p_outline_size; + rect->px_range = p_px_range; +} + void RendererCanvasCull::canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) { Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 79b5450d14..5e343dcf02 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -222,6 +222,7 @@ public: void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color); void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false); + void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0); void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, RS::NinePatchAxisMode p_x_axis_mode = RS::NINE_PATCH_STRETCH, RS::NinePatchAxisMode p_y_axis_mode = RS::NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1)); void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0); void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID()); diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index 8afe9ef410..04ddae4089 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -45,6 +45,7 @@ public: CANVAS_RECT_TRANSPOSE = 16, CANVAS_RECT_CLIP_UV = 32, CANVAS_RECT_IS_GROUP = 64, + CANVAS_RECT_MSDF = 128, }; struct Light { @@ -193,11 +194,15 @@ public: Color modulate; Rect2 source; uint8_t flags; + float outline; + float px_range; RID texture; CommandRect() { flags = 0; + outline = 0; + px_range = 1; type = TYPE_RECT; } }; diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index 6206849b66..1971c3e781 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -41,7 +41,8 @@ class RendererSceneRender; struct BlitToScreen { RID render_target; - Rect2i rect; + Rect2 src_rect = Rect2(0.0, 0.0, 1.0, 1.0); + Rect2i dst_rect; struct { bool use_layer = false; diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 80d843227b..236eb5e596 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -812,6 +812,7 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone tonemap.push_constant.exposure = p_settings.exposure; tonemap.push_constant.white = p_settings.white; tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey; + tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier; tonemap.push_constant.use_color_correction = p_settings.use_color_correction; @@ -864,6 +865,7 @@ void EffectsRD::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_colo tonemap.push_constant.use_color_correction = p_settings.use_color_correction; tonemap.push_constant.use_debanding = p_settings.use_debanding; + tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier; RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass())); RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_for_input(p_source_color), 0); diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index c8d4cb7ad4..0db0919dbc 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -255,7 +255,7 @@ private: float exposure; // 4 - 84 float white; // 4 - 88 float auto_exposure_grey; // 4 - 92 - uint32_t pad2; // 4 - 96 + float luminance_multiplier; // 4 - 96 float pixel_size[2]; // 8 - 104 uint32_t use_fxaa; // 4 - 108 @@ -308,7 +308,7 @@ private: float exposure_adjust; float min_luminance; float max_luminance; - float pad[1]; + uint32_t pad1; }; struct LuminanceReduceFragment { @@ -818,6 +818,7 @@ public: bool use_auto_exposure = false; float auto_exposure_grey = 0.5; RID exposure_texture; + float luminance_multiplier = 1.0; bool use_bcs = false; float brightness = 1.0; 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 611f7c6494..1b730567d9 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -484,7 +484,7 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p if (material_uniform_set != prev_material_uniform_set) { // Update uniform set. - if (RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. + if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } 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 2064d9c5c5..276a44bc27 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -86,12 +86,13 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::clear() { void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { clear(); - bool is_half_resolution = false; // Set this once we support this feature. - msaa = p_msaa; + Size2i target_size = RD::get_singleton()->texture_size(p_target_buffer); + width = p_width; height = p_height; + bool is_scaled = (target_size.width != p_width) || (target_size.height != p_height); view_count = p_view_count; color = p_color_buffer; @@ -124,7 +125,7 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b passes.push_back(pass); color_fbs[FB_CONFIG_THREE_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); - if (!is_half_resolution) { + if (!is_scaled) { // - add blit to 2D pass fb.push_back(p_target_buffer); // 2 - target buffer @@ -211,7 +212,7 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b color_fbs[FB_CONFIG_ONE_PASS] = RD::get_singleton()->framebuffer_create_multipass(fb, one_pass_with_resolve, RenderingDevice::INVALID_ID, view_count); } - if (!is_half_resolution) { + if (!is_scaled) { // - add blit to 2D pass fb.push_back(p_target_buffer); // 3 - target buffer RD::FramebufferPass blit_pass; @@ -271,6 +272,12 @@ bool RenderForwardMobile::free(RID p_rid) { /* Render functions */ +float RenderForwardMobile::_render_buffers_get_luminance_multiplier() { + // On mobile renderer we need to multiply source colors by 2 due to using a UNORM buffer + // and multiplying by the output color during 3D rendering by 0.5 + return 2.0; +} + RD::DataFormat RenderForwardMobile::_render_buffers_get_color_format() { // Using 32bit buffers enables AFBC on mobile devices which should have a definite performance improvement (MALI G710 and newer support this on 64bit RTs) return RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32; @@ -491,7 +498,6 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color bool using_subpass_transparent = true; bool using_subpass_post_process = true; - bool is_half_resolution = false; // Set this once we support this feature. bool using_ssr = false; // I don't think we support this in our mobile renderer so probably should phase it out bool using_sss = false; // I don't think we support this in our mobile renderer so probably should phase it out @@ -512,7 +518,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color screen_size.x = render_buffer->width; screen_size.y = render_buffer->height; - if (is_half_resolution) { + if (render_buffer->color_fbs[FB_CONFIG_FOUR_SUBPASSES].is_null()) { // can't do blit subpass using_subpass_post_process = false; } else if (env && (env->glow_enabled || env->auto_exposure || camera_effects_uses_dof(p_render_data->camera_effects))) { @@ -631,7 +637,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RID sky_rid = env->sky; if (sky_rid.is_valid()) { - sky.update(env, projection, p_render_data->cam_transform, time); + sky.update(env, projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid); } else { // do not try to draw sky if invalid @@ -750,9 +756,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color CameraMatrix correction; correction.set_depth_correction(true); CameraMatrix projection = correction * p_render_data->cam_projection; - sky.draw(draw_list, env, framebuffer, 1, &projection, p_render_data->cam_transform, time); + sky.draw(draw_list, env, framebuffer, 1, &projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); } else { - sky.draw(draw_list, env, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + sky.draw(draw_list, env, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); } RD::get_singleton()->draw_command_end_label(); // Draw Sky Subpass @@ -1976,7 +1982,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr if (material_uniform_set != prev_material_uniform_set) { // Update uniform set. - if (RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. + if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } 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 764d8e80df..38f80c5347 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -202,6 +202,7 @@ protected: } }; + virtual float _render_buffers_get_luminance_multiplier() override; virtual RD::DataFormat _render_buffers_get_color_format() override; virtual bool _render_buffers_can_be_storage() override; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 04ad1a2724..14b3b6d9aa 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -665,6 +665,8 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p actions.global_buffer_array_variable = "global_variables.data"; actions.instance_uniform_index_variable = "draw_call.instance_uniforms_ofs"; + actions.apply_luminance_multiplier = true; // apply luminance multiplier to screen texture + compiler.initialize(actions); } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 3af5047854..647c348d9f 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -541,6 +541,14 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend src_rect = Rect2(0, 0, 1, 1); } + if (rect->flags & CANVAS_RECT_MSDF) { + push_constant.flags |= FLAGS_USE_MSDF; + push_constant.msdf[0] = rect->px_range; // Pixel range. + push_constant.msdf[1] = rect->outline; // Outline size. + push_constant.msdf[2] = 0.f; // Reserved. + push_constant.msdf[3] = 0.f; // Reserved. + } + push_constant.modulation[0] = rect->modulate.r * base_color.r; push_constant.modulation[1] = rect->modulate.g * base_color.g; push_constant.modulation[2] = rect->modulate.b * base_color.b; @@ -1094,7 +1102,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { pipeline_variants = &material_data->shader_data->pipeline_variants; // Update uniform set. - if (RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. + if (material_data->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET); } } else { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 7c4f62832c..ec7d7e2854 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -84,8 +84,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender { FLAGS_LIGHT_COUNT_SHIFT = 20, FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26), - FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27) + FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27), + FLAGS_USE_MSDF = (1 << 28), }; enum { @@ -388,7 +389,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender { //rect struct { float modulation[4]; - float ninepatch_margins[4]; + union { + float msdf[4]; + float ninepatch_margins[4]; + }; float dst_rect[4]; float src_rect[4]; float pad[2]; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 62e9386f95..c53c202bab 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -46,6 +46,8 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID RID rd_texture = storage->texture_get_rd_texture(texture); ERR_CONTINUE(rd_texture.is_null()); + // TODO if keep_3d_linear was set when rendering to this render target we need to add a linear->sRGB conversion in. + if (!render_target_descriptors.has(rd_texture) || !RD::get_singleton()->uniform_set_is_valid(render_target_descriptors[rd_texture])) { Vector<RD::Uniform> uniforms; RD::Uniform u; @@ -65,10 +67,14 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0); - blit.push_constant.rect[0] = p_render_targets[i].rect.position.x / screen_size.width; - blit.push_constant.rect[1] = p_render_targets[i].rect.position.y / screen_size.height; - blit.push_constant.rect[2] = p_render_targets[i].rect.size.width / screen_size.width; - blit.push_constant.rect[3] = p_render_targets[i].rect.size.height / screen_size.height; + blit.push_constant.src_rect[0] = p_render_targets[i].src_rect.position.x; + blit.push_constant.src_rect[1] = p_render_targets[i].src_rect.position.y; + blit.push_constant.src_rect[2] = p_render_targets[i].src_rect.size.width; + blit.push_constant.src_rect[3] = p_render_targets[i].src_rect.size.height; + blit.push_constant.dst_rect[0] = p_render_targets[i].dst_rect.position.x / screen_size.width; + blit.push_constant.dst_rect[1] = p_render_targets[i].dst_rect.position.y / screen_size.height; + blit.push_constant.dst_rect[2] = p_render_targets[i].dst_rect.size.width / screen_size.width; + blit.push_constant.dst_rect[3] = p_render_targets[i].dst_rect.size.height / screen_size.height; blit.push_constant.layer = p_render_targets[i].multi_view.layer; blit.push_constant.eye_center[0] = p_render_targets[i].lens_distortion.eye_center.x; blit.push_constant.eye_center[1] = p_render_targets[i].lens_distortion.eye_center.y; @@ -203,10 +209,14 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uset, 0); - blit.push_constant.rect[0] = screenrect.position.x; - blit.push_constant.rect[1] = screenrect.position.y; - blit.push_constant.rect[2] = screenrect.size.width; - blit.push_constant.rect[3] = screenrect.size.height; + blit.push_constant.src_rect[0] = 0.0; + blit.push_constant.src_rect[1] = 0.0; + blit.push_constant.src_rect[2] = 1.0; + blit.push_constant.src_rect[3] = 1.0; + blit.push_constant.dst_rect[0] = screenrect.position.x; + blit.push_constant.dst_rect[1] = screenrect.position.y; + blit.push_constant.dst_rect[2] = screenrect.size.width; + blit.push_constant.dst_rect[3] = screenrect.size.height; blit.push_constant.layer = 0; blit.push_constant.eye_center[0] = 0; blit.push_constant.eye_center[1] = 0; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 8639362da9..0230c46800 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -55,7 +55,8 @@ protected: }; struct BlitPushConstant { - float rect[4]; + float src_rect[4]; + float dst_rect[4]; float eye_center[2]; float k1; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 8496ef631b..fa66ed85a9 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -2237,6 +2237,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende } } + tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier(); tonemap.view_count = p_render_data->view_count; storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap); @@ -2301,6 +2302,7 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr tonemap.use_debanding = rb->use_debanding; tonemap.texture_size = Vector2i(rb->width, rb->height); + tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier(); tonemap.view_count = p_render_data->view_count; storage->get_effects()->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap); @@ -2573,6 +2575,10 @@ float RendererSceneRenderRD::render_buffers_get_volumetric_fog_detail_spread(RID return rb->volumetric_fog->spread; } +float RendererSceneRenderRD::_render_buffers_get_luminance_multiplier() { + return 1.0; +} + RD::DataFormat RendererSceneRenderRD::_render_buffers_get_color_format() { return RD::DATA_FORMAT_R16G16B16A16_SFLOAT; } @@ -2585,6 +2591,8 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p ERR_FAIL_COND_MSG(p_view_count == 0, "Must have at least 1 view"); RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + + // Should we add an overrule per viewport? rb->width = p_width; rb->height = p_height; rb->render_target = p_render_target; @@ -2631,8 +2639,8 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p tf.format = RD::DATA_FORMAT_R32_SFLOAT; } - tf.width = p_width; - tf.height = p_height; + tf.width = rb->width; + tf.height = rb->height; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; tf.array_layers = rb->view_count; // create a layer for every view @@ -2654,10 +2662,10 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p } RID target_texture = storage->render_target_get_rd_texture(rb->render_target); - rb->data->configure(rb->texture, rb->depth_texture, target_texture, p_width, p_height, p_msaa, p_view_count); + rb->data->configure(rb->texture, rb->depth_texture, target_texture, rb->width, rb->height, p_msaa, p_view_count); if (is_clustered_enabled()) { - rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture); + rb->cluster_builder->setup(Size2i(rb->width, rb->height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture); } } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 37533baecf..eb61af517a 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -1190,6 +1190,7 @@ public: /* render buffers */ + virtual float _render_buffers_get_luminance_multiplier(); virtual RD::DataFormat _render_buffers_get_color_format(); virtual bool _render_buffers_can_be_storage(); virtual RID render_buffers_create() override; diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index 4165c3f7ba..c388da755c 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -259,7 +259,7 @@ static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_ar p_array[11] = 0; } -void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { +void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier) { SkyPushConstant sky_push_constant; memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); @@ -276,6 +276,7 @@ void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_ sky_push_constant.position[2] = p_position.z; sky_push_constant.multiplier = p_multiplier; sky_push_constant.time = p_time; + sky_push_constant.luminance_multiplier = p_luminance_multiplier; store_transform_3x3(p_orientation, sky_push_constant.orientation); RenderingDevice::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_fb); @@ -287,7 +288,7 @@ void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_ // Update uniform sets. { RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0); - if (RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set. + if (p_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); } RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); @@ -1195,7 +1196,7 @@ void RendererSceneSkyRD::setup(RendererSceneEnvironmentRD *p_env, RID p_render_b RD::get_singleton()->buffer_update(sky_scene_state.uniform_buffer, 0, sizeof(SkySceneState::UBO), &sky_scene_state.ubo); } -void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time) { +void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) { ERR_FAIL_COND(!p_env); Sky *sky = get_sky(p_env->sky); @@ -1287,7 +1288,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1306,7 +1307,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1321,7 +1322,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1439,7 +1440,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); RD::get_singleton()->draw_list_end(); } @@ -1452,7 +1453,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); RD::get_singleton()->draw_list_end(); } @@ -1466,11 +1467,11 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont } RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); - _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); RD::get_singleton()->draw_list_end(); } -void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) { +void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) { ERR_FAIL_COND(!p_env); ERR_FAIL_COND(p_view_count == 0); @@ -1546,7 +1547,7 @@ void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, u clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1559,12 +1560,12 @@ void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, u clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } } -void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) { +void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) { ERR_FAIL_COND(!p_env); ERR_FAIL_COND(p_view_count == 0); @@ -1640,7 +1641,7 @@ void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironme texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set; } - _render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier); } void RendererSceneSkyRD::invalidate_sky(Sky *p_sky) { diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index 7b670bddd5..7f563c9bc4 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -100,7 +100,8 @@ private: float position[3]; // 12 - 92 float multiplier; // 4 - 96 float time; // 4 - 100 - float pad[3]; // 12 - 112 // Using pad to align on 16 bytes + float luminance_multiplier; // 4 - 104 + float pad[2]; // 8 - 112 // Using pad to align on 16 bytes // 128 is the max size of a push constant. We can replace "pad" but we can't add any more. }; @@ -138,7 +139,7 @@ private: virtual ~SkyShaderData(); }; - void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); + void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier); public: struct SkySceneState { @@ -293,10 +294,10 @@ public: ~RendererSceneSkyRD(); void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); - void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); - void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); - void update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); - void draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); + void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); + void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); // only called by clustered renderer + void update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); + void draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); void invalidate_sky(Sky *p_sky); void update_dirty_skys(); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 14a5a01054..ec0d25376f 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -4848,7 +4848,7 @@ void RendererStorageRD::_particles_process(Particles *p_particles, double p_delt RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, 1); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->collision_textures_uniform_set, 2); - if (m->uniform_set.is_valid()) { + if (m->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(m->uniform_set)) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 3); } diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp index bad37f5c25..b95d4b642c 100644 --- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp @@ -1165,6 +1165,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge SL::VariableNode *vnode = (SL::VariableNode *)onode->arguments[0]; bool is_texture_func = false; + bool is_screen_texture = false; if (onode->op == SL::OP_STRUCT) { code += _mkid(vnode->name); } else if (onode->op == SL::OP_CONSTRUCT) { @@ -1197,6 +1198,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge const SL::VariableNode *varnode = static_cast<const SL::VariableNode *>(onode->arguments[i]); StringName texture_uniform = varnode->name; + is_screen_texture = (texture_uniform == "SCREEN_TEXTURE"); String sampler_name; @@ -1236,6 +1238,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge } } code += ")"; + if (is_screen_texture && actions.apply_luminance_multiplier) { + code = "(" + code + " * vec4(vec3(sc_luminance_multiplier), 1.0))"; + } } break; case SL::OP_INDEX: { code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.h b/servers/rendering/renderer_rd/shader_compiler_rd.h index 2da127ffa3..0fe9047967 100644 --- a/servers/rendering/renderer_rd/shader_compiler_rd.h +++ b/servers/rendering/renderer_rd/shader_compiler_rd.h @@ -95,6 +95,7 @@ public: String global_buffer_array_variable; String instance_uniform_index_variable; uint32_t base_varying_index = 0; + bool apply_luminance_multiplier = false; }; private: diff --git a/servers/rendering/renderer_rd/shaders/blit.glsl b/servers/rendering/renderer_rd/shaders/blit.glsl index 967da1e6e4..8051f96738 100644 --- a/servers/rendering/renderer_rd/shaders/blit.glsl +++ b/servers/rendering/renderer_rd/shaders/blit.glsl @@ -5,6 +5,7 @@ #VERSION_DEFINES layout(push_constant, binding = 0, std140) uniform Pos { + vec4 src_rect; vec4 dst_rect; vec2 eye_center; @@ -22,8 +23,8 @@ layout(location = 0) out vec2 uv; void main() { vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv = base_arr[gl_VertexIndex]; - vec2 vtx = data.dst_rect.xy + uv * data.dst_rect.zw; + uv = data.src_rect.xy + base_arr[gl_VertexIndex] * data.src_rect.zw; + vec2 vtx = data.dst_rect.xy + base_arr[gl_VertexIndex] * data.dst_rect.zw; gl_Position = vec4(vtx * 2.0 - 1.0, 0.0, 1.0); } @@ -34,6 +35,7 @@ void main() { #VERSION_DEFINES layout(push_constant, binding = 0, std140) uniform Pos { + vec4 src_rect; vec4 dst_rect; vec2 eye_center; diff --git a/servers/rendering/renderer_rd/shaders/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/blur_raster.glsl index 0789a4b396..f8b4e3f610 100644 --- a/servers/rendering/renderer_rd/shaders/blur_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/blur_raster.glsl @@ -38,6 +38,8 @@ layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure; layout(location = 0) out vec4 frag_color; void main() { + // We do not apply our color scale for our mobile renderer here, we'll leave our colors at half brightness and apply scale in the tonemap raster. + #ifdef MODE_MIPMAP vec2 pix_size = blur.pixel_size; diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index a443bcdcb8..2911e8b731 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -458,6 +458,14 @@ void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { #endif +float msdf_median(float r, float g, float b, float a) { + return min(max(min(r, g), min(max(r, g), b)), a); +} + +vec2 msdf_map(vec2 value, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max) { + return out_min + (out_max - out_min) * (value - in_min) / (in_max - in_min); +} + void main() { vec4 color = color_interp; vec2 uv = uv_interp; @@ -485,7 +493,34 @@ void main() { #endif - color *= texture(sampler2D(color_texture, texture_sampler), uv); +#ifndef USE_PRIMITIVE + if (bool(draw_data.flags & FLAGS_USE_MSDF)) { + float px_range = draw_data.ninepatch_margins.x; + float outline_thickness = draw_data.ninepatch_margins.y; + //float reserved1 = draw_data.ninepatch_margins.z; + //float reserved2 = draw_data.ninepatch_margins.w; + + vec4 msdf_sample = texture(sampler2D(color_texture, texture_sampler), uv); + vec2 msdf_size = vec2(textureSize(sampler2D(color_texture, texture_sampler), 0)); + vec2 dest_size = vec2(1.0) / fwidth(uv); + float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0); + float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5; + + if (outline_thickness > 0) { + float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range; + float a = clamp((d + cr) * px_size, 0.0, 1.0); + color.a = a * color.a; + } else { + float a = clamp(d * px_size + 0.5, 0.0, 1.0); + color.a = a * color.a; + } + + } else { +#else + { +#endif + color *= texture(sampler2D(color_texture, texture_sampler), uv); + } uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights bool using_light = light_count > 0 || canvas_data.directional_light_count > 0; diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl index 451f9b0089..0cff505cae 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl @@ -24,6 +24,8 @@ #define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26) #define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27) +#define FLAGS_USE_MSDF (1 << 28) + #define SAMPLER_NEAREST_CLAMP 0 #define SAMPLER_LINEAR_CLAMP 1 #define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2 diff --git a/servers/rendering/renderer_rd/shaders/giprobe_write.glsl b/servers/rendering/renderer_rd/shaders/giprobe_write.glsl index 5dc2d08a3b..25d87ca45d 100644 --- a/servers/rendering/renderer_rd/shaders/giprobe_write.glsl +++ b/servers/rendering/renderer_rd/shaders/giprobe_write.glsl @@ -202,12 +202,7 @@ void main() { vec3 emission = vec3(ivec3(cell_data.data[cell_index].emission & 0x3FF, (cell_data.data[cell_index].emission >> 10) & 0x7FF, cell_data.data[cell_index].emission >> 21)) * params.emission_scale; vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal); -#ifdef MODE_ANISOTROPIC - vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); - const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0)); -#else vec3 accum = vec3(0.0); -#endif for (uint i = 0; i < params.light_count; i++) { float attenuation; @@ -242,77 +237,35 @@ void main() { vec3 light = lights.data[i].color * albedo.rgb * attenuation * lights.data[i].energy; -#ifdef MODE_ANISOTROPIC - for (uint j = 0; j < 6; j++) { - accum[j] += max(0.0, dot(accum_dir, -light_dir)) * light + emission; - } -#else if (length(normal.xyz) > 0.2) { accum += max(0.0, dot(normal.xyz, -light_dir)) * light + emission; } else { //all directions accum += light + emission; } -#endif } -#ifdef MODE_ANISOTROPIC - - output.data[cell_index * 6 + 0] = vec4(accum[0], 0.0); - output.data[cell_index * 6 + 1] = vec4(accum[1], 0.0); - output.data[cell_index * 6 + 2] = vec4(accum[2], 0.0); - output.data[cell_index * 6 + 3] = vec4(accum[3], 0.0); - output.data[cell_index * 6 + 4] = vec4(accum[4], 0.0); - output.data[cell_index * 6 + 5] = vec4(accum[5], 0.0); -#else output.data[cell_index] = vec4(accum, 0.0); -#endif - #endif //MODE_COMPUTE_LIGHT #ifdef MODE_UPDATE_MIPMAPS { -#ifdef MODE_ANISOTROPIC - vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); -#else vec3 light_accum = vec3(0.0); -#endif float count = 0.0; for (uint i = 0; i < 8; i++) { uint child_index = cell_children.data[cell_index].children[i]; if (child_index == NO_CHILDREN) { continue; } -#ifdef MODE_ANISOTROPIC - light_accum[1] += output.data[child_index * 6 + 0].rgb; - light_accum[2] += output.data[child_index * 6 + 1].rgb; - light_accum[3] += output.data[child_index * 6 + 2].rgb; - light_accum[4] += output.data[child_index * 6 + 3].rgb; - light_accum[5] += output.data[child_index * 6 + 4].rgb; - light_accum[6] += output.data[child_index * 6 + 5].rgb; - -#else light_accum += output.data[child_index].rgb; -#endif - count += 1.0; } float divisor = mix(8.0, count, params.propagation); -#ifdef MODE_ANISOTROPIC - output.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0); - output.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0); - output.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0); - output.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0); - output.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0); - output.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0); - -#else output.data[cell_index] = vec4(light_accum / divisor, 0.0); -#endif } #endif diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl index ed389ffe56..3cde9923fa 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl @@ -6,6 +6,6 @@ layout(push_constant, binding = 1, std430) uniform PushConstant { float exposure_adjust; float min_luminance; float max_luminance; - float pad; + uint pad1; } settings; diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl index 7a11f8904e..edbe1031b7 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl @@ -374,6 +374,9 @@ layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4; layout(constant_id = 10) const bool sc_decal_use_mipmaps = true; layout(constant_id = 11) const bool sc_projector_use_mipmaps = true; +// not used in clustered renderer but we share some code with the mobile renderer that requires this. +const float sc_luminance_multiplier = 1.0; + #include "scene_forward_clustered_inc.glsl" /* Varyings */ 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 9fa5d3280d..f3db4abe3b 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -969,7 +969,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 normal, float roughnes vec4 reflection; - reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb; + reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; if (reflections.data[ref_index].exterior) { reflection.rgb = mix(specular_light, reflection.rgb, blend); diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl index 7e2cc8fe01..518b0a6c7f 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl @@ -401,6 +401,8 @@ layout(constant_id = 14) const bool sc_disable_fog = false; #endif //!MODE_RENDER_DEPTH +layout(constant_id = 15) const float sc_luminance_multiplier = 2.0; + /* Include our forward mobile UBOs definitions etc. */ #include "scene_forward_mobile_inc.glsl" @@ -1551,12 +1553,15 @@ void main() { frag_color = vec4(albedo, alpha); #else // MODE_UNSHADED frag_color = vec4(emission + ambient_light + diffuse_light + specular_light, alpha); - //frag_color = vec4(1.0); #endif // MODE_UNSHADED // Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky. frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); + // On mobile we use a UNORM buffer with 10bpp which results in a range from 0.0 - 1.0 resulting in HDR breaking + // We divide by sc_luminance_multiplier to support a range from 0.0 - 2.0 both increasing precision on bright and darker images + frag_color.rgb = frag_color.rgb / sc_luminance_multiplier; + #endif //MODE_MULTIPLE_RENDER_TARGETS #endif //MODE_RENDER_DEPTH diff --git a/servers/rendering/renderer_rd/shaders/sky.glsl b/servers/rendering/renderer_rd/shaders/sky.glsl index 41c6325bc5..d07a454ade 100644 --- a/servers/rendering/renderer_rd/shaders/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/sky.glsl @@ -17,6 +17,8 @@ layout(push_constant, binding = 1, std430) uniform Params { vec4 projections[MAX_VIEWS]; vec4 position_multiplier; float time; + float luminance_multiplier; + float pad[2]; } params; @@ -55,6 +57,8 @@ layout(push_constant, binding = 1, std430) uniform Params { vec4 projections[MAX_VIEWS]; vec4 position_multiplier; float time; + float luminance_multiplier; + float pad[2]; } params; @@ -199,17 +203,17 @@ void main() { vec3 inverted_cube_normal = cube_normal; inverted_cube_normal.z *= -1.0; #ifdef USES_HALF_RES_COLOR - half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal); + half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal) * params.luminance_multiplier; #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal); + quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal) * params.luminance_multiplier; #endif #else #ifdef USES_HALF_RES_COLOR - half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0); + half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * params.luminance_multiplier; #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0); + quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * params.luminance_multiplier; #endif #endif @@ -246,4 +250,7 @@ void main() { if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) { frag_color.a = 0.0; } + + // For mobile renderer we're dividing by 2.0 as we're using a UNORM buffer + frag_color.rgb = frag_color.rgb / params.luminance_multiplier; } diff --git a/servers/rendering/renderer_rd/shaders/tonemap.glsl b/servers/rendering/renderer_rd/shaders/tonemap.glsl index f997101183..4411587116 100644 --- a/servers/rendering/renderer_rd/shaders/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/tonemap.glsl @@ -71,7 +71,7 @@ layout(push_constant, binding = 1, std430) uniform Params { float exposure; float white; float auto_exposure_grey; - uint pad2; + float luminance_multiplier; vec2 pixel_size; bool use_fxaa; @@ -298,15 +298,15 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { const float FXAA_SPAN_MAX = 8.0; #ifdef MULTIVIEW - vec3 rgbNW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; - vec3 rgbNE = textureLod(source_color, vec3(uv_interp + vec2(1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; - vec3 rgbSW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; - vec3 rgbSE = textureLod(source_color, vec3(uv_interp + vec2(1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; + vec3 rgbNW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier; + vec3 rgbNE = textureLod(source_color, vec3(uv_interp + vec2(1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier; + vec3 rgbSW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier; + vec3 rgbSE = textureLod(source_color, vec3(uv_interp + vec2(1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier; #else - vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure; - vec3 rgbNE = textureLod(source_color, uv_interp + vec2(1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure; - vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure; - vec3 rgbSE = textureLod(source_color, uv_interp + vec2(1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure; + vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier; + vec3 rgbNE = textureLod(source_color, uv_interp + vec2(1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier; + vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier; + vec3 rgbSE = textureLod(source_color, uv_interp + vec2(1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier; #endif vec3 rgbM = color; vec3 luma = vec3(0.299, 0.587, 0.114); @@ -333,11 +333,11 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { params.pixel_size; #ifdef MULTIVIEW - vec3 rgbA = 0.5 * exposure * (textureLod(source_color, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); + vec3 rgbA = 0.5 * exposure * (textureLod(source_color, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz) * params.luminance_multiplier; + vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz) * params.luminance_multiplier; #else - vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz); + vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz) * params.luminance_multiplier; + vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz) * params.luminance_multiplier; #endif float lumaB = dot(rgbB, luma); @@ -364,11 +364,11 @@ vec3 screen_space_dither(vec2 frag_coord) { void main() { #ifdef SUBPASS // SUBPASS and MULTIVIEW can be combined but in that case we're already reading from the correct layer - vec3 color = subpassLoad(input_color).rgb; + vec3 color = subpassLoad(input_color).rgb * params.luminance_multiplier; #elif defined(MULTIVIEW) - vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb; + vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb * params.luminance_multiplier; #else - vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb; + vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb * params.luminance_multiplier; #endif // Exposure @@ -377,7 +377,7 @@ void main() { #ifndef SUBPASS if (params.use_auto_exposure) { - exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.auto_exposure_grey); + exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_grey); } #endif @@ -386,7 +386,7 @@ void main() { // Early Tonemap & SRGB Conversion #ifndef SUBPASS if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) { - vec3 glow = gather_glow(source_glow, uv_interp); + vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier; color.rgb = mix(color.rgb, glow, params.glow_intensity); } @@ -411,7 +411,7 @@ void main() { // Glow if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) { - vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity; + vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier; // high dynamic range -> SRGB glow = apply_tonemapping(glow, params.white); diff --git a/servers/rendering/renderer_rd/shaders/voxel_gi.glsl b/servers/rendering/renderer_rd/shaders/voxel_gi.glsl index 49a493cdc7..779f04ed35 100644 --- a/servers/rendering/renderer_rd/shaders/voxel_gi.glsl +++ b/servers/rendering/renderer_rd/shaders/voxel_gi.glsl @@ -71,11 +71,6 @@ lights; layout(set = 0, binding = 5) uniform texture3D color_texture; -#ifdef MODE_ANISOTROPIC -layout(set = 0, binding = 7) uniform texture3D aniso_pos_texture; -layout(set = 0, binding = 8) uniform texture3D aniso_neg_texture; -#endif // MODE ANISOTROPIC - #endif // MODE_SECOND_BOUNCE #ifndef MODE_DYNAMIC @@ -110,13 +105,6 @@ layout(set = 0, binding = 10) uniform sampler texture_sampler; layout(rgba8, set = 0, binding = 5) uniform restrict writeonly image3D color_tex; -#ifdef MODE_ANISOTROPIC - -layout(r16ui, set = 0, binding = 6) uniform restrict writeonly uimage3D aniso_pos_tex; -layout(r16ui, set = 0, binding = 7) uniform restrict writeonly uimage3D aniso_neg_tex; - -#endif - #endif #ifdef MODE_DYNAMIC @@ -170,13 +158,6 @@ layout(r32f, set = 0, binding = 8) uniform restrict writeonly image2D depth; layout(rgba8, set = 0, binding = 11) uniform restrict image3D color_texture; -#ifdef MODE_ANISOTROPIC - -layout(r16ui, set = 0, binding = 12) uniform restrict writeonly uimage3D aniso_pos_texture; -layout(r16ui, set = 0, binding = 13) uniform restrict writeonly uimage3D aniso_neg_texture; - -#endif // MODE ANISOTROPIC - #endif //MODE_DYNAMIC_SHRINK_PLOT #endif // MODE_DYNAMIC_SHRINK @@ -374,12 +355,7 @@ void main() { vec3 emission = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0); vec3 normal = unpackSnorm4x8(cell_data.data[cell_index].normal).xyz; -#ifdef MODE_ANISOTROPIC - vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); - const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0)); -#else vec3 accum = vec3(0.0); -#endif for (uint i = 0; i < params.light_count; i++) { vec3 light; @@ -390,38 +366,16 @@ void main() { light *= albedo.rgb; -#ifdef MODE_ANISOTROPIC - for (uint j = 0; j < 6; j++) { - accum[j] += max(0.0, dot(accum_dirs[j], -light_dir)) * light; - } -#else if (length(normal) > 0.2) { accum += max(0.0, dot(normal, -light_dir)) * light; } else { //all directions accum += light; } -#endif } -#ifdef MODE_ANISOTROPIC - - for (uint i = 0; i < 6; i++) { - vec3 light = accum[i]; - if (length(normal) > 0.2) { - light += max(0.0, dot(accum_dirs[i], -normal)) * emission; - } else { - light += emission; - } - - outputs.data[cell_index * 6 + i] = vec4(light, 0.0); - } - -#else outputs.data[cell_index] = vec4(accum + emission, 0.0); -#endif - #endif //MODE_COMPUTE_LIGHT /////////////////SECOND BOUNCE/////////////////////////////// @@ -431,32 +385,8 @@ void main() { ivec3 ipos = ivec3(posu); vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal); -#ifdef MODE_ANISOTROPIC - vec3 accum[6]; - const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0)); - - /*vec3 src_color = texelFetch(sampler3D(color_texture,texture_sampler),ipos,0).rgb * params.dynamic_range; - vec3 src_aniso_pos = texelFetch(sampler3D(aniso_pos_texture,texture_sampler),ipos,0).rgb; - vec3 src_anisp_neg = texelFetch(sampler3D(anisp_neg_texture,texture_sampler),ipos,0).rgb; - accum[0]=src_col * src_aniso_pos.x; - accum[1]=src_col * src_aniso_neg.x; - accum[2]=src_col * src_aniso_pos.y; - accum[3]=src_col * src_aniso_neg.y; - accum[4]=src_col * src_aniso_pos.z; - accum[5]=src_col * src_aniso_neg.z;*/ - - accum[0] = outputs.data[cell_index * 6 + 0].rgb; - accum[1] = outputs.data[cell_index * 6 + 1].rgb; - accum[2] = outputs.data[cell_index * 6 + 2].rgb; - accum[3] = outputs.data[cell_index * 6 + 3].rgb; - accum[4] = outputs.data[cell_index * 6 + 4].rgb; - accum[5] = outputs.data[cell_index * 6 + 5].rgb; - -#else vec3 accum = outputs.data[cell_index].rgb; -#endif - if (length(normal.xyz) > 0.2) { 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.xyz)); @@ -484,9 +414,6 @@ void main() { float max_distance = length(vec3(params.limits)); vec3 cell_size = 1.0 / vec3(params.limits); -#ifdef MODE_ANISOTROPIC - vec3 aniso_normal = mix(direction, normal.xyz, params.aniso_strength); -#endif while (dist < max_distance && color.a < 0.95) { float diameter = max(1.0, 2.0 * tan_half_angle * dist); vec3 uvw_pos = (pos + dist * direction) * cell_size; @@ -498,42 +425,18 @@ void main() { float log2_diameter = log2(diameter); vec4 scolor = textureLod(sampler3D(color_texture, texture_sampler), uvw_pos, log2_diameter); -#ifdef MODE_ANISOTROPIC - - vec3 aniso_neg = textureLod(sampler3D(aniso_neg_texture, texture_sampler), uvw_pos, log2_diameter).rgb; - vec3 aniso_pos = textureLod(sampler3D(aniso_pos_texture, texture_sampler), uvw_pos, log2_diameter).rgb; - - scolor.rgb *= dot(max(vec3(0.0), (aniso_normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-aniso_normal * aniso_neg)), vec3(1.0)); -#endif float a = (1.0 - color.a); color += a * scolor; dist += half_diameter; } } color *= cone_weights[i] * vec4(albedo.rgb, 1.0) * params.dynamic_range; //restore range -#ifdef MODE_ANISOTROPIC - for (uint j = 0; j < 6; j++) { - accum[j] += max(0.0, dot(accum_dirs[j], direction)) * color.rgb; - } -#else accum += color.rgb; -#endif } } -#ifdef MODE_ANISOTROPIC - - outputs.data[cell_index * 6 + 0] = vec4(accum[0], 0.0); - outputs.data[cell_index * 6 + 1] = vec4(accum[1], 0.0); - outputs.data[cell_index * 6 + 2] = vec4(accum[2], 0.0); - outputs.data[cell_index * 6 + 3] = vec4(accum[3], 0.0); - outputs.data[cell_index * 6 + 4] = vec4(accum[4], 0.0); - outputs.data[cell_index * 6 + 5] = vec4(accum[5], 0.0); -#else outputs.data[cell_index] = vec4(accum, 0.0); -#endif - #endif // MODE_SECOND_BOUNCE /////////////////UPDATE MIPMAPS/////////////////////////////// @@ -541,45 +444,20 @@ void main() { #ifdef MODE_UPDATE_MIPMAPS { -#ifdef MODE_ANISOTROPIC - vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); -#else vec3 light_accum = vec3(0.0); -#endif float count = 0.0; for (uint i = 0; i < 8; i++) { uint child_index = cell_children.data[cell_index].children[i]; if (child_index == NO_CHILDREN) { continue; } -#ifdef MODE_ANISOTROPIC - light_accum[0] += outputs.data[child_index * 6 + 0].rgb; - light_accum[1] += outputs.data[child_index * 6 + 1].rgb; - light_accum[2] += outputs.data[child_index * 6 + 2].rgb; - light_accum[3] += outputs.data[child_index * 6 + 3].rgb; - light_accum[4] += outputs.data[child_index * 6 + 4].rgb; - light_accum[5] += outputs.data[child_index * 6 + 5].rgb; - -#else light_accum += outputs.data[child_index].rgb; -#endif - count += 1.0; } float divisor = mix(8.0, count, params.propagation); -#ifdef MODE_ANISOTROPIC - outputs.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0); - outputs.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0); - outputs.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0); - outputs.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0); - outputs.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0); - outputs.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0); - -#else outputs.data[cell_index] = vec4(light_accum / divisor, 0.0); -#endif } #endif @@ -587,40 +465,7 @@ void main() { #ifdef MODE_WRITE_TEXTURE { -#ifdef MODE_ANISOTROPIC - vec3 accum_total = vec3(0.0); - accum_total += outputs.data[cell_index * 6 + 0].rgb; - accum_total += outputs.data[cell_index * 6 + 1].rgb; - accum_total += outputs.data[cell_index * 6 + 2].rgb; - accum_total += outputs.data[cell_index * 6 + 3].rgb; - accum_total += outputs.data[cell_index * 6 + 4].rgb; - accum_total += outputs.data[cell_index * 6 + 5].rgb; - - float accum_total_energy = max(dot(accum_total, GREY_VEC), 0.00001); - vec3 iso_positive = vec3(dot(outputs.data[cell_index * 6 + 0].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 2].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 4].rgb, GREY_VEC)) / vec3(accum_total_energy); - vec3 iso_negative = vec3(dot(outputs.data[cell_index * 6 + 1].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 3].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 5].rgb, GREY_VEC)) / vec3(accum_total_energy); - - { - uint aniso_pos = uint(clamp(iso_positive.b * 31.0, 0.0, 31.0)); - aniso_pos |= uint(clamp(iso_positive.g * 63.0, 0.0, 63.0)) << 5; - aniso_pos |= uint(clamp(iso_positive.r * 31.0, 0.0, 31.0)) << 11; - imageStore(aniso_pos_tex, ivec3(posu), uvec4(aniso_pos)); - } - - { - uint aniso_neg = uint(clamp(iso_negative.b * 31.0, 0.0, 31.0)); - aniso_neg |= uint(clamp(iso_negative.g * 63.0, 0.0, 63.0)) << 5; - aniso_neg |= uint(clamp(iso_negative.r * 31.0, 0.0, 31.0)) << 11; - imageStore(aniso_neg_tex, ivec3(posu), uvec4(aniso_neg)); - } - - imageStore(color_tex, ivec3(posu), vec4(accum_total / params.dynamic_range, albedo.a)); - -#else - imageStore(color_tex, ivec3(posu), vec4(outputs.data[cell_index].rgb / params.dynamic_range, albedo.a)); - -#endif } #endif @@ -763,13 +608,6 @@ void main() { color.rgb /= params.dynamic_range; imageStore(color_texture, pos3d, color); //imageStore(color_texture,pos3d,vec4(1,1,1,1)); - -#ifdef MODE_ANISOTROPIC - //do not care about anisotropy for dynamic objects, just store full lit in all directions - imageStore(aniso_pos_texture, pos3d, uvec4(0xFFFF)); - imageStore(aniso_neg_texture, pos3d, uvec4(0xFFFF)); - -#endif // ANISOTROPIC } #endif // MODE_DYNAMIC_SHRINK_PLOT } diff --git a/servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl b/servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl index 7d4d72967a..281c496df3 100644 --- a/servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl +++ b/servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl @@ -20,11 +20,6 @@ layout(set = 0, binding = 2) uniform texture3D color_tex; layout(set = 0, binding = 3) uniform sampler tex_sampler; -#ifdef USE_ANISOTROPY -layout(set = 0, binding = 4) uniform texture3D aniso_pos_tex; -layout(set = 0, binding = 5) uniform texture3D aniso_neg_tex; -#endif - layout(push_constant, binding = 0, std430) uniform Params { mat4 projection; uint cell_offset; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 15ce1dbe63..8af2049ab3 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -71,6 +71,44 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, return xf; } +void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { + if (p_viewport->render_buffers.is_valid()) { + if (p_viewport->size.width == 0 || p_viewport->size.height == 0) { + RSG::scene->free(p_viewport->render_buffers); + p_viewport->render_buffers = RID(); + } else { + RS::ViewportScale3D scale_3d = p_viewport->scale_3d; + if (Engine::get_singleton()->is_editor_hint()) { // ignore this inside of the editor + scale_3d = RS::VIEWPORT_SCALE_3D_DISABLED; + } + + int width = p_viewport->size.width; + int height = p_viewport->size.height; + switch (scale_3d) { + case RS::VIEWPORT_SCALE_3D_75_PERCENT: { + width = (width * 3) / 4; + height = (height * 3) / 4; + }; break; + case RS::VIEWPORT_SCALE_3D_50_PERCENT: { + width = width >> 1; + height = height >> 1; + }; break; + case RS::VIEWPORT_SCALE_3D_33_PERCENT: { + width = width / 3; + height = height / 3; + }; break; + case RS::VIEWPORT_SCALE_3D_25_PERCENT: { + width = width >> 2; + height = height >> 2; + }; break; + default: + break; + } + RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, width, height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding, p_viewport->get_view_count()); + } + } +} + void RendererViewport::_draw_3d(Viewport *p_viewport) { RENDER_TIMESTAMP(">Begin Rendering 3D Scene"); @@ -100,7 +138,7 @@ void RendererViewport::_draw_3d(Viewport *p_viewport) { RENDER_TIMESTAMP("<End Rendering 3D Scene"); } -void RendererViewport::_draw_viewport(Viewport *p_viewport, uint32_t p_view_count) { +void RendererViewport::_draw_viewport(Viewport *p_viewport) { if (p_viewport->measure_render_time) { String rt_id = "vp_begin_" + itos(p_viewport->self.get_id()); RSG::storage->capture_timestamp(rt_id); @@ -142,7 +180,8 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, uint32_t p_view_coun if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) { //wants to draw 3D but there is no render buffer, create p_viewport->render_buffers = RSG::scene->render_buffers_create(); - RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding, p_view_count); + + _configure_3d_render_buffers(p_viewport); } RSG::storage->render_target_request_clear(p_viewport->render_target, bgcolor); @@ -544,7 +583,7 @@ void RendererViewport::draw_viewports() { RSG::storage->render_target_set_as_unused(vp->render_target); if (vp->use_xr && xr_interface.is_valid()) { // override our size, make sure it matches our required size and is created as a stereo target - vp->size = xr_interface->get_render_targetsize(); + vp->size = xr_interface->get_render_target_size(); uint32_t view_count = xr_interface->get_view_count(); RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count); @@ -556,7 +595,7 @@ void RendererViewport::draw_viewports() { RSG::scene->set_debug_draw_mode(vp->debug_draw); // and draw viewport - _draw_viewport(vp, view_count); + _draw_viewport(vp); // measure @@ -580,17 +619,17 @@ void RendererViewport::draw_viewports() { RSG::scene->set_debug_draw_mode(vp->debug_draw); // render standard mono camera - _draw_viewport(vp, 1); + _draw_viewport(vp); if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) { //copy to screen if set as such BlitToScreen blit; blit.render_target = vp->render_target; if (vp->viewport_to_screen_rect != Rect2()) { - blit.rect = vp->viewport_to_screen_rect; + blit.dst_rect = vp->viewport_to_screen_rect; } else { - blit.rect.position = Vector2(); - blit.rect.size = vp->size; + blit.dst_rect.position = Vector2(); + blit.dst_rect.size = vp->size; } if (!blit_to_screen_list.has(vp->viewport_to_screen)) { @@ -648,9 +687,19 @@ void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) { } viewport->use_xr = p_use_xr; - if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding, viewport->get_view_count()); + _configure_3d_render_buffers(viewport); +} + +void RendererViewport::viewport_set_scale_3d(RID p_viewport, RenderingServer::ViewportScale3D p_scale_3d) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (viewport->scale_3d == p_scale_3d) { + return; } + + viewport->scale_3d = p_scale_3d; + _configure_3d_render_buffers(viewport); } uint32_t RendererViewport::Viewport::get_view_count() { @@ -677,14 +726,7 @@ void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_heig viewport->size = Size2(p_width, p_height); uint32_t view_count = viewport->get_view_count(); RSG::storage->render_target_set_size(viewport->render_target, p_width, p_height, view_count); - if (viewport->render_buffers.is_valid()) { - if (p_width == 0 || p_height == 0) { - RSG::scene->free(viewport->render_buffers); - viewport->render_buffers = RID(); - } else { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding, view_count); - } - } + _configure_3d_render_buffers(viewport); viewport->occlusion_buffer_dirty = true; } @@ -915,9 +957,7 @@ void RendererViewport::viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa return; } viewport->msaa = p_msaa; - if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa, viewport->use_debanding, viewport->get_view_count()); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) { @@ -928,9 +968,7 @@ void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::Viewport return; } viewport->screen_space_aa = p_mode; - if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode, viewport->use_debanding, viewport->get_view_count()); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) { @@ -941,9 +979,7 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb return; } viewport->use_debanding = p_use_debanding; - if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, p_use_debanding, viewport->get_view_count()); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index ac7a35f97d..f6095e18d7 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -49,6 +49,8 @@ public: bool use_xr; /* use xr interface to override camera positioning and projection matrices and control output */ + RS::ViewportScale3D scale_3d = RenderingServer::VIEWPORT_SCALE_3D_DISABLED; + Size2i size; RID camera; RID scenario; @@ -192,8 +194,9 @@ public: int total_draw_calls_used = 0; private: + void _configure_3d_render_buffers(Viewport *p_viewport); void _draw_3d(Viewport *p_viewport); - void _draw_viewport(Viewport *p_viewport, uint32_t p_view_count = 1); + void _draw_viewport(Viewport *p_viewport); int occlusion_rays_per_thread = 512; @@ -204,6 +207,7 @@ public: void viewport_initialize(RID p_rid); void viewport_set_use_xr(RID p_viewport, bool p_use_xr); + void viewport_set_scale_3d(RID p_viewport, RenderingServer::ViewportScale3D p_scale_3d); void viewport_set_size(RID p_viewport, int p_width, int p_height); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index e2d207dab2..2cf1f165dd 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -495,6 +495,7 @@ public: virtual bool texture_is_format_supported_for_usage(DataFormat p_format, uint32_t p_usage) const = 0; virtual bool texture_is_shared(RID p_texture) = 0; virtual bool texture_is_valid(RID p_texture) = 0; + virtual Size2i texture_size(RID p_texture) = 0; virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index c1336ee42d..56e79b62f2 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -526,6 +526,7 @@ public: FUNCRIDSPLIT(viewport) FUNC2(viewport_set_use_xr, RID, bool) + FUNC2(viewport_set_scale_3d, RID, ViewportScale3D) FUNC3(viewport_set_size, RID, int, int) FUNC2(viewport_set_active, RID, bool) @@ -762,6 +763,7 @@ public: FUNC4(canvas_item_add_circle, RID, const Point2 &, float, const Color &) FUNC6(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool) FUNC7(canvas_item_add_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, bool, bool) + FUNC7(canvas_item_add_msdf_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, int, float) FUNC10(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &) FUNC6(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float) FUNC5(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 222ea9e622..78604dfe8c 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2138,6 +2138,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_create"), &RenderingServer::viewport_create); ClassDB::bind_method(D_METHOD("viewport_set_use_xr", "viewport", "use_xr"), &RenderingServer::viewport_set_use_xr); + ClassDB::bind_method(D_METHOD("viewport_set_scale_3d", "viewport", "scale"), &RenderingServer::viewport_set_scale_3d); ClassDB::bind_method(D_METHOD("viewport_set_size", "viewport", "width", "height"), &RenderingServer::viewport_set_size); ClassDB::bind_method(D_METHOD("viewport_set_active", "viewport", "active"), &RenderingServer::viewport_set_active); ClassDB::bind_method(D_METHOD("viewport_set_parent_viewport", "viewport", "parent_viewport"), &RenderingServer::viewport_set_parent_viewport); @@ -2255,6 +2256,12 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_OCCLUDERS); + BIND_ENUM_CONSTANT(VIEWPORT_SCALE_3D_DISABLED); + BIND_ENUM_CONSTANT(VIEWPORT_SCALE_3D_75_PERCENT); + BIND_ENUM_CONSTANT(VIEWPORT_SCALE_3D_50_PERCENT); + BIND_ENUM_CONSTANT(VIEWPORT_SCALE_3D_33_PERCENT); + BIND_ENUM_CONSTANT(VIEWPORT_SCALE_3D_25_PERCENT); + /* SKY API */ ClassDB::bind_method(D_METHOD("sky_create"), &RenderingServer::sky_create); @@ -2795,6 +2802,12 @@ RenderingServer::RenderingServer() { "rendering/vulkan/rendering/back_end", PROPERTY_HINT_ENUM, "Forward Clustered (Supports Desktop Only),Forward Mobile (Supports Desktop and Mobile)")); + GLOBAL_DEF("rendering/3d/viewport/scale", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/3d/viewport/scale", + PropertyInfo(Variant::INT, + "rendering/3d/viewport/scale", + PROPERTY_HINT_ENUM, "Disabled,75%,50%,33%,25%")); + GLOBAL_DEF("rendering/shader_compiler/shader_cache/enabled", true); GLOBAL_DEF("rendering/shader_compiler/shader_cache/compress", true); GLOBAL_DEF("rendering/shader_compiler/shader_cache/use_zstd_compression", true); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 545270dcd9..8fbf231d9b 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -752,9 +752,19 @@ public: CANVAS_ITEM_TEXTURE_REPEAT_MAX, }; + enum ViewportScale3D { + VIEWPORT_SCALE_3D_DISABLED, + VIEWPORT_SCALE_3D_75_PERCENT, + VIEWPORT_SCALE_3D_50_PERCENT, + VIEWPORT_SCALE_3D_33_PERCENT, + VIEWPORT_SCALE_3D_25_PERCENT, + VIEWPORT_SCALE_3D_MAX, + }; + virtual RID viewport_create() = 0; virtual void viewport_set_use_xr(RID p_viewport, bool p_use_xr) = 0; + virtual void viewport_set_scale_3d(RID p_viewport, ViewportScale3D p_scale_3d) = 0; virtual void viewport_set_size(RID p_viewport, int p_width, int p_height) = 0; virtual void viewport_set_active(RID p_viewport, bool p_active) = 0; virtual void viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) = 0; @@ -1248,6 +1258,7 @@ public: virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0; virtual void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) = 0; virtual void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false) = 0; + virtual void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0) = 0; virtual void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, NinePatchAxisMode p_x_axis_mode = NINE_PATCH_STRETCH, NinePatchAxisMode p_y_axis_mode = NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1)) = 0; virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0) = 0; virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID()) = 0; @@ -1542,6 +1553,7 @@ VARIANT_ENUM_CAST(RenderingServer::ViewportDebugDraw); VARIANT_ENUM_CAST(RenderingServer::ViewportOcclusionCullingBuildQuality); VARIANT_ENUM_CAST(RenderingServer::ViewportSDFOversize); VARIANT_ENUM_CAST(RenderingServer::ViewportSDFScale); +VARIANT_ENUM_CAST(RenderingServer::ViewportScale3D); VARIANT_ENUM_CAST(RenderingServer::SkyMode); VARIANT_ENUM_CAST(RenderingServer::EnvironmentBG); VARIANT_ENUM_CAST(RenderingServer::EnvironmentAmbientSource); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index e51e229418..2f343e8f80 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -216,83 +216,130 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("free_rid", "rid"), &TextServer::free); // shouldn't conflict with Object::free() /* Font Interface */ - ClassDB::bind_method(D_METHOD("create_font_system", "name", "base_size"), &TextServer::create_font_system, DEFVAL(16)); - ClassDB::bind_method(D_METHOD("create_font_resource", "filename", "base_size"), &TextServer::create_font_resource, DEFVAL(16)); - ClassDB::bind_method(D_METHOD("create_font_memory", "data", "type", "base_size"), &TextServer::_create_font_memory, DEFVAL(16)); - ClassDB::bind_method(D_METHOD("create_font_bitmap", "height", "ascent", "base_size"), &TextServer::create_font_bitmap); - ClassDB::bind_method(D_METHOD("font_bitmap_add_texture", "font", "texture"), &TextServer::font_bitmap_add_texture); - ClassDB::bind_method(D_METHOD("font_bitmap_add_char", "font", "char", "texture_idx", "rect", "align", "advance"), &TextServer::font_bitmap_add_char); - ClassDB::bind_method(D_METHOD("font_bitmap_add_kerning_pair", "font", "A", "B", "kerning"), &TextServer::font_bitmap_add_kerning_pair); + ClassDB::bind_method(D_METHOD("create_font"), &TextServer::create_font); - ClassDB::bind_method(D_METHOD("font_get_height", "font", "size"), &TextServer::font_get_height); - ClassDB::bind_method(D_METHOD("font_get_ascent", "font", "size"), &TextServer::font_get_ascent); - ClassDB::bind_method(D_METHOD("font_get_descent", "font", "size"), &TextServer::font_get_descent); + ClassDB::bind_method(D_METHOD("font_set_data", "data"), &TextServer::font_set_data); - ClassDB::bind_method(D_METHOD("font_get_underline_position", "font", "size"), &TextServer::font_get_underline_position); - ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font", "size"), &TextServer::font_get_underline_thickness); + ClassDB::bind_method(D_METHOD("font_set_antialiased", "font_rid", "antialiased"), &TextServer::font_set_antialiased); + ClassDB::bind_method(D_METHOD("font_is_antialiased", "font_rid"), &TextServer::font_is_antialiased); - ClassDB::bind_method(D_METHOD("font_get_spacing_space", "font"), &TextServer::font_get_spacing_space); - ClassDB::bind_method(D_METHOD("font_set_spacing_space", "font", "value"), &TextServer::font_set_spacing_space); + ClassDB::bind_method(D_METHOD("font_set_multichannel_signed_distance_field", "font_rid", "msdf"), &TextServer::font_set_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("font_is_multichannel_signed_distance_field", "font_rid"), &TextServer::font_is_multichannel_signed_distance_field); - ClassDB::bind_method(D_METHOD("font_get_spacing_glyph", "font"), &TextServer::font_get_spacing_glyph); - ClassDB::bind_method(D_METHOD("font_set_spacing_glyph", "font", "value"), &TextServer::font_set_spacing_glyph); + ClassDB::bind_method(D_METHOD("font_set_msdf_pixel_range", "font_rid", "msdf_pixel_range"), &TextServer::font_set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("font_get_msdf_pixel_range", "font_rid"), &TextServer::font_get_msdf_pixel_range); - ClassDB::bind_method(D_METHOD("font_set_antialiased", "font", "antialiased"), &TextServer::font_set_antialiased); - ClassDB::bind_method(D_METHOD("font_get_antialiased", "font"), &TextServer::font_get_antialiased); + ClassDB::bind_method(D_METHOD("font_set_msdf_size", "font_rid", "msdf_size"), &TextServer::font_set_msdf_size); + ClassDB::bind_method(D_METHOD("font_get_msdf_size", "font_rid"), &TextServer::font_get_msdf_size); - ClassDB::bind_method(D_METHOD("font_get_feature_list", "font"), &TextServer::font_get_feature_list); - ClassDB::bind_method(D_METHOD("font_get_variation_list", "font"), &TextServer::font_get_variation_list); + ClassDB::bind_method(D_METHOD("font_set_fixed_size", "font_rid", "fixed_size"), &TextServer::font_set_fixed_size); + ClassDB::bind_method(D_METHOD("font_get_fixed_size", "font_rid"), &TextServer::font_get_fixed_size); - ClassDB::bind_method(D_METHOD("font_set_variation", "font", "tag", "value"), &TextServer::font_set_variation); - ClassDB::bind_method(D_METHOD("font_get_variation", "font", "tag"), &TextServer::font_get_variation); + ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter); + ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter); - ClassDB::bind_method(D_METHOD("font_set_hinting", "font", "hinting"), &TextServer::font_set_hinting); - ClassDB::bind_method(D_METHOD("font_get_hinting", "font"), &TextServer::font_get_hinting); + ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "_hinting"), &TextServer::font_set_hinting); + ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting); - ClassDB::bind_method(D_METHOD("font_set_distance_field_hint", "font", "distance_field"), &TextServer::font_set_distance_field_hint); - ClassDB::bind_method(D_METHOD("font_get_distance_field_hint", "font"), &TextServer::font_get_distance_field_hint); + ClassDB::bind_method(D_METHOD("font_set_variation_coordinates", "font_rid", "variation_coordinates"), &TextServer::font_set_variation_coordinates); + ClassDB::bind_method(D_METHOD("font_get_variation_coordinates", "font_rid"), &TextServer::font_get_variation_coordinates); - ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font", "enabeld"), &TextServer::font_set_force_autohinter); - ClassDB::bind_method(D_METHOD("font_get_force_autohinter", "font"), &TextServer::font_get_force_autohinter); + ClassDB::bind_method(D_METHOD("font_set_oversampling", "font_rid", "oversampling"), &TextServer::font_set_oversampling); + ClassDB::bind_method(D_METHOD("font_get_oversampling", "font_rid"), &TextServer::font_get_oversampling); - ClassDB::bind_method(D_METHOD("font_has_char", "font", "char"), &TextServer::font_has_char); - ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font"), &TextServer::font_get_supported_chars); + ClassDB::bind_method(D_METHOD("font_get_size_cache_list", "font_rid"), &TextServer::font_get_size_cache_list); + ClassDB::bind_method(D_METHOD("font_clear_size_cache", "font_rid"), &TextServer::font_clear_size_cache); + ClassDB::bind_method(D_METHOD("font_remove_size_cache", "font_rid", "size"), &TextServer::font_remove_size_cache); - ClassDB::bind_method(D_METHOD("font_has_outline", "font"), &TextServer::font_has_outline); - ClassDB::bind_method(D_METHOD("font_get_base_size", "font"), &TextServer::font_get_base_size); + ClassDB::bind_method(D_METHOD("font_set_ascent", "font_rid", "size", "ascent"), &TextServer::font_set_ascent); + ClassDB::bind_method(D_METHOD("font_get_ascent", "font_rid", "size"), &TextServer::font_get_ascent); - ClassDB::bind_method(D_METHOD("font_is_language_supported", "font", "language"), &TextServer::font_is_language_supported); - ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font", "language", "supported"), &TextServer::font_set_language_support_override); + ClassDB::bind_method(D_METHOD("font_set_descent", "font_rid", "size", "descent"), &TextServer::font_set_descent); + ClassDB::bind_method(D_METHOD("font_get_descent", "font_rid", "size"), &TextServer::font_get_descent); - ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font", "language"), &TextServer::font_get_language_support_override); - ClassDB::bind_method(D_METHOD("font_remove_language_support_override", "font", "language"), &TextServer::font_remove_language_support_override); - ClassDB::bind_method(D_METHOD("font_get_language_support_overrides", "font"), &TextServer::font_get_language_support_overrides); + ClassDB::bind_method(D_METHOD("font_set_underline_position", "font_rid", "size", "underline_position"), &TextServer::font_set_underline_position); + ClassDB::bind_method(D_METHOD("font_get_underline_position", "font_rid", "size"), &TextServer::font_get_underline_position); - ClassDB::bind_method(D_METHOD("font_is_script_supported", "font", "script"), &TextServer::font_is_script_supported); - ClassDB::bind_method(D_METHOD("font_set_script_support_override", "font", "script", "supported"), &TextServer::font_set_script_support_override); + ClassDB::bind_method(D_METHOD("font_set_underline_thickness", "font_rid", "size", "underline_thickness"), &TextServer::font_set_underline_thickness); + ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font_rid", "size"), &TextServer::font_get_underline_thickness); - ClassDB::bind_method(D_METHOD("font_get_script_support_override", "font", "script"), &TextServer::font_get_script_support_override); - ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font", "script"), &TextServer::font_remove_script_support_override); - ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font"), &TextServer::font_get_script_support_overrides); + ClassDB::bind_method(D_METHOD("font_set_scale", "font_rid", "size", "scale"), &TextServer::font_set_scale); + ClassDB::bind_method(D_METHOD("font_get_scale", "font_rid", "size"), &TextServer::font_get_scale); - ClassDB::bind_method(D_METHOD("font_get_glyph_index", "font", "char", "variation_selector"), &TextServer::font_get_glyph_index, DEFVAL(0x0000)); - ClassDB::bind_method(D_METHOD("font_get_glyph_advance", "font", "index", "size"), &TextServer::font_get_glyph_advance); - ClassDB::bind_method(D_METHOD("font_get_glyph_kerning", "font", "index_a", "index_b", "size"), &TextServer::font_get_glyph_kerning); + ClassDB::bind_method(D_METHOD("font_set_spacing", "font_rid", "size", "spacing", "value"), &TextServer::font_set_spacing); + ClassDB::bind_method(D_METHOD("font_get_spacing", "font_rid", "size", "spacing"), &TextServer::font_get_spacing); - ClassDB::bind_method(D_METHOD("font_draw_glyph", "font", "canvas", "size", "pos", "index", "color"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font", "canvas", "size", "outline_size", "pos", "index", "color"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("font_get_texture_count", "font_rid", "size"), &TextServer::font_get_texture_count); + ClassDB::bind_method(D_METHOD("font_clear_textures", "font_rid", "size"), &TextServer::font_clear_textures); + ClassDB::bind_method(D_METHOD("font_remove_texture", "font_rid", "size", "texture_index"), &TextServer::font_remove_texture); - ClassDB::bind_method(D_METHOD("font_get_oversampling"), &TextServer::font_get_oversampling); - ClassDB::bind_method(D_METHOD("font_set_oversampling", "oversampling"), &TextServer::font_set_oversampling); + ClassDB::bind_method(D_METHOD("font_set_texture_image", "font_rid", "size", "texture_index", "image"), &TextServer::font_set_texture_image); + ClassDB::bind_method(D_METHOD("font_get_texture_image", "font_rid", "size", "texture_index"), &TextServer::font_get_texture_image); - ClassDB::bind_method(D_METHOD("get_system_fonts"), &TextServer::get_system_fonts); + ClassDB::bind_method(D_METHOD("font_set_texture_offsets", "font_rid", "size", "texture_index", "offset"), &TextServer::font_set_texture_offsets); + ClassDB::bind_method(D_METHOD("font_get_texture_offsets", "font_rid", "size", "texture_index"), &TextServer::font_get_texture_offsets); - ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size); - ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box); + ClassDB::bind_method(D_METHOD("font_get_glyph_list", "font_rid", "size"), &TextServer::font_get_glyph_list); + ClassDB::bind_method(D_METHOD("font_clear_glyphs", "font_rid", "size"), &TextServer::font_clear_glyphs); + ClassDB::bind_method(D_METHOD("font_remove_glyph", "font_rid", "size", "glyph"), &TextServer::font_remove_glyph); + + ClassDB::bind_method(D_METHOD("font_get_glyph_advance", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_advance); + ClassDB::bind_method(D_METHOD("font_set_glyph_advance", "font_rid", "size", "glyph", "advance"), &TextServer::font_set_glyph_advance); + + ClassDB::bind_method(D_METHOD("font_get_glyph_offset", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_offset); + ClassDB::bind_method(D_METHOD("font_set_glyph_offset", "font_rid", "size", "glyph", "offset"), &TextServer::font_set_glyph_offset); + + ClassDB::bind_method(D_METHOD("font_get_glyph_size", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_size); + ClassDB::bind_method(D_METHOD("font_set_glyph_size", "font_rid", "size", "glyph", "gl_size"), &TextServer::font_set_glyph_size); + + ClassDB::bind_method(D_METHOD("font_get_glyph_uv_rect", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_uv_rect); + ClassDB::bind_method(D_METHOD("font_set_glyph_uv_rect", "font_rid", "size", "glyph", "uv_rect"), &TextServer::font_set_glyph_uv_rect); + + ClassDB::bind_method(D_METHOD("font_get_glyph_texture_idx", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_idx); + ClassDB::bind_method(D_METHOD("font_set_glyph_texture_idx", "font_rid", "size", "glyph", "texture_idx"), &TextServer::font_set_glyph_texture_idx); ClassDB::bind_method(D_METHOD("font_get_glyph_contours", "font", "size", "index"), &TextServer::_font_get_glyph_contours); + ClassDB::bind_method(D_METHOD("font_get_kerning_list", "font_rid", "size"), &TextServer::font_get_kerning_list); + ClassDB::bind_method(D_METHOD("font_clear_kerning_map", "font_rid", "size"), &TextServer::font_clear_kerning_map); + ClassDB::bind_method(D_METHOD("font_remove_kerning", "font_rid", "size", "glyph_pair"), &TextServer::font_remove_kerning); + + ClassDB::bind_method(D_METHOD("font_set_kerning", "font_rid", "size", "glyph_pair", "kerning"), &TextServer::font_set_kerning); + ClassDB::bind_method(D_METHOD("font_get_kerning", "font_rid", "size", "glyph_pair"), &TextServer::font_get_kerning); + + ClassDB::bind_method(D_METHOD("font_get_glyph_index", "font_rid", "size", "char", "variation_selector"), &TextServer::font_get_glyph_index); + + ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char); + ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars); + + ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range); + ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph); + + ClassDB::bind_method(D_METHOD("font_draw_glyph", "font_rid", "canvas", "size", "pos", "index", "color"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font_rid", "canvas", "size", "outline_size", "pos", "index", "color"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("font_is_language_supported", "font_rid", "language"), &TextServer::font_is_language_supported); + ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font_rid", "language", "supported"), &TextServer::font_set_language_support_override); + ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font_rid", "language"), &TextServer::font_get_language_support_override); + ClassDB::bind_method(D_METHOD("font_remove_language_support_override", "font_rid", "language"), &TextServer::font_remove_language_support_override); + ClassDB::bind_method(D_METHOD("font_get_language_support_overrides", "font_rid"), &TextServer::font_get_language_support_overrides); + + ClassDB::bind_method(D_METHOD("font_is_script_supported", "font_rid", "script"), &TextServer::font_is_script_supported); + ClassDB::bind_method(D_METHOD("font_set_script_support_override", "font_rid", "script", "supported"), &TextServer::font_set_script_support_override); + ClassDB::bind_method(D_METHOD("font_get_script_support_override", "font_rid", "script"), &TextServer::font_get_script_support_override); + ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font_rid", "script"), &TextServer::font_remove_script_support_override); + ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font_rid"), &TextServer::font_get_script_support_overrides); + + ClassDB::bind_method(D_METHOD("font_supported_feature_list", "font_rid"), &TextServer::font_supported_feature_list); + ClassDB::bind_method(D_METHOD("font_supported_variation_list", "font_rid"), &TextServer::font_supported_variation_list); + + ClassDB::bind_method(D_METHOD("font_get_global_oversampling"), &TextServer::font_get_global_oversampling); + ClassDB::bind_method(D_METHOD("font_set_global_oversampling", "oversampling"), &TextServer::font_set_global_oversampling); + + ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size); + ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box); + /* Shaped text buffer interface */ ClassDB::bind_method(D_METHOD("create_shaped_text", "direction", "orientation"), &TextServer::create_shaped_text, DEFVAL(DIRECTION_AUTO), DEFVAL(ORIENTATION_HORIZONTAL)); @@ -420,6 +467,12 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_ON); BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CONIC); BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CUBIC); + + /* Font Spacing*/ + BIND_ENUM_CONSTANT(SPACING_GLYPH); + BIND_ENUM_CONSTANT(SPACING_SPACE); + BIND_ENUM_CONSTANT(SPACING_TOP); + BIND_ENUM_CONSTANT(SPACING_BOTTOM); } Vector3 TextServer::hex_code_box_font_size[2] = { Vector3(5, 5, 1), Vector3(10, 10, 2) }; @@ -514,8 +567,8 @@ void TextServer::finish_hex_code_box_fonts() { Vector2 TextServer::get_hex_code_box_size(int p_size, char32_t p_index) const { int fnt = (p_size < 20) ? 0 : 1; - float w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; - float h = 2 * hex_code_box_font_size[fnt].y; + real_t w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; + real_t h = 2 * hex_code_box_font_size[fnt].y; return Vector2(w + 4, h + 3 + 2 * hex_code_box_font_size[fnt].z); } @@ -534,8 +587,8 @@ void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_po Vector2 pos = p_pos; Rect2 dest = Rect2(Vector2(), Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y)); - float w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; - float h = 2 * hex_code_box_font_size[fnt].y; + real_t w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; + real_t h = 2 * hex_code_box_font_size[fnt].y; pos.y -= Math::floor((h + 3 + hex_code_box_font_size[fnt].z) * 0.75); @@ -575,7 +628,7 @@ void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_po } } -Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start, bool p_once, uint8_t /*TextBreakFlag*/ p_break_flags) const { +Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start, bool p_once, uint8_t /*TextBreakFlag*/ p_break_flags) const { Vector<Vector2i> lines; ERR_FAIL_COND_V(p_width.is_empty(), lines); @@ -584,7 +637,7 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); const Vector2i &range = shaped_text_get_range(p_shaped); - float width = 0.f; + real_t width = 0.f; int line_start = MAX(p_start, range.x); int last_safe_break = -1; int chunk = 0; @@ -646,14 +699,14 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const return lines; } -Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t /*TextBreakFlag*/ p_break_flags) const { +Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t /*TextBreakFlag*/ p_break_flags) const { Vector<Vector2i> lines; const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped); const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); const Vector2i &range = shaped_text_get_range(p_shaped); - float width = 0.f; + real_t width = 0.f; int line_start = MAX(p_start, range.x); int last_safe_break = -1; int word_count = 0; @@ -747,11 +800,11 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); const Vector2 &range = shaped_text_get_range(p_shaped); - float ascent = shaped_text_get_ascent(p_shaped); - float descent = shaped_text_get_descent(p_shaped); - float height = (ascent + descent) / 2; + real_t ascent = shaped_text_get_ascent(p_shaped); + real_t descent = shaped_text_get_descent(p_shaped); + real_t height = (ascent + descent) / 2; - float off = 0.0f; + real_t off = 0.0f; p_leading_dir = DIRECTION_AUTO; p_trailing_dir = DIRECTION_AUTO; @@ -857,11 +910,11 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l } // Caret inside grapheme (middle). if (p_position > glyphs[i].start && p_position < glyphs[i].end && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) { - float advance = 0.f; + real_t advance = 0.f; for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance * glyphs[i + j].repeat; } - float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); Rect2 cr; if (orientation == ORIENTATION_HORIZONTAL) { cr.size.x = 1.0f; @@ -940,14 +993,14 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, int v_size = visual.size(); const Glyph *glyphs = visual.ptr(); - float off = 0.0f; + real_t off = 0.0f; for (int i = 0; i < v_size; i++) { for (int k = 0; k < glyphs[i].repeat; k++) { if ((glyphs[i].count > 0) && ((glyphs[i].index != 0) || ((glyphs[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE))) { if (glyphs[i].start < end && glyphs[i].end > start) { // Grapheme fully in selection range. if (glyphs[i].start >= start && glyphs[i].end <= end) { - float advance = 0.f; + real_t advance = 0.f; for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance; } @@ -955,11 +1008,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } // Only start of grapheme is in selection range. if (glyphs[i].start >= start && glyphs[i].end > end) { - float advance = 0.f; + real_t advance = 0.f; for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance; } - float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + advance)); } else { @@ -968,11 +1021,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } // Only end of grapheme is in selection range. if (glyphs[i].start < start && glyphs[i].end <= end) { - float advance = 0.f; + real_t advance = 0.f; for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance; } - float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { ranges.push_back(Vector2(off, off + char_adv * (start - glyphs[i].start))); } else { @@ -981,11 +1034,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } // Selection range is within grapheme. if (glyphs[i].start < start && glyphs[i].end > end) { - float advance = 0.f; + real_t advance = 0.f; for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance; } - float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + char_adv * (glyphs[i].end - start))); } else { @@ -1020,11 +1073,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, return ranges; } -int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const { +int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, real_t p_coords) const { const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); // Exact grapheme hit test, return -1 if missed. - float off = 0.0f; + real_t off = 0.0f; int v_size = visual.size(); const Glyph *glyphs = visual.ptr(); @@ -1040,7 +1093,7 @@ int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) cons return -1; } -int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) const { +int TextServer::shaped_text_hit_test_position(RID p_shaped, real_t p_coords) const { const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); int v_size = visual.size(); @@ -1074,10 +1127,10 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons } } - float off = 0.0f; + real_t off = 0.0f; for (int i = 0; i < v_size; i++) { if (glyphs[i].count > 0) { - float advance = 0.f; + real_t advance = 0.f; for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance * glyphs[i + j].repeat; } @@ -1135,7 +1188,7 @@ int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) { return p_pos; } -void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, const Color &p_color) const { +void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l, real_t p_clip_r, const Color &p_color) const { const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped); @@ -1228,7 +1281,7 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p } } -void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, int p_outline_size, const Color &p_color) const { +void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l, real_t p_clip_r, int p_outline_size, const Color &p_color) const { const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); @@ -1316,11 +1369,7 @@ void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vect } } -RID TextServer::_create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) { - return create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size); -} - -Dictionary TextServer::_font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index) const { +Dictionary TextServer::_font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const { Vector<Vector3> points; Vector<int32_t> contours; bool orientation; @@ -1378,7 +1427,7 @@ Array TextServer::_shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFlo return ret; } -Array TextServer::_shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const { +Array TextServer::_shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const { Array ret; Vector<Vector2i> lines = shaped_text_get_line_breaks(p_shaped, p_width, p_start, p_break_flags); diff --git a/servers/text_server.h b/servers/text_server.h index 26993838a3..62e02e3c97 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -116,6 +116,13 @@ public: CONTOUR_CURVE_TAG_OFF_CUBIC = 0x02 }; + enum SpacingType { + SPACING_GLYPH, + SPACING_SPACE, + SPACING_TOP, + SPACING_BOTTOM, + }; + struct Glyph { int start = -1; // Start offset in the source string. int end = -1; // End offset in the source string. @@ -124,13 +131,13 @@ public: uint8_t repeat = 1; // Draw multiple times in the row. uint16_t flags = 0; // Grapheme flags (valid, rtl, virtual), set in the first glyph only. - float x_off = 0.f; // Offset from the origin of the glyph on baseline. - float y_off = 0.f; - float advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical). + real_t x_off = 0.f; // Offset from the origin of the glyph on baseline. + real_t y_off = 0.f; + real_t advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical). RID font_rid; // Font resource. int font_size = 0; // Font size; - uint32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs). + int32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs). bool operator==(const Glyph &p_a) const; bool operator!=(const Glyph &p_a) const; @@ -163,6 +170,8 @@ public: }; struct ShapedTextData { + Mutex mutex; + /* Source data */ RID parent; // Substring parent ShapedTextData. @@ -205,13 +214,13 @@ public: bool preserve_invalid = true; // Draw hex code box instead of missing characters. bool preserve_control = false; // Draw control characters. - float ascent = 0.f; // Ascent for horizontal layout, 1/2 of width for vertical. - float descent = 0.f; // Descent for horizontal layout, 1/2 of width for vertical. - float width = 0.f; // Width for horizontal layout, height for vertical. - float width_trimmed = 0.f; + real_t ascent = 0.f; // Ascent for horizontal layout, 1/2 of width for vertical. + real_t descent = 0.f; // Descent for horizontal layout, 1/2 of width for vertical. + real_t width = 0.f; // Width for horizontal layout, height for vertical. + real_t width_trimmed = 0.f; - float upos = 0.f; - float uthk = 0.f; + real_t upos = 0.f; + real_t uthk = 0.f; TrimData overrun_trim_data; bool fit_width_minimum_reached = false; @@ -245,85 +254,134 @@ public: virtual bool is_locale_right_to_left(const String &p_locale) = 0; - virtual int32_t name_to_tag(const String &p_name) { return 0; }; - virtual String tag_to_name(int32_t p_tag) { return ""; }; + virtual int32_t name_to_tag(const String &p_name) const { return 0; }; + virtual String tag_to_name(int32_t p_tag) const { return ""; }; /* Font interface */ - virtual RID create_font_system(const String &p_name, int p_base_size = 16) = 0; - virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) = 0; - virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) = 0; - virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) = 0; + virtual RID create_font() = 0; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) = 0; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) = 0; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) = 0; + virtual bool font_is_antialiased(RID p_font_rid) const = 0; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) = 0; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const = 0; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) = 0; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const = 0; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) = 0; + virtual int font_get_msdf_size(RID p_font_rid) const = 0; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) = 0; + virtual int font_get_fixed_size(RID p_font_rid) const = 0; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) = 0; + virtual bool font_is_force_autohinter(RID p_font_rid) const = 0; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) = 0; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const = 0; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) = 0; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) = 0; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) = 0; + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) = 0; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const = 0; - virtual float font_get_height(RID p_font, int p_size) const = 0; - virtual float font_get_ascent(RID p_font, int p_size) const = 0; - virtual float font_get_descent(RID p_font, int p_size) const = 0; + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) = 0; + virtual real_t font_get_oversampling(RID p_font_rid) const = 0; - virtual int font_get_spacing_space(RID p_font) const = 0; - virtual void font_set_spacing_space(RID p_font, int p_value) = 0; + virtual Array font_get_size_cache_list(RID p_font_rid) const = 0; + virtual void font_clear_size_cache(RID p_font_rid) = 0; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) = 0; - virtual int font_get_spacing_glyph(RID p_font) const = 0; - virtual void font_set_spacing_glyph(RID p_font, int p_value) = 0; + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) = 0; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const = 0; - virtual float font_get_underline_position(RID p_font, int p_size) const = 0; - virtual float font_get_underline_thickness(RID p_font, int p_size) const = 0; + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) = 0; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const = 0; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) = 0; - virtual bool font_get_antialiased(RID p_font) const = 0; + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) = 0; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const = 0; - virtual Dictionary font_get_feature_list(RID p_font) const { return Dictionary(); }; - virtual Dictionary font_get_variation_list(RID p_font) const { return Dictionary(); }; + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) = 0; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const = 0; - virtual void font_set_variation(RID p_font, const String &p_name, double p_value){}; - virtual double font_get_variation(RID p_font, const String &p_name) const { return 0; }; + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) = 0; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const = 0; - virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) = 0; - virtual bool font_get_distance_field_hint(RID p_font) const = 0; + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) = 0; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const = 0; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) = 0; - virtual Hinting font_get_hinting(RID p_font) const = 0; + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const = 0; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) = 0; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) = 0; - virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) = 0; - virtual bool font_get_force_autohinter(RID p_font) const = 0; + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) = 0; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const = 0; - virtual bool font_has_char(RID p_font, char32_t p_char) const = 0; - virtual String font_get_supported_chars(RID p_font) const = 0; + virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) = 0; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const = 0; - virtual bool font_has_outline(RID p_font) const = 0; - virtual float font_get_base_size(RID p_font) const = 0; + virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const = 0; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) = 0; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) = 0; - virtual bool font_is_language_supported(RID p_font, const String &p_language) const = 0; - virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) = 0; - virtual bool font_get_language_support_override(RID p_font, const String &p_language) = 0; - virtual void font_remove_language_support_override(RID p_font, const String &p_language) = 0; - virtual Vector<String> font_get_language_support_overrides(RID p_font) = 0; + virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const = 0; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) = 0; - virtual bool font_is_script_supported(RID p_font, const String &p_script) const = 0; - virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) = 0; - virtual bool font_get_script_support_override(RID p_font, const String &p_script) = 0; - virtual void font_remove_script_support_override(RID p_font, const String &p_script) = 0; - virtual Vector<String> font_get_script_support_overrides(RID p_font) = 0; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) = 0; - virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const = 0; - virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const = 0; - virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const = 0; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) = 0; - virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0; - virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0; + virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) = 0; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const = 0; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) = 0; - virtual float font_get_oversampling() const = 0; - virtual void font_set_oversampling(float p_oversampling) = 0; + virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const = 0; + + virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const = 0; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) = 0; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) = 0; + + virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) = 0; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const = 0; + + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const = 0; + + virtual bool font_has_char(RID p_font_rid, char32_t p_char) const = 0; + virtual String font_get_supported_chars(RID p_font_rid) const = 0; + + virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) = 0; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) = 0; + + virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0; + + virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const = 0; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) = 0; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) = 0; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) = 0; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) = 0; + + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const = 0; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) = 0; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) = 0; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) = 0; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) = 0; + + virtual Dictionary font_supported_feature_list(RID p_font_rid) const = 0; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const = 0; + + virtual real_t font_get_global_oversampling() const = 0; + virtual void font_set_global_oversampling(real_t p_oversampling) = 0; Vector2 get_hex_code_box_size(int p_size, char32_t p_index) const; void draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_pos, char32_t p_index, const Color &p_color) const; - virtual Vector<String> get_system_fonts() const = 0; - /* Shaped text buffer interface */ virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0; @@ -351,8 +409,8 @@ public: virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range. virtual RID shaped_text_get_parent(RID p_shaped) const = 0; - virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) = 0; - virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) = 0; + virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) = 0; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) = 0; virtual bool shaped_text_shape(RID p_shaped) = 0; virtual bool shaped_text_update_breaks(RID p_shaped) = 0; @@ -366,36 +424,36 @@ public: virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) = 0; - virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const; - virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const; + virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const; + virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start = 0, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const; virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION) const; virtual TrimData shaped_text_get_trim_data(RID p_shaped) const; - virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) = 0; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) = 0; virtual Array shaped_text_get_objects(RID p_shaped) const = 0; virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const = 0; virtual Size2 shaped_text_get_size(RID p_shaped) const = 0; - virtual float shaped_text_get_ascent(RID p_shaped) const = 0; - virtual float shaped_text_get_descent(RID p_shaped) const = 0; - virtual float shaped_text_get_width(RID p_shaped) const = 0; - virtual float shaped_text_get_underline_position(RID p_shaped) const = 0; - virtual float shaped_text_get_underline_thickness(RID p_shaped) const = 0; + virtual real_t shaped_text_get_ascent(RID p_shaped) const = 0; + virtual real_t shaped_text_get_descent(RID p_shaped) const = 0; + virtual real_t shaped_text_get_width(RID p_shaped) const = 0; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const = 0; + virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const = 0; virtual Direction shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const; virtual void shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const; virtual Vector<Vector2> shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const; - virtual int shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const; // Return grapheme index. - virtual int shaped_text_hit_test_position(RID p_shaped, float p_coords) const; // Return caret/selection position. + virtual int shaped_text_hit_test_grapheme(RID p_shaped, real_t p_coords) const; // Return grapheme index. + virtual int shaped_text_hit_test_position(RID p_shaped, real_t p_coords) const; // Return caret/selection position. virtual int shaped_text_next_grapheme_pos(RID p_shaped, int p_pos); virtual int shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos); // The pen position is always placed on the baseline and moveing left to right. - virtual void shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, const Color &p_color = Color(1, 1, 1)) const; - virtual void shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; + virtual void shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l = -1.f, real_t p_clip_r = -1.f, const Color &p_color = Color(1, 1, 1)) const; + virtual void shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l = -1.f, real_t p_clip_r = -1.f, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; // Number conversion. virtual String format_number(const String &p_string, const String &p_language = "") const { return p_string; }; @@ -403,9 +461,9 @@ public: virtual String percent_sign(const String &p_language = "") const { return "%"; }; /* GDScript wrappers */ - RID _create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16); + RID _create_font_memory(const PackedByteArray &p_data, int p_base_size = 16); - Dictionary _font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index) const; + Dictionary _font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const; Array _shaped_text_get_glyphs(RID p_shaped) const; Dictionary _shaped_text_get_carets(RID p_shaped, int p_position) const; @@ -413,7 +471,7 @@ public: void _shaped_text_set_bidi_override(RID p_shaped, const Array &p_override); Array _shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFloat32Array &p_width, int p_start, bool p_once, uint8_t p_break_flags) const; - Array _shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const; + Array _shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const; Array _shaped_text_get_word_breaks(RID p_shaped) const; Array _shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const; @@ -491,5 +549,6 @@ VARIANT_ENUM_CAST(TextServer::GraphemeFlag); VARIANT_ENUM_CAST(TextServer::Hinting); VARIANT_ENUM_CAST(TextServer::Feature); VARIANT_ENUM_CAST(TextServer::ContourPointTag); +VARIANT_ENUM_CAST(TextServer::SpacingType); #endif // TEXT_SERVER_H diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 09e8e12f8b..fc1d82a964 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -36,21 +36,19 @@ void XRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("get_capabilities"), &XRInterface::get_capabilities); ClassDB::bind_method(D_METHOD("is_primary"), &XRInterface::is_primary); - ClassDB::bind_method(D_METHOD("set_is_primary", "enable"), &XRInterface::set_is_primary); + ClassDB::bind_method(D_METHOD("set_primary", "primary"), &XRInterface::set_primary); ClassDB::bind_method(D_METHOD("is_initialized"), &XRInterface::is_initialized); - ClassDB::bind_method(D_METHOD("set_is_initialized", "initialized"), &XRInterface::set_is_initialized); ClassDB::bind_method(D_METHOD("initialize"), &XRInterface::initialize); ClassDB::bind_method(D_METHOD("uninitialize"), &XRInterface::uninitialize); ClassDB::bind_method(D_METHOD("get_tracking_status"), &XRInterface::get_tracking_status); - ClassDB::bind_method(D_METHOD("get_render_targetsize"), &XRInterface::get_render_targetsize); + ClassDB::bind_method(D_METHOD("get_render_target_size"), &XRInterface::get_render_target_size); ClassDB::bind_method(D_METHOD("get_view_count"), &XRInterface::get_view_count); ADD_GROUP("Interface", "interface_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_is_primary", "is_primary"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_initialized"), "set_is_initialized", "is_initialized"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_primary", "is_primary"); // we don't have any properties specific to VR yet.... @@ -77,70 +75,48 @@ void XRInterface::_bind_methods() { BIND_ENUM_CONSTANT(XR_INSUFFICIENT_FEATURES); BIND_ENUM_CONSTANT(XR_UNKNOWN_TRACKING); BIND_ENUM_CONSTANT(XR_NOT_TRACKING); -}; - -StringName XRInterface::get_name() const { - return "Unknown"; -}; +} bool XRInterface::is_primary() { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, false); return xr_server->get_primary_interface() == this; -}; +} -void XRInterface::set_is_primary(bool p_is_primary) { +void XRInterface::set_primary(bool p_primary) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - if (p_is_primary) { + if (p_primary) { ERR_FAIL_COND(!is_initialized()); xr_server->set_primary_interface(this); - } else { - xr_server->clear_primary_interface_if(this); - }; -}; - -void XRInterface::set_is_initialized(bool p_initialized) { - if (p_initialized) { - if (!is_initialized()) { - initialize(); - }; - } else { - if (is_initialized()) { - uninitialize(); - }; - }; -}; - -XRInterface::Tracking_status XRInterface::get_tracking_status() const { - return tracking_state; -}; - -XRInterface::XRInterface() { - tracking_state = XR_UNKNOWN_TRACKING; -}; + } else if (xr_server->get_primary_interface() == this) { + xr_server->set_primary_interface(nullptr); + } +} -XRInterface::~XRInterface() {} +XRInterface::XRInterface() {} -// optional render to external texture which enhances performance on those platforms that require us to submit our end result into special textures. -unsigned int XRInterface::get_external_texture_for_eye(XRInterface::Eyes p_eye) { - return 0; -}; +XRInterface::~XRInterface() {} /** these will only be implemented on AR interfaces, so we want dummies for VR **/ bool XRInterface::get_anchor_detection_is_enabled() const { return false; -}; +} void XRInterface::set_anchor_detection_is_enabled(bool p_enable) { - // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. } int XRInterface::get_camera_feed_id() { - // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. - return 0; -}; +} + +/** these are optional, so we want dummies **/ +XRInterface::TrackingStatus XRInterface::get_tracking_status() const { + return XR_UNKNOWN_TRACKING; +} + +void XRInterface::notification(int p_what) { +} diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index 6b248c9554..4f5d4bad10 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -68,7 +68,7 @@ public: EYE_RIGHT }; - enum Tracking_status { /* tracking status currently based on AR but we can start doing more with this for VR as well */ + enum TrackingStatus { /* tracking status currently based on AR but we can start doing more with this for VR as well */ XR_NORMAL_TRACKING, XR_EXCESSIVE_MOTION, XR_INSUFFICIENT_FEATURES, @@ -76,26 +76,25 @@ public: XR_NOT_TRACKING }; +private: protected: _THREAD_SAFE_CLASS_ - Tracking_status tracking_state; static void _bind_methods(); public: /** general interface information **/ - virtual StringName get_name() const; - virtual int get_capabilities() const = 0; + virtual StringName get_name() const = 0; + virtual uint32_t get_capabilities() const = 0; bool is_primary(); - void set_is_primary(bool p_is_primary); + void set_primary(bool p_is_primary); virtual bool is_initialized() const = 0; /* returns true if we've initialized this interface */ - void set_is_initialized(bool p_initialized); /* helper function, will call initialize or uninitialize */ virtual bool initialize() = 0; /* initialize this interface, if this has an HMD it becomes the primary interface */ virtual void uninitialize() = 0; /* deinitialize this interface */ - Tracking_status get_tracking_status() const; /* get the status of our current tracking */ + virtual TrackingStatus get_tracking_status() const; /* get the status of our current tracking */ /** specific to VR **/ // nothing yet @@ -107,27 +106,25 @@ public: /** rendering and internal **/ - virtual Size2 get_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */ + virtual Size2 get_render_target_size() = 0; /* returns the recommended render target size per eye for this device */ virtual uint32_t get_view_count() = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */ virtual Transform3D get_camera_transform() = 0; /* returns the position of our camera for updating our camera node. For monoscopic this is equal to the views transform, for stereoscopic this should be an average */ virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */ virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each view projection matrix */ + // note, external color/depth/vrs texture support will be added here soon. + virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* commit rendered views to the XR interface */ virtual void process() = 0; - virtual void notification(int p_what) = 0; + virtual void notification(int p_what); XRInterface(); ~XRInterface(); - - // deprecated - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); /* if applicable return external texture to render to */ - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ }; VARIANT_ENUM_CAST(XRInterface::Capabilities); VARIANT_ENUM_CAST(XRInterface::Eyes); -VARIANT_ENUM_CAST(XRInterface::Tracking_status); +VARIANT_ENUM_CAST(XRInterface::TrackingStatus); -#endif +#endif // !XR_INTERFACE_H diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp new file mode 100644 index 0000000000..5c90139375 --- /dev/null +++ b/servers/xr/xr_interface_extension.cpp @@ -0,0 +1,242 @@ +/*************************************************************************/ +/* xr_interface_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "xr_interface_extension.h" +#include "servers/rendering/renderer_compositor.h" + +void XRInterfaceExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_name); + GDVIRTUAL_BIND(_get_capabilities); + + GDVIRTUAL_BIND(_is_initialized); + GDVIRTUAL_BIND(_initialize); + GDVIRTUAL_BIND(_uninitialize); + + GDVIRTUAL_BIND(_get_tracking_status); + + ClassDB::bind_method(D_METHOD("add_blit", "render_target", "src_rect", "dst_rect", "use_layer", "layer", "apply_lens_distortion", "eye_center", "k1", "k2", "upscale", "aspect_ratio"), &XRInterfaceExtension::add_blit); + + GDVIRTUAL_BIND(_get_render_target_size); + GDVIRTUAL_BIND(_get_view_count); + GDVIRTUAL_BIND(_get_camera_transform); + GDVIRTUAL_BIND(_get_transform_for_view, "view", "cam_transform"); + GDVIRTUAL_BIND(_get_projection_for_view, "view", "aspect", "z_near", "z_far"); + + GDVIRTUAL_BIND(_commit_views, "render_target", "screen_rect"); + + GDVIRTUAL_BIND(_process); + GDVIRTUAL_BIND(_notification, "what"); + + // we don't have any properties specific to VR yet.... + + // but we do have properties specific to AR.... + GDVIRTUAL_BIND(_get_anchor_detection_is_enabled); + GDVIRTUAL_BIND(_set_anchor_detection_is_enabled, "enabled"); + GDVIRTUAL_BIND(_get_camera_feed_id); +} + +StringName XRInterfaceExtension::get_name() const { + StringName name; + + if (GDVIRTUAL_CALL(_get_name, name)) { + return name; + } + + return "Unknown"; +} + +uint32_t XRInterfaceExtension::get_capabilities() const { + uint32_t capabilities; + + if (GDVIRTUAL_CALL(_get_capabilities, capabilities)) { + return capabilities; + } + + return 0; +} + +bool XRInterfaceExtension::is_initialized() const { + bool initialised = false; + + if (GDVIRTUAL_CALL(_is_initialized, initialised)) { + return initialised; + } + + return false; +} + +bool XRInterfaceExtension::initialize() { + bool initialised = false; + + if (GDVIRTUAL_CALL(_initialize, initialised)) { + return initialised; + } + + return false; +} + +void XRInterfaceExtension::uninitialize() { + GDVIRTUAL_CALL(_uninitialize); +} + +XRInterface::TrackingStatus XRInterfaceExtension::get_tracking_status() const { + uint32_t status; + + if (GDVIRTUAL_CALL(_get_tracking_status, status)) { + return TrackingStatus(status); + } + + return XR_UNKNOWN_TRACKING; +} + +/** these will only be implemented on AR interfaces, so we want dummies for VR **/ +bool XRInterfaceExtension::get_anchor_detection_is_enabled() const { + bool enabled; + + if (GDVIRTUAL_CALL(_get_anchor_detection_is_enabled, enabled)) { + return enabled; + } + + return false; +} + +void XRInterfaceExtension::set_anchor_detection_is_enabled(bool p_enable) { + // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. + GDVIRTUAL_CALL(_set_anchor_detection_is_enabled, p_enable); +} + +int XRInterfaceExtension::get_camera_feed_id() { + int feed_id; + + if (GDVIRTUAL_CALL(_get_camera_feed_id, feed_id)) { + return feed_id; + } + + return 0; +} + +Size2 XRInterfaceExtension::get_render_target_size() { + Size2 size; + + if (GDVIRTUAL_CALL(_get_render_target_size, size)) { + return size; + } + + return Size2(0, 0); +} + +uint32_t XRInterfaceExtension::get_view_count() { + uint32_t view_count; + + if (GDVIRTUAL_CALL(_get_view_count, view_count)) { + return view_count; + } + + return 1; +} + +Transform3D XRInterfaceExtension::get_camera_transform() { + Transform3D transform; + + if (GDVIRTUAL_CALL(_get_camera_transform, transform)) { + return transform; + } + + return Transform3D(); +} + +Transform3D XRInterfaceExtension::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { + Transform3D transform; + + if (GDVIRTUAL_CALL(_get_transform_for_view, p_view, p_cam_transform, transform)) { + return transform; + } + + return Transform3D(); +} + +CameraMatrix XRInterfaceExtension::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + CameraMatrix cm; + PackedFloat64Array arr; + + if (GDVIRTUAL_CALL(_get_projection_for_view, p_view, p_aspect, p_z_near, p_z_far, arr)) { + ERR_FAIL_COND_V_MSG(arr.size() != 16, CameraMatrix(), "Projection matrix must contain 16 floats"); + real_t *m = (real_t *)cm.matrix; + for (int i = 0; i < 16; i++) { + m[i] = arr[i]; + } + return cm; + } + + return CameraMatrix(); +} + +void XRInterfaceExtension::add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer, uint32_t p_layer, bool p_apply_lens_distortion, Vector2 p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ratio) { + BlitToScreen blit; + + ERR_FAIL_COND_MSG(!can_add_blits, "add_blit can only be called from an XR plugin from within _commit_views!"); + + blit.render_target = p_render_target; + blit.src_rect = p_src_rect; + blit.dst_rect = p_dst_rect; + + blit.multi_view.use_layer = p_use_layer; + blit.multi_view.layer = p_layer; + + blit.lens_distortion.apply = p_apply_lens_distortion; + blit.lens_distortion.eye_center = p_eye_center; + blit.lens_distortion.k1 = p_k1; + blit.lens_distortion.k2 = p_k2; + blit.lens_distortion.upscale = p_upscale; + blit.lens_distortion.aspect_ratio = p_aspect_ratio; + + blits.push_back(blit); +} + +Vector<BlitToScreen> XRInterfaceExtension::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { + // This is just so our XR plugin can add blits... + blits.clear(); + can_add_blits = true; + + if (GDVIRTUAL_CALL(_commit_views, p_render_target, p_screen_rect)) { + return blits; + } + + can_add_blits = false; + return blits; +} + +void XRInterfaceExtension::process() { + GDVIRTUAL_CALL(_process); +} + +void XRInterfaceExtension::notification(int p_what) { + GDVIRTUAL_CALL(_notification, p_what); +} diff --git a/servers/xr/xr_interface_extension.h b/servers/xr/xr_interface_extension.h new file mode 100644 index 0000000000..df6f5f8e35 --- /dev/null +++ b/servers/xr/xr_interface_extension.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* xr_interface_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 XR_INTERFACE_EXTENSION_H +#define XR_INTERFACE_EXTENSION_H + +#include "servers/xr/xr_interface.h" + +class XRInterfaceExtension : public XRInterface { + GDCLASS(XRInterfaceExtension, XRInterface); + +public: +private: + bool can_add_blits = false; + Vector<BlitToScreen> blits; + +protected: + _THREAD_SAFE_CLASS_ + + static void _bind_methods(); + +public: + /** general interface information **/ + virtual StringName get_name() const override; + virtual uint32_t get_capabilities() const override; + + GDVIRTUAL0RC(StringName, _get_name); + GDVIRTUAL0RC(uint32_t, _get_capabilities); + + virtual bool is_initialized() const override; + virtual bool initialize() override; + virtual void uninitialize() override; + + GDVIRTUAL0RC(bool, _is_initialized); + GDVIRTUAL0R(bool, _initialize); + GDVIRTUAL0(_uninitialize); + + virtual TrackingStatus get_tracking_status() const override; + GDVIRTUAL0RC(uint32_t, _get_tracking_status); + + /** specific to VR **/ + // nothing yet + + /** specific to AR **/ + virtual bool get_anchor_detection_is_enabled() const override; + virtual void set_anchor_detection_is_enabled(bool p_enable) override; + virtual int get_camera_feed_id() override; + + GDVIRTUAL0RC(bool, _get_anchor_detection_is_enabled); + GDVIRTUAL1(_set_anchor_detection_is_enabled, bool); + GDVIRTUAL0RC(int, _get_camera_feed_id); + + /** rendering and internal **/ + + virtual Size2 get_render_target_size() override; + virtual uint32_t get_view_count() override; + virtual Transform3D get_camera_transform() override; + virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; + virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; + + GDVIRTUAL0R(Size2, _get_render_target_size); + GDVIRTUAL0R(uint32_t, _get_view_count); + GDVIRTUAL0R(Transform3D, _get_camera_transform); + GDVIRTUAL2R(Transform3D, _get_transform_for_view, uint32_t, const Transform3D &); + GDVIRTUAL4R(PackedFloat64Array, _get_projection_for_view, uint32_t, real_t, real_t, real_t); + + void add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer = false, uint32_t p_layer = 0, bool p_apply_lens_distortion = false, Vector2 p_eye_center = Vector2(), float p_k1 = 0.0, float p_k2 = 0.0, float p_upscale = 1.0, float p_aspect_ratio = 1.0); + virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; + GDVIRTUAL2(_commit_views, RID, const Rect2 &); + + virtual void process() override; + virtual void notification(int p_what) override; + + GDVIRTUAL0(_process); + GDVIRTUAL1(_notification, int); +}; + +#endif // !XR_INTERFACE_EXTENSION_H diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp index c27656047f..c18a9f8b4e 100644 --- a/servers/xr_server.cpp +++ b/servers/xr_server.cpp @@ -49,7 +49,6 @@ void XRServer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); ClassDB::bind_method(D_METHOD("add_interface", "interface"), &XRServer::add_interface); - ClassDB::bind_method(D_METHOD("clear_primary_interface_if", "interface"), &XRServer::clear_primary_interface_if); ClassDB::bind_method(D_METHOD("get_interface_count"), &XRServer::get_interface_count); ClassDB::bind_method(D_METHOD("remove_interface", "interface"), &XRServer::remove_interface); ClassDB::bind_method(D_METHOD("get_interface", "idx"), &XRServer::get_interface); @@ -316,17 +315,14 @@ Ref<XRInterface> XRServer::get_primary_interface() const { }; void XRServer::set_primary_interface(const Ref<XRInterface> &p_primary_interface) { - ERR_FAIL_COND(p_primary_interface.is_null()); - primary_interface = p_primary_interface; - - print_verbose("XR: Primary interface set to: " + primary_interface->get_name()); -}; - -void XRServer::clear_primary_interface_if(const Ref<XRInterface> &p_primary_interface) { - if (primary_interface == p_primary_interface) { + if (p_primary_interface.is_null()) { print_verbose("XR: Clearing primary interface"); primary_interface.unref(); - }; + } else { + primary_interface = p_primary_interface; + + print_verbose("XR: Primary interface set to: " + primary_interface->get_name()); + } }; uint64_t XRServer::get_last_process_usec() { diff --git a/servers/xr_server.h b/servers/xr_server.h index 25431844c2..af183e175d 100644 --- a/servers/xr_server.h +++ b/servers/xr_server.h @@ -159,7 +159,6 @@ public: */ Ref<XRInterface> get_primary_interface() const; void set_primary_interface(const Ref<XRInterface> &p_primary_interface); - void clear_primary_interface_if(const Ref<XRInterface> &p_primary_interface); /* this is automatically called if an interface destructs */ /* Our trackers are objects that expose the orientation and position of physical devices such as controller, anchor points, etc. |