summaryrefslogtreecommitdiff
path: root/servers
diff options
context:
space:
mode:
Diffstat (limited to 'servers')
-rw-r--r--servers/audio/audio_stream.cpp45
-rw-r--r--servers/audio/audio_stream.h16
-rw-r--r--servers/audio/effects/audio_stream_generator.cpp3
-rw-r--r--servers/audio/effects/audio_stream_generator.h2
-rw-r--r--servers/audio_server.cpp492
-rw-r--r--servers/audio_server.h85
-rw-r--r--servers/physics_2d/area_pair_2d_sw.cpp67
-rw-r--r--servers/physics_2d/area_pair_2d_sw.h6
-rw-r--r--servers/physics_2d/body_pair_2d_sw.cpp4
-rw-r--r--servers/physics_2d/collision_solver_2d_sat.cpp18
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.cpp92
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.h8
-rw-r--r--servers/physics_2d/physics_server_2d_sw.cpp11
-rw-r--r--servers/physics_2d/physics_server_2d_sw.h3
-rw-r--r--servers/physics_2d/physics_server_2d_wrap_mt.h5
-rw-r--r--servers/physics_2d/shape_2d_sw.cpp55
-rw-r--r--servers/physics_2d/shape_2d_sw.h47
-rw-r--r--servers/physics_2d/space_2d_sw.cpp20
-rw-r--r--servers/physics_2d/space_2d_sw.h2
-rw-r--r--servers/physics_3d/area_pair_3d_sw.cpp69
-rw-r--r--servers/physics_3d/area_pair_3d_sw.h6
-rw-r--r--servers/physics_3d/collision_solver_3d_sat.cpp6
-rw-r--r--servers/physics_3d/collision_solver_3d_sw.cpp87
-rw-r--r--servers/physics_3d/collision_solver_3d_sw.h8
-rw-r--r--servers/physics_3d/physics_server_3d_sw.cpp10
-rw-r--r--servers/physics_3d/physics_server_3d_sw.h3
-rw-r--r--servers/physics_3d/physics_server_3d_wrap_mt.h5
-rw-r--r--servers/physics_3d/shape_3d_sw.cpp116
-rw-r--r--servers/physics_3d/shape_3d_sw.h64
-rw-r--r--servers/physics_3d/space_3d_sw.cpp14
-rw-r--r--servers/physics_3d/space_3d_sw.h2
-rw-r--r--servers/physics_server_2d.cpp8
-rw-r--r--servers/physics_server_2d.h6
-rw-r--r--servers/physics_server_3d.cpp10
-rw-r--r--servers/physics_server_3d.h6
-rw-r--r--servers/register_server_types.cpp2
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp34
-rw-r--r--servers/rendering/renderer_canvas_cull.h1
-rw-r--r--servers/rendering/renderer_canvas_render.h5
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp12
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp8
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h8
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp10
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl37
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/giprobe_write.glsl47
-rw-r--r--servers/rendering/renderer_rd/shaders/voxel_gi.glsl162
-rw-r--r--servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl5
-rw-r--r--servers/rendering/renderer_viewport.cpp84
-rw-r--r--servers/rendering/renderer_viewport.h6
-rw-r--r--servers/rendering/rendering_device.h1
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering_server.cpp13
-rw-r--r--servers/rendering_server.h12
-rw-r--r--servers/text_server.cpp223
-rw-r--r--servers/text_server.h221
-rw-r--r--servers/xr/xr_interface.cpp70
-rw-r--r--servers/xr/xr_interface.h27
-rw-r--r--servers/xr/xr_interface_extension.cpp241
-rw-r--r--servers/xr/xr_interface_extension.h105
-rw-r--r--servers/xr_server.cpp16
-rw-r--r--servers/xr_server.h1
62 files changed, 2025 insertions, 731 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_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_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_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/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp
index 88c097453e..813e5ddc17 100644
--- a/servers/physics_2d/physics_server_2d_sw.cpp
+++ b/servers/physics_2d/physics_server_2d_sw.cpp
@@ -45,6 +45,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 +82,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);
}
@@ -939,7 +946,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,7 +954,7 @@ 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) {
diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h
index 3610f43f93..c5b6d550d7 100644
--- a/servers/physics_2d/physics_server_2d_sw.h
+++ b/servers/physics_2d/physics_server_2d_sw.h
@@ -88,6 +88,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;
@@ -246,7 +247,7 @@ public:
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..05d4ac64b0 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)
@@ -252,9 +253,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..c0493749e0 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -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);
@@ -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);
diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h
index 3be36852b0..392df9b025 100644
--- a/servers/physics_2d/space_2d_sw.h
+++ b/servers/physics_2d/space_2d_sw.h
@@ -183,7 +183,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_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/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..fbc6f7eee8 100644
--- a/servers/physics_3d/physics_server_3d_sw.cpp
+++ b/servers/physics_3d/physics_server_3d_sw.cpp
@@ -48,6 +48,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);
@@ -848,7 +854,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,7 +862,7 @@ 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) {
diff --git a/servers/physics_3d/physics_server_3d_sw.h b/servers/physics_3d/physics_server_3d_sw.h
index 6e59a77e89..a18593c90c 100644
--- a/servers/physics_3d/physics_server_3d_sw.h
+++ b/servers/physics_3d/physics_server_3d_sw.h
@@ -83,6 +83,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;
@@ -241,7 +242,7 @@ public:
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..674c52d4a3 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)
@@ -249,9 +250,9 @@ public:
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..b81d3272c3 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;
@@ -1790,7 +1882,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;
}
@@ -1826,13 +1918,17 @@ void HeightMapShape3DSW::cull(const AABB &p_local_aabb, Callback p_callback, voi
_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);
+ 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);
+ if (p_callback(p_userdata, &face)) {
+ return;
+ }
}
}
}
diff --git a/servers/physics_3d/shape_3d_sw.h b/servers/physics_3d/shape_3d_sw.h
index ca58900111..b05f65f268 100644
--- a/servers/physics_3d/shape_3d_sw.h
+++ b/servers/physics_3d/shape_3d_sw.h
@@ -101,10 +101,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 +119,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 +325,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 +351,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 +369,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;
@@ -382,7 +412,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/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp
index f9e55ad525..37dee436df 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;
diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h
index 9b5b4de069..1c3d1cf9f6 100644
--- a/servers/physics_3d/space_3d_sw.h
+++ b/servers/physics_3d/space_3d_sw.h
@@ -203,7 +203,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_server_2d.cpp b/servers/physics_server_2d.cpp
index c96f85446c..4660b83f4d 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -500,7 +500,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 +509,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);
@@ -635,7 +636,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 +676,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);
diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h
index 4c559dd7bd..9afac0cbd1 100644
--- a/servers/physics_server_2d.h
+++ b/servers/physics_server_2d.h
@@ -210,7 +210,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 +220,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 +231,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;
@@ -485,7 +487,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..6cde4977f4 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -447,7 +447,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 +456,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 +490,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);
@@ -609,7 +612,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 +751,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);
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 5201e76498..8f83e360f2 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -212,7 +212,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 +222,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 +237,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;
@@ -497,7 +499,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_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 926d599c84..1b4052b622 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;
@@ -497,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
@@ -518,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))) {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 3af5047854..6267f684e3 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;
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_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index acda68abb7..fa66ed85a9 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -2591,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;
@@ -2637,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
@@ -2660,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/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/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..3ede9fed2d 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,7 +619,7 @@ 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
@@ -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..c05ff78718 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", "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..e1519d1463
--- /dev/null
+++ b/servers/xr/xr_interface_extension.cpp
@@ -0,0 +1,241 @@
+/*************************************************************************/
+/* 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", "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);
+
+ 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, Rect2i p_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.rect = p_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..ab4d90bfe6
--- /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, Rect2i p_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.