feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

39
engine/servers/SCsub Normal file
View file

@ -0,0 +1,39 @@
#!/usr/bin/env python
Import("env")
env.servers_sources = []
env.add_source_files(env.servers_sources, "audio_server.cpp")
env.add_source_files(env.servers_sources, "camera_server.cpp")
env.add_source_files(env.servers_sources, "display_server.cpp")
env.add_source_files(env.servers_sources, "navigation_server_2d.cpp")
env.add_source_files(env.servers_sources, "navigation_server_3d.cpp")
env.add_source_files(env.servers_sources, "physics_server_2d.cpp")
env.add_source_files(env.servers_sources, "physics_server_2d_wrap_mt.cpp")
env.add_source_files(env.servers_sources, "register_server_types.cpp")
env.add_source_files(env.servers_sources, "rendering_server.cpp")
env.add_source_files(env.servers_sources, "text_server.cpp")
SConscript("audio/SCsub")
SConscript("camera/SCsub")
SConscript("debugger/SCsub")
SConscript("display/SCsub")
SConscript("extensions/SCsub")
SConscript("movie_writer/SCsub")
SConscript("navigation/SCsub")
SConscript("rendering/SCsub")
SConscript("text/SCsub")
SConscript("physics_2d/SCsub")
if not env["disable_3d"]:
SConscript("physics_3d/SCsub")
env.add_source_files(env.servers_sources, "physics_server_3d.cpp")
env.add_source_files(env.servers_sources, "physics_server_3d_wrap_mt.cpp")
SConscript("xr/SCsub")
env.add_source_files(env.servers_sources, "xr_server.cpp")
lib = env.add_library("servers", env.servers_sources)
env.Prepend(LIBS=[lib])

View file

@ -0,0 +1,7 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")
SConscript("effects/SCsub")

View file

@ -0,0 +1,151 @@
/**************************************************************************/
/* audio_driver_dummy.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_driver_dummy.h"
#include "core/config/project_settings.h"
#include "core/os/os.h"
AudioDriverDummy *AudioDriverDummy::singleton = nullptr;
Error AudioDriverDummy::init() {
active.clear();
exit_thread.clear();
samples_in = nullptr;
if (mix_rate == -1) {
mix_rate = _get_configured_mix_rate();
}
channels = get_channels();
samples_in = memnew_arr(int32_t, (size_t)buffer_frames * channels);
if (use_threads) {
thread.start(AudioDriverDummy::thread_func, this);
}
return OK;
}
void AudioDriverDummy::thread_func(void *p_udata) {
AudioDriverDummy *ad = static_cast<AudioDriverDummy *>(p_udata);
uint64_t usdelay = (ad->buffer_frames / float(ad->mix_rate)) * 1000000;
while (!ad->exit_thread.is_set()) {
if (ad->active.is_set()) {
ad->lock();
ad->start_counting_ticks();
ad->audio_server_process(ad->buffer_frames, ad->samples_in);
ad->stop_counting_ticks();
ad->unlock();
}
OS::get_singleton()->delay_usec(usdelay);
}
}
void AudioDriverDummy::start() {
active.set();
}
int AudioDriverDummy::get_mix_rate() const {
return mix_rate;
}
AudioDriver::SpeakerMode AudioDriverDummy::get_speaker_mode() const {
return speaker_mode;
}
void AudioDriverDummy::lock() {
mutex.lock();
}
void AudioDriverDummy::unlock() {
mutex.unlock();
}
void AudioDriverDummy::set_use_threads(bool p_use_threads) {
use_threads = p_use_threads;
}
void AudioDriverDummy::set_speaker_mode(SpeakerMode p_mode) {
speaker_mode = p_mode;
}
void AudioDriverDummy::set_mix_rate(int p_rate) {
mix_rate = p_rate;
}
uint32_t AudioDriverDummy::get_channels() const {
static const int channels_for_mode[4] = { 2, 4, 8, 16 };
return channels_for_mode[speaker_mode];
}
void AudioDriverDummy::mix_audio(int p_frames, int32_t *p_buffer) {
ERR_FAIL_COND(!active.is_set()); // If not active, should not mix.
ERR_FAIL_COND(use_threads == true); // If using threads, this will not work well.
uint32_t todo = p_frames;
while (todo) {
uint32_t to_mix = MIN(buffer_frames, todo);
lock();
audio_server_process(to_mix, samples_in);
unlock();
uint32_t total_samples = to_mix * channels;
for (uint32_t i = 0; i < total_samples; i++) {
p_buffer[i] = samples_in[i];
}
todo -= to_mix;
p_buffer += total_samples;
}
}
void AudioDriverDummy::finish() {
if (use_threads) {
exit_thread.set();
if (thread.is_started()) {
thread.wait_to_finish();
}
}
if (samples_in) {
memdelete_arr(samples_in);
}
}
AudioDriverDummy::AudioDriverDummy() {
singleton = this;
}

View file

@ -0,0 +1,89 @@
/**************************************************************************/
/* audio_driver_dummy.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_DRIVER_DUMMY_H
#define AUDIO_DRIVER_DUMMY_H
#include "servers/audio_server.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
class AudioDriverDummy : public AudioDriver {
Thread thread;
Mutex mutex;
int32_t *samples_in = nullptr;
static void thread_func(void *p_udata);
uint32_t buffer_frames = 4096;
int32_t mix_rate = -1;
SpeakerMode speaker_mode = SPEAKER_MODE_STEREO;
int channels;
SafeFlag active;
SafeFlag exit_thread;
bool use_threads = true;
static AudioDriverDummy *singleton;
public:
virtual const char *get_name() const override {
return "Dummy";
};
virtual Error init() override;
virtual void start() override;
virtual int get_mix_rate() const override;
virtual SpeakerMode get_speaker_mode() const override;
virtual void lock() override;
virtual void unlock() override;
virtual void finish() override;
void set_use_threads(bool p_use_threads);
void set_speaker_mode(SpeakerMode p_mode);
void set_mix_rate(int p_rate);
uint32_t get_channels() const;
void mix_audio(int p_frames, int32_t *p_buffer);
static AudioDriverDummy *get_dummy_singleton() { return singleton; }
AudioDriverDummy();
~AudioDriverDummy() {}
};
#endif // AUDIO_DRIVER_DUMMY_H

View file

@ -0,0 +1,59 @@
/**************************************************************************/
/* audio_effect.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect.h"
void AudioEffectInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
GDVIRTUAL_REQUIRED_CALL(_process, p_src_frames, p_dst_frames, p_frame_count);
}
bool AudioEffectInstance::process_silence() const {
bool ret = false;
GDVIRTUAL_CALL(_process_silence, ret);
return ret;
}
void AudioEffectInstance::_bind_methods() {
GDVIRTUAL_BIND(_process, "src_buffer", "dst_buffer", "frame_count");
GDVIRTUAL_BIND(_process_silence);
}
////
Ref<AudioEffectInstance> AudioEffect::instantiate() {
Ref<AudioEffectInstance> ret;
GDVIRTUAL_REQUIRED_CALL(_instantiate, ret);
return ret;
}
void AudioEffect::_bind_methods() {
GDVIRTUAL_BIND(_instantiate);
}
AudioEffect::AudioEffect() {
}

View file

@ -0,0 +1,64 @@
/**************************************************************************/
/* audio_effect.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_H
#define AUDIO_EFFECT_H
#include "core/io/resource.h"
#include "core/math/audio_frame.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
class AudioEffectInstance : public RefCounted {
GDCLASS(AudioEffectInstance, RefCounted);
protected:
GDVIRTUAL3(_process, GDExtensionConstPtr<AudioFrame>, GDExtensionPtr<AudioFrame>, int)
GDVIRTUAL0RC(bool, _process_silence)
static void _bind_methods();
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count);
virtual bool process_silence() const;
};
class AudioEffect : public Resource {
GDCLASS(AudioEffect, Resource);
protected:
GDVIRTUAL0R(Ref<AudioEffectInstance>, _instantiate)
static void _bind_methods();
public:
virtual Ref<AudioEffectInstance> instantiate();
AudioEffect();
};
#endif // AUDIO_EFFECT_H

View file

@ -0,0 +1,260 @@
/**************************************************************************/
/* audio_filter_sw.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_filter_sw.h"
void AudioFilterSW::set_mode(Mode p_mode) {
mode = p_mode;
}
void AudioFilterSW::set_cutoff(float p_cutoff) {
cutoff = p_cutoff;
}
void AudioFilterSW::set_resonance(float p_resonance) {
resonance = p_resonance;
}
void AudioFilterSW::set_gain(float p_gain) {
gain = p_gain;
}
void AudioFilterSW::set_sampling_rate(float p_srate) {
sampling_rate = p_srate;
}
void AudioFilterSW::prepare_coefficients(Coeffs *p_coeffs) {
int sr_limit = (sampling_rate / 2) + 512;
double final_cutoff = (cutoff > sr_limit) ? sr_limit : cutoff;
if (final_cutoff < 1) {
final_cutoff = 1; //don't allow less than this
}
double omega = Math_TAU * final_cutoff / sampling_rate;
double sin_v = Math::sin(omega);
double cos_v = Math::cos(omega);
double Q = resonance;
if (Q <= 0.0) {
Q = 0.0001;
}
if (mode == BANDPASS) {
Q *= 2.0;
} else if (mode == PEAK) {
Q *= 3.0;
}
double tmpgain = gain;
if (tmpgain < 0.001) {
tmpgain = 0.001;
}
if (stages > 1) {
Q = (Q > 1.0 ? Math::pow(Q, 1.0 / stages) : Q);
tmpgain = Math::pow(tmpgain, 1.0 / (stages + 1));
}
double alpha = sin_v / (2 * Q);
double a0 = 1.0 + alpha;
switch (mode) {
case LOWPASS: {
p_coeffs->b0 = (1.0 - cos_v) / 2.0;
p_coeffs->b1 = 1.0 - cos_v;
p_coeffs->b2 = (1.0 - cos_v) / 2.0;
p_coeffs->a1 = -2.0 * cos_v;
p_coeffs->a2 = 1.0 - alpha;
} break;
case HIGHPASS: {
p_coeffs->b0 = (1.0 + cos_v) / 2.0;
p_coeffs->b1 = -(1.0 + cos_v);
p_coeffs->b2 = (1.0 + cos_v) / 2.0;
p_coeffs->a1 = -2.0 * cos_v;
p_coeffs->a2 = 1.0 - alpha;
} break;
case BANDPASS: {
p_coeffs->b0 = alpha * sqrt(Q + 1);
p_coeffs->b1 = 0.0;
p_coeffs->b2 = -alpha * sqrt(Q + 1);
p_coeffs->a1 = -2.0 * cos_v;
p_coeffs->a2 = 1.0 - alpha;
} break;
case NOTCH: {
p_coeffs->b0 = 1.0;
p_coeffs->b1 = -2.0 * cos_v;
p_coeffs->b2 = 1.0;
p_coeffs->a1 = -2.0 * cos_v;
p_coeffs->a2 = 1.0 - alpha;
} break;
case PEAK: {
p_coeffs->b0 = (1.0 + alpha * tmpgain);
p_coeffs->b1 = (-2.0 * cos_v);
p_coeffs->b2 = (1.0 - alpha * tmpgain);
p_coeffs->a1 = -2 * cos_v;
p_coeffs->a2 = (1 - alpha / tmpgain);
} break;
case BANDLIMIT: {
//this one is extra tricky
double hicutoff = resonance;
double centercutoff = (cutoff + resonance) / 2.0;
double bandwidth = (Math::log(centercutoff) - Math::log(hicutoff)) / Math::log((double)2);
omega = Math_TAU * centercutoff / sampling_rate;
alpha = Math::sin(omega) * Math::sinh(Math::log((double)2) / 2 * bandwidth * omega / Math::sin(omega));
a0 = 1 + alpha;
p_coeffs->b0 = alpha;
p_coeffs->b1 = 0;
p_coeffs->b2 = -alpha;
p_coeffs->a1 = -2 * Math::cos(omega);
p_coeffs->a2 = 1 - alpha;
} break;
case LOWSHELF: {
double tmpq = Math::sqrt(Q);
if (tmpq <= 0) {
tmpq = 0.001;
}
double beta = Math::sqrt(tmpgain) / tmpq;
a0 = (tmpgain + 1.0) + (tmpgain - 1.0) * cos_v + beta * sin_v;
p_coeffs->b0 = tmpgain * ((tmpgain + 1.0) - (tmpgain - 1.0) * cos_v + beta * sin_v);
p_coeffs->b1 = 2.0 * tmpgain * ((tmpgain - 1.0) - (tmpgain + 1.0) * cos_v);
p_coeffs->b2 = tmpgain * ((tmpgain + 1.0) - (tmpgain - 1.0) * cos_v - beta * sin_v);
p_coeffs->a1 = -2.0 * ((tmpgain - 1.0) + (tmpgain + 1.0) * cos_v);
p_coeffs->a2 = ((tmpgain + 1.0) + (tmpgain - 1.0) * cos_v - beta * sin_v);
} break;
case HIGHSHELF: {
double tmpq = Math::sqrt(Q);
if (tmpq <= 0) {
tmpq = 0.001;
}
double beta = Math::sqrt(tmpgain) / tmpq;
a0 = (tmpgain + 1.0) - (tmpgain - 1.0) * cos_v + beta * sin_v;
p_coeffs->b0 = tmpgain * ((tmpgain + 1.0) + (tmpgain - 1.0) * cos_v + beta * sin_v);
p_coeffs->b1 = -2.0 * tmpgain * ((tmpgain - 1.0) + (tmpgain + 1.0) * cos_v);
p_coeffs->b2 = tmpgain * ((tmpgain + 1.0) + (tmpgain - 1.0) * cos_v - beta * sin_v);
p_coeffs->a1 = 2.0 * ((tmpgain - 1.0) - (tmpgain + 1.0) * cos_v);
p_coeffs->a2 = ((tmpgain + 1.0) - (tmpgain - 1.0) * cos_v - beta * sin_v);
} break;
}
p_coeffs->b0 /= a0;
p_coeffs->b1 /= a0;
p_coeffs->b2 /= a0;
p_coeffs->a1 /= 0.0 - a0;
p_coeffs->a2 /= 0.0 - a0;
}
void AudioFilterSW::set_stages(int p_stages) {
stages = p_stages;
}
/* Fourier transform kernel to obtain response */
float AudioFilterSW::get_response(float p_freq, Coeffs *p_coeffs) {
float freq = p_freq / sampling_rate * Math_TAU;
float cx = p_coeffs->b0, cy = 0.0;
cx += cos(freq) * p_coeffs->b1;
cy -= sin(freq) * p_coeffs->b1;
cx += cos(2 * freq) * p_coeffs->b2;
cy -= sin(2 * freq) * p_coeffs->b2;
float H = cx * cx + cy * cy;
cx = 1.0;
cy = 0.0;
cx -= cos(freq) * p_coeffs->a1;
cy += sin(freq) * p_coeffs->a1;
cx -= cos(2 * freq) * p_coeffs->a2;
cy += sin(2 * freq) * p_coeffs->a2;
H = H / (cx * cx + cy * cy);
return H;
}
AudioFilterSW::Processor::Processor() {
set_filter(nullptr);
}
void AudioFilterSW::Processor::set_filter(AudioFilterSW *p_filter, bool p_clear_history) {
if (p_clear_history) {
ha1 = ha2 = hb1 = hb2 = 0;
}
filter = p_filter;
}
void AudioFilterSW::Processor::update_coeffs(int p_interp_buffer_len) {
if (!filter) {
return;
}
if (p_interp_buffer_len) { //interpolate
Coeffs old_coeffs = coeffs;
filter->prepare_coefficients(&coeffs);
incr_coeffs.a1 = (coeffs.a1 - old_coeffs.a1) / p_interp_buffer_len;
incr_coeffs.a2 = (coeffs.a2 - old_coeffs.a2) / p_interp_buffer_len;
incr_coeffs.b0 = (coeffs.b0 - old_coeffs.b0) / p_interp_buffer_len;
incr_coeffs.b1 = (coeffs.b1 - old_coeffs.b1) / p_interp_buffer_len;
incr_coeffs.b2 = (coeffs.b2 - old_coeffs.b2) / p_interp_buffer_len;
coeffs = old_coeffs;
} else {
filter->prepare_coefficients(&coeffs);
}
}
void AudioFilterSW::Processor::process(float *p_samples, int p_amount, int p_stride, bool p_interpolate) {
if (!filter) {
return;
}
if (p_interpolate) {
for (int i = 0; i < p_amount; i++) {
process_one_interp(*p_samples);
p_samples += p_stride;
}
} else {
for (int i = 0; i < p_amount; i++) {
process_one(*p_samples);
p_samples += p_stride;
}
}
}

View file

@ -0,0 +1,126 @@
/**************************************************************************/
/* audio_filter_sw.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_FILTER_SW_H
#define AUDIO_FILTER_SW_H
#include "core/math/math_funcs.h"
class AudioFilterSW {
public:
struct Coeffs {
float a1 = 0.0f;
float a2 = 0.0f;
float b0 = 0.0f;
float b1 = 0.0f;
float b2 = 0.0f;
};
enum Mode {
BANDPASS,
HIGHPASS,
LOWPASS,
NOTCH,
PEAK,
BANDLIMIT,
LOWSHELF,
HIGHSHELF
};
class Processor { // Simple filter processor.
AudioFilterSW *filter = nullptr;
Coeffs coeffs;
// History.
float ha1 = 0.0f;
float ha2 = 0.0f;
float hb1 = 0.0f;
float hb2 = 0.0f;
Coeffs incr_coeffs;
public:
void set_filter(AudioFilterSW *p_filter, bool p_clear_history = true);
void process(float *p_samples, int p_amount, int p_stride = 1, bool p_interpolate = false);
void update_coeffs(int p_interp_buffer_len = 0);
_ALWAYS_INLINE_ void process_one(float &p_sample);
_ALWAYS_INLINE_ void process_one_interp(float &p_sample);
Processor();
};
private:
float cutoff = 5000.0f;
float resonance = 0.5f;
float gain = 1.0f;
float sampling_rate = 44100.0f;
int stages = 1;
Mode mode = LOWPASS;
public:
float get_response(float p_freq, Coeffs *p_coeffs);
void set_mode(Mode p_mode);
void set_cutoff(float p_cutoff);
void set_resonance(float p_resonance);
void set_gain(float p_gain);
void set_sampling_rate(float p_srate);
void set_stages(int p_stages); //adjust for multiple stages
void prepare_coefficients(Coeffs *p_coeffs);
AudioFilterSW() {}
};
/* inline methods */
void AudioFilterSW::Processor::process_one(float &p_sample) {
float pre = p_sample;
p_sample = (p_sample * coeffs.b0 + hb1 * coeffs.b1 + hb2 * coeffs.b2 + ha1 * coeffs.a1 + ha2 * coeffs.a2);
ha2 = ha1;
hb2 = hb1;
hb1 = pre;
ha1 = p_sample;
}
void AudioFilterSW::Processor::process_one_interp(float &p_sample) {
float pre = p_sample;
p_sample = (p_sample * coeffs.b0 + hb1 * coeffs.b1 + hb2 * coeffs.b2 + ha1 * coeffs.a1 + ha2 * coeffs.a2);
ha2 = ha1;
hb2 = hb1;
hb1 = pre;
ha1 = p_sample;
coeffs.b0 += incr_coeffs.b0;
coeffs.b1 += incr_coeffs.b1;
coeffs.b2 += incr_coeffs.b2;
coeffs.a1 += incr_coeffs.a1;
coeffs.a2 += incr_coeffs.a2;
}
#endif // AUDIO_FILTER_SW_H

View file

@ -0,0 +1,235 @@
/**************************************************************************/
/* audio_rb_resampler.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_rb_resampler.h"
#include "core/math/math_funcs.h"
#include "core/os/os.h"
#include "servers/audio_server.h"
int AudioRBResampler::get_channel_count() const {
if (!rb) {
return 0;
}
return channels;
}
// Linear interpolation based sample rate conversion (low quality)
// Note that AudioStreamPlaybackResampled::mix has better algorithm,
// but it wasn't obvious to integrate that with VideoStreamPlayer
template <int C>
uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_increment) {
uint32_t read = offset & MIX_FRAC_MASK;
for (int i = 0; i < p_todo; i++) {
offset = (offset + p_increment) & (((1 << (rb_bits + MIX_FRAC_BITS)) - 1));
read += p_increment;
uint32_t pos = offset >> MIX_FRAC_BITS;
float frac = float(offset & MIX_FRAC_MASK) / float(MIX_FRAC_LEN);
ERR_FAIL_COND_V(pos >= rb_len, 0);
uint32_t pos_next = (pos + 1) & rb_mask;
// since this is a template with a known compile time value (C), conditionals go away when compiling.
if constexpr (C == 1) {
float v0 = rb[pos];
float v0n = rb[pos_next];
v0 += (v0n - v0) * frac;
p_dest[i] = AudioFrame(v0, v0);
}
if constexpr (C == 2) {
float v0 = rb[(pos << 1) + 0];
float v1 = rb[(pos << 1) + 1];
float v0n = rb[(pos_next << 1) + 0];
float v1n = rb[(pos_next << 1) + 1];
v0 += (v0n - v0) * frac;
v1 += (v1n - v1) * frac;
p_dest[i] = AudioFrame(v0, v1);
}
// This will probably never be used, but added anyway
if constexpr (C == 4) {
float v0 = rb[(pos << 2) + 0];
float v1 = rb[(pos << 2) + 1];
float v0n = rb[(pos_next << 2) + 0];
float v1n = rb[(pos_next << 2) + 1];
v0 += (v0n - v0) * frac;
v1 += (v1n - v1) * frac;
p_dest[i] = AudioFrame(v0, v1);
}
if constexpr (C == 6) {
float v0 = rb[(pos * 6) + 0];
float v1 = rb[(pos * 6) + 1];
float v0n = rb[(pos_next * 6) + 0];
float v1n = rb[(pos_next * 6) + 1];
v0 += (v0n - v0) * frac;
v1 += (v1n - v1) * frac;
p_dest[i] = AudioFrame(v0, v1);
}
}
return read >> MIX_FRAC_BITS; //rb_read_pos = offset >> MIX_FRAC_BITS;
}
bool AudioRBResampler::mix(AudioFrame *p_dest, int p_frames) {
if (!rb) {
return false;
}
int32_t increment = (src_mix_rate * MIX_FRAC_LEN) / target_mix_rate;
int read_space = get_reader_space();
int target_todo = MIN(get_num_of_ready_frames(), p_frames);
{
int src_read = 0;
switch (channels) {
case 1:
src_read = _resample<1>(p_dest, target_todo, increment);
break;
case 2:
src_read = _resample<2>(p_dest, target_todo, increment);
break;
case 4:
src_read = _resample<4>(p_dest, target_todo, increment);
break;
case 6:
src_read = _resample<6>(p_dest, target_todo, increment);
break;
}
if (src_read > read_space) {
src_read = read_space;
}
rb_read_pos.set((rb_read_pos.get() + src_read) & rb_mask);
// Create fadeout effect for the end of stream (note that it can be because of slow writer)
if (p_frames - target_todo > 0) {
for (int i = 0; i < target_todo; i++) {
p_dest[i] = p_dest[i] * float(target_todo - i) / float(target_todo);
}
}
// Fill zeros (silence) for the rest of frames
for (int i = target_todo; i < p_frames; i++) {
p_dest[i] = AudioFrame(0, 0);
}
}
return true;
}
int AudioRBResampler::get_num_of_ready_frames() {
if (!is_ready()) {
return 0;
}
int32_t increment = (src_mix_rate * MIX_FRAC_LEN) / target_mix_rate;
int read_space = get_reader_space();
return (int64_t(read_space) << MIX_FRAC_BITS) / increment;
}
Error AudioRBResampler::setup(int p_channels, int p_src_mix_rate, int p_target_mix_rate, int p_buffer_msec, int p_minbuff_needed) {
ERR_FAIL_COND_V(p_channels != 1 && p_channels != 2 && p_channels != 4 && p_channels != 6, ERR_INVALID_PARAMETER);
int desired_rb_bits = nearest_shift(MAX((p_buffer_msec / 1000.0) * p_src_mix_rate, p_minbuff_needed));
bool recreate = !rb;
if (rb && (uint32_t(desired_rb_bits) != rb_bits || channels != uint32_t(p_channels))) {
memdelete_arr(rb);
memdelete_arr(read_buf);
recreate = true;
}
if (recreate) {
channels = p_channels;
rb_bits = desired_rb_bits;
rb_len = (1 << rb_bits);
rb_mask = rb_len - 1;
const size_t array_size = rb_len * (size_t)p_channels;
rb = memnew_arr(float, array_size);
read_buf = memnew_arr(float, array_size);
}
src_mix_rate = p_src_mix_rate;
target_mix_rate = p_target_mix_rate;
offset = 0;
rb_read_pos.set(0);
rb_write_pos.set(0);
//avoid maybe strange noises upon load
for (unsigned int i = 0; i < (rb_len * channels); i++) {
rb[i] = 0;
read_buf[i] = 0;
}
return OK;
}
void AudioRBResampler::clear() {
if (!rb) {
return;
}
//should be stopped at this point but just in case
memdelete_arr(rb);
memdelete_arr(read_buf);
rb = nullptr;
offset = 0;
rb_read_pos.set(0);
rb_write_pos.set(0);
read_buf = nullptr;
}
AudioRBResampler::AudioRBResampler() {
rb = nullptr;
offset = 0;
read_buf = nullptr;
rb_read_pos.set(0);
rb_write_pos.set(0);
rb_bits = 0;
rb_len = 0;
rb_mask = 0;
read_buff_len = 0;
channels = 0;
src_mix_rate = 0;
target_mix_rate = 0;
}
AudioRBResampler::~AudioRBResampler() {
if (rb) {
memdelete_arr(rb);
memdelete_arr(read_buf);
}
}

View file

@ -0,0 +1,173 @@
/**************************************************************************/
/* audio_rb_resampler.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_RB_RESAMPLER_H
#define AUDIO_RB_RESAMPLER_H
#include "core/os/memory.h"
#include "core/templates/safe_refcount.h"
#include "core/typedefs.h"
#include "servers/audio_server.h"
struct AudioRBResampler {
uint32_t rb_bits;
uint32_t rb_len;
uint32_t rb_mask;
uint32_t read_buff_len;
uint32_t channels;
uint32_t src_mix_rate;
uint32_t target_mix_rate;
SafeNumeric<int> rb_read_pos;
SafeNumeric<int> rb_write_pos;
int32_t offset; //contains the fractional remainder of the resampler
enum {
MIX_FRAC_BITS = 13,
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
float *read_buf = nullptr;
float *rb = nullptr;
template <int C>
uint32_t _resample(AudioFrame *p_dest, int p_todo, int32_t p_increment);
public:
_FORCE_INLINE_ void flush() {
rb_read_pos.set(0);
rb_write_pos.set(0);
offset = 0;
}
_FORCE_INLINE_ bool is_ready() const {
return rb != nullptr;
}
_FORCE_INLINE_ int get_total() const {
return rb_len - 1;
}
_FORCE_INLINE_ int get_writer_space() const {
int space, r, w;
r = rb_read_pos.get();
w = rb_write_pos.get();
if (r == w) {
space = rb_len - 1;
} else if (w < r) {
space = r - w - 1;
} else {
space = (rb_len - r) + w - 1;
}
return space;
}
_FORCE_INLINE_ int get_reader_space() const {
int space, r, w;
r = rb_read_pos.get();
w = rb_write_pos.get();
if (r == w) {
space = 0;
} else if (w < r) {
space = rb_len - r + w;
} else {
space = w - r;
}
return space;
}
_FORCE_INLINE_ bool has_data() const {
return rb && rb_read_pos.get() != rb_write_pos.get();
}
_FORCE_INLINE_ float *get_write_buffer() { return read_buf; }
_FORCE_INLINE_ void write(uint32_t p_frames) {
ERR_FAIL_COND(p_frames >= rb_len);
int wp = rb_write_pos.get();
switch (channels) {
case 1: {
for (uint32_t i = 0; i < p_frames; i++) {
rb[wp] = read_buf[i];
wp = (wp + 1) & rb_mask;
}
} break;
case 2: {
for (uint32_t i = 0; i < p_frames; i++) {
rb[(wp << 1) + 0] = read_buf[(i << 1) + 0];
rb[(wp << 1) + 1] = read_buf[(i << 1) + 1];
wp = (wp + 1) & rb_mask;
}
} break;
case 4: {
for (uint32_t i = 0; i < p_frames; i++) {
rb[(wp << 2) + 0] = read_buf[(i << 2) + 0];
rb[(wp << 2) + 1] = read_buf[(i << 2) + 1];
rb[(wp << 2) + 2] = read_buf[(i << 2) + 2];
rb[(wp << 2) + 3] = read_buf[(i << 2) + 3];
wp = (wp + 1) & rb_mask;
}
} break;
case 6: {
for (uint32_t i = 0; i < p_frames; i++) {
rb[(wp * 6) + 0] = read_buf[(i * 6) + 0];
rb[(wp * 6) + 1] = read_buf[(i * 6) + 1];
rb[(wp * 6) + 2] = read_buf[(i * 6) + 2];
rb[(wp * 6) + 3] = read_buf[(i * 6) + 3];
rb[(wp * 6) + 4] = read_buf[(i * 6) + 4];
rb[(wp * 6) + 5] = read_buf[(i * 6) + 5];
wp = (wp + 1) & rb_mask;
}
} break;
}
rb_write_pos.set(wp);
}
int get_channel_count() const;
Error setup(int p_channels, int p_src_mix_rate, int p_target_mix_rate, int p_buffer_msec, int p_minbuff_needed = -1);
void clear();
bool mix(AudioFrame *p_dest, int p_frames);
int get_num_of_ready_frames();
AudioRBResampler();
~AudioRBResampler();
};
#endif // AUDIO_RB_RESAMPLER_H

View file

@ -0,0 +1,834 @@
/**************************************************************************/
/* audio_stream.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_stream.h"
#include "core/config/project_settings.h"
#include "core/os/os.h"
void AudioStreamPlayback::start(double p_from_pos) {
if (GDVIRTUAL_CALL(_start, p_from_pos)) {
return;
}
ERR_FAIL_MSG("AudioStreamPlayback::start unimplemented!");
}
void AudioStreamPlayback::stop() {
if (GDVIRTUAL_CALL(_stop)) {
return;
}
ERR_FAIL_MSG("AudioStreamPlayback::stop unimplemented!");
}
bool AudioStreamPlayback::is_playing() const {
bool ret;
if (GDVIRTUAL_CALL(_is_playing, ret)) {
return ret;
}
ERR_FAIL_V_MSG(false, "AudioStreamPlayback::is_playing unimplemented!");
}
int AudioStreamPlayback::get_loop_count() const {
int ret = 0;
GDVIRTUAL_CALL(_get_loop_count, ret);
return ret;
}
double AudioStreamPlayback::get_playback_position() const {
double ret;
if (GDVIRTUAL_CALL(_get_playback_position, ret)) {
return ret;
}
ERR_FAIL_V_MSG(0, "AudioStreamPlayback::get_playback_position unimplemented!");
}
void AudioStreamPlayback::seek(double p_time) {
GDVIRTUAL_CALL(_seek, p_time);
}
int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret);
return ret;
}
void AudioStreamPlayback::tag_used_streams() {
GDVIRTUAL_CALL(_tag_used_streams);
}
void AudioStreamPlayback::set_parameter(const StringName &p_name, const Variant &p_value) {
GDVIRTUAL_CALL(_set_parameter, p_name, p_value);
}
Variant AudioStreamPlayback::get_parameter(const StringName &p_name) const {
Variant ret;
GDVIRTUAL_CALL(_get_parameter, p_name, ret);
return ret;
}
Ref<AudioSamplePlayback> AudioStreamPlayback::get_sample_playback() const {
return nullptr;
}
void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_start, "from_pos")
GDVIRTUAL_BIND(_stop)
GDVIRTUAL_BIND(_is_playing)
GDVIRTUAL_BIND(_get_loop_count)
GDVIRTUAL_BIND(_get_playback_position)
GDVIRTUAL_BIND(_seek, "position")
GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames");
GDVIRTUAL_BIND(_tag_used_streams);
GDVIRTUAL_BIND(_set_parameter, "name", "value");
GDVIRTUAL_BIND(_get_parameter, "name");
ClassDB::bind_method(D_METHOD("set_sample_playback", "playback_sample"), &AudioStreamPlayback::set_sample_playback);
ClassDB::bind_method(D_METHOD("get_sample_playback"), &AudioStreamPlayback::get_sample_playback);
}
AudioStreamPlayback::AudioStreamPlayback() {}
AudioStreamPlayback::~AudioStreamPlayback() {
if (get_sample_playback().is_valid() && likely(AudioServer::get_singleton() != nullptr)) {
AudioServer::get_singleton()->stop_sample_playback(get_sample_playback());
}
}
//////////////////////////////
void AudioStreamPlaybackResampled::begin_resample() {
//clear cubic interpolation history
internal_buffer[0] = AudioFrame(0.0, 0.0);
internal_buffer[1] = AudioFrame(0.0, 0.0);
internal_buffer[2] = AudioFrame(0.0, 0.0);
internal_buffer[3] = AudioFrame(0.0, 0.0);
//mix buffer
_mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN);
mix_offset = 0;
}
int AudioStreamPlaybackResampled::_mix_internal(AudioFrame *p_buffer, int p_frames) {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_mix_resampled, p_buffer, p_frames, ret);
return ret;
}
float AudioStreamPlaybackResampled::get_stream_sampling_rate() {
float ret = 0;
GDVIRTUAL_REQUIRED_CALL(_get_stream_sampling_rate, ret);
return ret;
}
void AudioStreamPlaybackResampled::_bind_methods() {
ClassDB::bind_method(D_METHOD("begin_resample"), &AudioStreamPlaybackResampled::begin_resample);
GDVIRTUAL_BIND(_mix_resampled, "dst_buffer", "frame_count");
GDVIRTUAL_BIND(_get_stream_sampling_rate);
}
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 = -1;
int i;
for (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)
//this used to be moved to a LUT for greater performance, but nowadays CPU speed is generally faster than memory.
float mu = (mix_offset & FP_MASK) / float(FP_LEN);
AudioFrame y0 = internal_buffer[idx - 3];
AudioFrame y1 = internal_buffer[idx - 2];
AudioFrame y2 = internal_buffer[idx - 1];
AudioFrame y3 = internal_buffer[idx - 0];
if (idx >= internal_buffer_end && mixed_frames_total == -1) {
// 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;
AudioFrame a2 = y2 - y0;
AudioFrame a3 = 2 * y1;
p_buffer[i] = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3) / 2;
mix_offset += mix_increment;
while ((mix_offset >> FP_BITS) >= INTERNAL_BUFFER_LEN) {
internal_buffer[0] = internal_buffer[INTERNAL_BUFFER_LEN + 0];
internal_buffer[1] = internal_buffer[INTERNAL_BUFFER_LEN + 1];
internal_buffer[2] = internal_buffer[INTERNAL_BUFFER_LEN + 2];
internal_buffer[3] = internal_buffer[INTERNAL_BUFFER_LEN + 3];
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;
}
mix_offset -= (INTERNAL_BUFFER_LEN << FP_BITS);
}
}
if (mixed_frames_total == -1 && i == p_frames) {
mixed_frames_total = p_frames;
}
return mixed_frames_total;
}
////////////////////////////////
Ref<AudioStreamPlayback> AudioStream::instantiate_playback() {
Ref<AudioStreamPlayback> ret;
if (GDVIRTUAL_CALL(_instantiate_playback, ret)) {
return ret;
}
ERR_FAIL_V_MSG(Ref<AudioStreamPlayback>(), "Method must be implemented!");
}
String AudioStream::get_stream_name() const {
String ret;
GDVIRTUAL_CALL(_get_stream_name, ret);
return ret;
}
double AudioStream::get_length() const {
double ret = 0;
GDVIRTUAL_CALL(_get_length, ret);
return ret;
}
bool AudioStream::is_monophonic() const {
bool ret = true;
GDVIRTUAL_CALL(_is_monophonic, ret);
return ret;
}
double AudioStream::get_bpm() const {
double ret = 0;
GDVIRTUAL_CALL(_get_bpm, ret);
return ret;
}
bool AudioStream::has_loop() const {
bool ret = 0;
GDVIRTUAL_CALL(_has_loop, ret);
return ret;
}
int AudioStream::get_bar_beats() const {
int ret = 0;
GDVIRTUAL_CALL(_get_bar_beats, ret);
return ret;
}
int AudioStream::get_beat_count() const {
int ret = 0;
GDVIRTUAL_CALL(_get_beat_count, ret);
return ret;
}
void AudioStream::tag_used(float p_offset) {
if (tagged_frame != AudioServer::get_singleton()->get_mixed_frames()) {
offset_count = 0;
tagged_frame = AudioServer::get_singleton()->get_mixed_frames();
}
if (offset_count < MAX_TAGGED_OFFSETS) {
tagged_offsets[offset_count++] = p_offset;
}
}
uint64_t AudioStream::get_tagged_frame() const {
return tagged_frame;
}
uint32_t AudioStream::get_tagged_frame_count() const {
return offset_count;
}
float AudioStream::get_tagged_frame_offset(int p_index) const {
ERR_FAIL_INDEX_V(p_index, MAX_TAGGED_OFFSETS, 0);
return tagged_offsets[p_index];
}
void AudioStream::get_parameter_list(List<Parameter> *r_parameters) {
TypedArray<Dictionary> ret;
GDVIRTUAL_CALL(_get_parameter_list, ret);
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
ERR_CONTINUE(!d.has("default_value"));
r_parameters->push_back(Parameter(PropertyInfo::from_dict(d), d["default_value"]));
}
}
Ref<AudioSample> AudioStream::generate_sample() const {
ERR_FAIL_COND_V_MSG(!can_be_sampled(), nullptr, "Cannot generate a sample for a stream that cannot be sampled.");
Ref<AudioSample> sample;
sample.instantiate();
sample->stream = this;
return sample;
}
void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
ClassDB::bind_method(D_METHOD("instantiate_playback"), &AudioStream::instantiate_playback);
ClassDB::bind_method(D_METHOD("can_be_sampled"), &AudioStream::can_be_sampled);
ClassDB::bind_method(D_METHOD("generate_sample"), &AudioStream::generate_sample);
ClassDB::bind_method(D_METHOD("is_meta_stream"), &AudioStream::is_meta_stream);
GDVIRTUAL_BIND(_instantiate_playback);
GDVIRTUAL_BIND(_get_stream_name);
GDVIRTUAL_BIND(_get_length);
GDVIRTUAL_BIND(_is_monophonic);
GDVIRTUAL_BIND(_get_bpm)
GDVIRTUAL_BIND(_get_beat_count)
GDVIRTUAL_BIND(_get_parameter_list)
ADD_SIGNAL(MethodInfo("parameter_list_changed"));
}
////////////////////////////////
Ref<AudioStreamPlayback> AudioStreamMicrophone::instantiate_playback() {
Ref<AudioStreamPlaybackMicrophone> playback;
playback.instantiate();
playbacks.insert(playback.ptr());
playback->microphone = Ref<AudioStreamMicrophone>((AudioStreamMicrophone *)this);
playback->active = false;
return playback;
}
String AudioStreamMicrophone::get_stream_name() const {
//if (audio_stream.is_valid()) {
//return "Random: " + audio_stream->get_name();
//}
return "Microphone";
}
double AudioStreamMicrophone::get_length() const {
return 0;
}
bool AudioStreamMicrophone::is_monophonic() const {
return true;
}
void AudioStreamMicrophone::_bind_methods() {
}
AudioStreamMicrophone::AudioStreamMicrophone() {
}
int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
AudioDriver::get_singleton()->lock();
Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
unsigned int input_size = AudioDriver::get_singleton()->get_input_size();
int mix_rate = AudioDriver::get_singleton()->get_mix_rate();
unsigned int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1);
#ifdef DEBUG_ENABLED
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);
}
input_ofs = 0;
} else {
for (int i = 0; i < p_frames; i++) {
if (input_size > input_ofs && (int)input_ofs < buf.size()) {
float l = (buf[input_ofs++] >> 16) / 32768.f;
if ((int)input_ofs >= buf.size()) {
input_ofs = 0;
}
float r = (buf[input_ofs++] >> 16) / 32768.f;
if ((int)input_ofs >= buf.size()) {
input_ofs = 0;
}
p_buffer[i] = AudioFrame(l, r);
} else {
if (mixed_frames == p_frames) {
mixed_frames = i;
}
p_buffer[i] = AudioFrame(0.0f, 0.0f);
}
}
}
#ifdef DEBUG_ENABLED
if (input_ofs > input_position && (int)(input_ofs - input_position) < (p_frames * 2)) {
print_verbose(String(get_class_name()) + " buffer underrun: input_position=" + itos(input_position) + " input_ofs=" + itos(input_ofs) + " input_size=" + itos(input_size));
}
#endif
AudioDriver::get_singleton()->unlock();
return mixed_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() {
return AudioDriver::get_singleton()->get_mix_rate();
}
void AudioStreamPlaybackMicrophone::start(double p_from_pos) {
if (active) {
return;
}
if (!GLOBAL_GET("audio/driver/enable_input")) {
WARN_PRINT("You must enable the project setting \"audio/driver/enable_input\" to use audio capture.");
return;
}
input_ofs = 0;
if (AudioDriver::get_singleton()->input_start() == OK) {
active = true;
begin_resample();
}
}
void AudioStreamPlaybackMicrophone::stop() {
if (active) {
AudioDriver::get_singleton()->input_stop();
active = false;
}
}
bool AudioStreamPlaybackMicrophone::is_playing() const {
return active;
}
int AudioStreamPlaybackMicrophone::get_loop_count() const {
return 0;
}
double AudioStreamPlaybackMicrophone::get_playback_position() const {
return 0;
}
void AudioStreamPlaybackMicrophone::seek(double p_time) {
// Can't seek a microphone input
}
void AudioStreamPlaybackMicrophone::tag_used_streams() {
microphone->tag_used(0);
}
AudioStreamPlaybackMicrophone::~AudioStreamPlaybackMicrophone() {
microphone->playbacks.erase(this);
stop();
}
AudioStreamPlaybackMicrophone::AudioStreamPlaybackMicrophone() {
}
////////////////////////////////
void AudioStreamRandomizer::add_stream(int p_index, Ref<AudioStream> p_stream, float p_weight) {
if (p_index < 0) {
p_index = audio_stream_pool.size();
}
ERR_FAIL_COND(p_index > audio_stream_pool.size());
PoolEntry entry{ p_stream, p_weight };
audio_stream_pool.insert(p_index, entry);
emit_signal(CoreStringName(changed));
notify_property_list_changed();
}
// p_index_to is relative to the array prior to the removal of from.
// Example: [0, 1, 2, 3], move(1, 3) => [0, 2, 1, 3]
void AudioStreamRandomizer::move_stream(int p_index_from, int p_index_to) {
ERR_FAIL_INDEX(p_index_from, audio_stream_pool.size());
// p_index_to == audio_stream_pool.size() is valid (move to end).
ERR_FAIL_COND(p_index_to < 0);
ERR_FAIL_COND(p_index_to > audio_stream_pool.size());
audio_stream_pool.insert(p_index_to, audio_stream_pool[p_index_from]);
// If 'from' is strictly after 'to' we need to increment the index by one because of the insertion.
if (p_index_from > p_index_to) {
p_index_from++;
}
audio_stream_pool.remove_at(p_index_from);
emit_signal(CoreStringName(changed));
notify_property_list_changed();
}
void AudioStreamRandomizer::remove_stream(int p_index) {
ERR_FAIL_INDEX(p_index, audio_stream_pool.size());
audio_stream_pool.remove_at(p_index);
emit_signal(CoreStringName(changed));
notify_property_list_changed();
}
void AudioStreamRandomizer::set_stream(int p_index, Ref<AudioStream> p_stream) {
ERR_FAIL_INDEX(p_index, audio_stream_pool.size());
audio_stream_pool.write[p_index].stream = p_stream;
emit_signal(CoreStringName(changed));
}
Ref<AudioStream> AudioStreamRandomizer::get_stream(int p_index) const {
ERR_FAIL_INDEX_V(p_index, audio_stream_pool.size(), nullptr);
return audio_stream_pool[p_index].stream;
}
void AudioStreamRandomizer::set_stream_probability_weight(int p_index, float p_weight) {
ERR_FAIL_INDEX(p_index, audio_stream_pool.size());
audio_stream_pool.write[p_index].weight = p_weight;
emit_signal(CoreStringName(changed));
}
float AudioStreamRandomizer::get_stream_probability_weight(int p_index) const {
ERR_FAIL_INDEX_V(p_index, audio_stream_pool.size(), 0);
return audio_stream_pool[p_index].weight;
}
void AudioStreamRandomizer::set_streams_count(int p_count) {
audio_stream_pool.resize(p_count);
}
int AudioStreamRandomizer::get_streams_count() const {
return audio_stream_pool.size();
}
void AudioStreamRandomizer::set_random_pitch(float p_pitch) {
if (p_pitch < 1) {
p_pitch = 1;
}
random_pitch_scale = p_pitch;
}
float AudioStreamRandomizer::get_random_pitch() const {
return random_pitch_scale;
}
void AudioStreamRandomizer::set_random_volume_offset_db(float p_volume_offset_db) {
if (p_volume_offset_db < 0) {
p_volume_offset_db = 0;
}
random_volume_offset_db = p_volume_offset_db;
}
float AudioStreamRandomizer::get_random_volume_offset_db() const {
return random_volume_offset_db;
}
void AudioStreamRandomizer::set_playback_mode(PlaybackMode p_playback_mode) {
playback_mode = p_playback_mode;
}
AudioStreamRandomizer::PlaybackMode AudioStreamRandomizer::get_playback_mode() const {
return playback_mode;
}
Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() {
Ref<AudioStreamPlaybackRandomizer> playback;
playback.instantiate();
playbacks.insert(playback.ptr());
playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this);
double total_weight = 0;
Vector<PoolEntry> local_pool;
for (const PoolEntry &entry : audio_stream_pool) {
if (entry.stream.is_valid() && entry.weight > 0) {
local_pool.push_back(entry);
total_weight += entry.weight;
}
}
if (local_pool.is_empty()) {
return playback;
}
double chosen_cumulative_weight = Math::random(0.0, total_weight);
double cumulative_weight = 0;
for (PoolEntry &entry : local_pool) {
cumulative_weight += entry.weight;
if (cumulative_weight > chosen_cumulative_weight) {
playback->playback = entry.stream->instantiate_playback();
last_playback = entry.stream;
break;
}
}
if (playback->playback.is_null()) {
// This indicates a floating point error. Take the last element.
last_playback = local_pool[local_pool.size() - 1].stream;
playback->playback = local_pool.write[local_pool.size() - 1].stream->instantiate_playback();
}
return playback;
}
Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_no_repeats() {
Ref<AudioStreamPlaybackRandomizer> playback;
double total_weight = 0;
Vector<PoolEntry> local_pool;
for (const PoolEntry &entry : audio_stream_pool) {
if (entry.stream == last_playback) {
continue;
}
if (entry.stream.is_valid() && entry.weight > 0) {
local_pool.push_back(entry);
total_weight += entry.weight;
}
}
if (local_pool.is_empty()) {
// There is only one sound to choose from.
// Always play a random sound while allowing repeats (which always plays the same sound).
playback = instance_playback_random();
return playback;
}
playback.instantiate();
playbacks.insert(playback.ptr());
playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this);
double chosen_cumulative_weight = Math::random(0.0, total_weight);
double cumulative_weight = 0;
for (PoolEntry &entry : local_pool) {
cumulative_weight += entry.weight;
if (cumulative_weight > chosen_cumulative_weight) {
last_playback = entry.stream;
playback->playback = entry.stream->instantiate_playback();
break;
}
}
if (playback->playback.is_null()) {
// This indicates a floating point error. Take the last element.
last_playback = local_pool[local_pool.size() - 1].stream;
playback->playback = local_pool.write[local_pool.size() - 1].stream->instantiate_playback();
}
return playback;
}
Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() {
Ref<AudioStreamPlaybackRandomizer> playback;
playback.instantiate();
playbacks.insert(playback.ptr());
playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this);
Vector<Ref<AudioStream>> local_pool;
for (const PoolEntry &entry : audio_stream_pool) {
if (entry.stream.is_null()) {
continue;
}
if (local_pool.has(entry.stream)) {
WARN_PRINT("Duplicate stream in sequential playback pool");
continue;
}
local_pool.push_back(entry.stream);
}
if (local_pool.is_empty()) {
return playback;
}
bool found_last_stream = false;
for (Ref<AudioStream> &entry : local_pool) {
if (found_last_stream) {
last_playback = entry;
playback->playback = entry->instantiate_playback();
break;
}
if (entry == last_playback) {
found_last_stream = true;
}
}
if (playback->playback.is_null()) {
// Wrap around
last_playback = local_pool[0];
playback->playback = local_pool.write[0]->instantiate_playback();
}
return playback;
}
Ref<AudioStreamPlayback> AudioStreamRandomizer::instantiate_playback() {
switch (playback_mode) {
case PLAYBACK_RANDOM:
return instance_playback_random();
case PLAYBACK_RANDOM_NO_REPEATS:
return instance_playback_no_repeats();
case PLAYBACK_SEQUENTIAL:
return instance_playback_sequential();
default:
ERR_FAIL_V_MSG(nullptr, "Unhandled playback mode.");
}
}
String AudioStreamRandomizer::get_stream_name() const {
return "Randomizer";
}
double AudioStreamRandomizer::get_length() const {
return 0;
}
bool AudioStreamRandomizer::is_monophonic() const {
for (const PoolEntry &entry : audio_stream_pool) {
if (entry.stream.is_valid() && entry.stream->is_monophonic()) {
return true;
}
}
return false;
}
void AudioStreamRandomizer::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_stream", "index", "stream", "weight"), &AudioStreamRandomizer::add_stream, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("move_stream", "index_from", "index_to"), &AudioStreamRandomizer::move_stream);
ClassDB::bind_method(D_METHOD("remove_stream", "index"), &AudioStreamRandomizer::remove_stream);
ClassDB::bind_method(D_METHOD("set_stream", "index", "stream"), &AudioStreamRandomizer::set_stream);
ClassDB::bind_method(D_METHOD("get_stream", "index"), &AudioStreamRandomizer::get_stream);
ClassDB::bind_method(D_METHOD("set_stream_probability_weight", "index", "weight"), &AudioStreamRandomizer::set_stream_probability_weight);
ClassDB::bind_method(D_METHOD("get_stream_probability_weight", "index"), &AudioStreamRandomizer::get_stream_probability_weight);
ClassDB::bind_method(D_METHOD("set_streams_count", "count"), &AudioStreamRandomizer::set_streams_count);
ClassDB::bind_method(D_METHOD("get_streams_count"), &AudioStreamRandomizer::get_streams_count);
ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomizer::set_random_pitch);
ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomizer::get_random_pitch);
ClassDB::bind_method(D_METHOD("set_random_volume_offset_db", "db_offset"), &AudioStreamRandomizer::set_random_volume_offset_db);
ClassDB::bind_method(D_METHOD("get_random_volume_offset_db"), &AudioStreamRandomizer::get_random_volume_offset_db);
ClassDB::bind_method(D_METHOD("set_playback_mode", "mode"), &AudioStreamRandomizer::set_playback_mode);
ClassDB::bind_method(D_METHOD("get_playback_mode"), &AudioStreamRandomizer::get_playback_mode);
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_mode", PROPERTY_HINT_ENUM, "Random (Avoid Repeats),Random,Sequential"), "set_playback_mode", "get_playback_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_pitch", PROPERTY_HINT_RANGE, "1,16,0.01"), "set_random_pitch", "get_random_pitch");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_volume_offset_db", PROPERTY_HINT_RANGE, "0,40,0.01,suffix:dB"), "set_random_volume_offset_db", "get_random_volume_offset_db");
ADD_ARRAY("streams", "stream_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "streams_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_streams_count", "get_streams_count");
BIND_ENUM_CONSTANT(PLAYBACK_RANDOM_NO_REPEATS);
BIND_ENUM_CONSTANT(PLAYBACK_RANDOM);
BIND_ENUM_CONSTANT(PLAYBACK_SEQUENTIAL);
PoolEntry defaults;
base_property_helper.set_prefix("stream_");
base_property_helper.set_array_length_getter(&AudioStreamRandomizer::get_streams_count);
base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), defaults.stream, &AudioStreamRandomizer::set_stream, &AudioStreamRandomizer::get_stream);
base_property_helper.register_property(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), defaults.weight, &AudioStreamRandomizer::set_stream_probability_weight, &AudioStreamRandomizer::get_stream_probability_weight);
PropertyListHelper::register_base_helper(&base_property_helper);
}
AudioStreamRandomizer::AudioStreamRandomizer() {
property_helper.setup_for_instance(base_property_helper, this);
}
void AudioStreamPlaybackRandomizer::start(double p_from_pos) {
playing = playback;
{
float range_from = 1.0 / randomizer->random_pitch_scale;
float range_to = randomizer->random_pitch_scale;
pitch_scale = range_from + Math::randf() * (range_to - range_from);
}
{
float range_from = -randomizer->random_volume_offset_db;
float range_to = randomizer->random_volume_offset_db;
float volume_offset_db = range_from + Math::randf() * (range_to - range_from);
volume_scale = Math::db_to_linear(volume_offset_db);
}
if (playing.is_valid()) {
playing->start(p_from_pos);
}
}
void AudioStreamPlaybackRandomizer::stop() {
if (playing.is_valid()) {
playing->stop();
}
}
bool AudioStreamPlaybackRandomizer::is_playing() const {
if (playing.is_valid()) {
return playing->is_playing();
}
return false;
}
int AudioStreamPlaybackRandomizer::get_loop_count() const {
if (playing.is_valid()) {
return playing->get_loop_count();
}
return 0;
}
double AudioStreamPlaybackRandomizer::get_playback_position() const {
if (playing.is_valid()) {
return playing->get_playback_position();
}
return 0;
}
void AudioStreamPlaybackRandomizer::seek(double p_time) {
if (playing.is_valid()) {
playing->seek(p_time);
}
}
void AudioStreamPlaybackRandomizer::tag_used_streams() {
Ref<AudioStreamPlayback> p = playing; // Thread safety
if (p.is_valid()) {
p->tag_used_streams();
}
randomizer->tag_used(0);
}
int AudioStreamPlaybackRandomizer::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (playing.is_valid()) {
int mixed_samples = playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames);
for (int samp = 0; samp < mixed_samples; samp++) {
p_buffer[samp] *= volume_scale;
}
return mixed_samples;
} else {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
return p_frames;
}
}
AudioStreamPlaybackRandomizer::~AudioStreamPlaybackRandomizer() {
randomizer->playbacks.erase(this);
}
/////////////////////////////////////////////

View file

@ -0,0 +1,376 @@
/**************************************************************************/
/* audio_stream.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_STREAM_H
#define AUDIO_STREAM_H
#include "core/io/image.h"
#include "core/io/resource.h"
#include "scene/property_list_helper.h"
#include "servers/audio/audio_filter_sw.h"
#include "servers/audio_server.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
#include "core/variant/typed_array.h"
class AudioStream;
class AudioSamplePlayback : public RefCounted {
GDCLASS(AudioSamplePlayback, RefCounted);
public:
Ref<AudioStream> stream;
float offset = 0.0f;
Vector<AudioFrame> volume_vector;
StringName bus;
};
class AudioSample : public RefCounted {
GDCLASS(AudioSample, RefCounted)
public:
enum LoopMode {
LOOP_DISABLED,
LOOP_FORWARD,
LOOP_PINGPONG,
LOOP_BACKWARD,
};
Ref<AudioStream> stream;
Vector<AudioFrame> data;
int num_channels = 1;
int sample_rate = 44100;
LoopMode loop_mode = LOOP_DISABLED;
int loop_begin = 0;
int loop_end = 0;
};
///////////
class AudioStreamPlayback : public RefCounted {
GDCLASS(AudioStreamPlayback, RefCounted);
protected:
static void _bind_methods();
GDVIRTUAL1(_start, double)
GDVIRTUAL0(_stop)
GDVIRTUAL0RC(bool, _is_playing)
GDVIRTUAL0RC(int, _get_loop_count)
GDVIRTUAL0RC(double, _get_playback_position)
GDVIRTUAL1(_seek, double)
GDVIRTUAL3R(int, _mix, GDExtensionPtr<AudioFrame>, float, int)
GDVIRTUAL0(_tag_used_streams)
GDVIRTUAL2(_set_parameter, const StringName &, const Variant &)
GDVIRTUAL1RC(Variant, _get_parameter, const StringName &)
public:
virtual void start(double p_from_pos = 0.0);
virtual void stop();
virtual bool is_playing() const;
virtual int get_loop_count() const; //times it looped
virtual double get_playback_position() const;
virtual void seek(double p_time);
virtual void tag_used_streams();
virtual void set_parameter(const StringName &p_name, const Variant &p_value);
virtual Variant get_parameter(const StringName &p_name) const;
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
virtual void set_is_sample(bool p_is_sample) {}
virtual bool get_is_sample() const { return false; }
virtual Ref<AudioSamplePlayback> get_sample_playback() const;
virtual void set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {}
AudioStreamPlayback();
~AudioStreamPlayback();
};
class AudioStreamPlaybackResampled : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackResampled, AudioStreamPlayback);
enum {
FP_BITS = 16, //fixed point used for resampling
FP_LEN = (1 << FP_BITS),
FP_MASK = FP_LEN - 1,
INTERNAL_BUFFER_LEN = 128, // 128 warrants 3ms positional jitter at much at 44100hz
CUBIC_INTERP_HISTORY = 4
};
AudioFrame internal_buffer[INTERNAL_BUFFER_LEN + CUBIC_INTERP_HISTORY];
unsigned int internal_buffer_end = -1;
uint64_t mix_offset = 0;
protected:
void begin_resample();
// Returns the number of frames that were mixed.
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames);
virtual float get_stream_sampling_rate();
GDVIRTUAL2R(int, _mix_resampled, GDExtensionPtr<AudioFrame>, int)
GDVIRTUAL0RC(float, _get_stream_sampling_rate)
static void _bind_methods();
public:
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
AudioStreamPlaybackResampled() { mix_offset = 0; }
};
class AudioStream : public Resource {
GDCLASS(AudioStream, Resource);
OBJ_SAVE_TYPE(AudioStream); // Saves derived classes with common type so they can be interchanged.
enum {
MAX_TAGGED_OFFSETS = 8
};
uint64_t tagged_frame = 0;
uint64_t offset_count = 0;
float tagged_offsets[MAX_TAGGED_OFFSETS];
protected:
static void _bind_methods();
GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instantiate_playback)
GDVIRTUAL0RC(String, _get_stream_name)
GDVIRTUAL0RC(double, _get_length)
GDVIRTUAL0RC(bool, _is_monophonic)
GDVIRTUAL0RC(double, _get_bpm)
GDVIRTUAL0RC(bool, _has_loop)
GDVIRTUAL0RC(int, _get_bar_beats)
GDVIRTUAL0RC(int, _get_beat_count)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_parameter_list)
public:
virtual Ref<AudioStreamPlayback> instantiate_playback();
virtual String get_stream_name() const;
virtual double get_bpm() const;
virtual bool has_loop() const;
virtual int get_bar_beats() const;
virtual int get_beat_count() const;
virtual double get_length() const;
virtual bool is_monophonic() const;
void tag_used(float p_offset);
uint64_t get_tagged_frame() const;
uint32_t get_tagged_frame_count() const;
float get_tagged_frame_offset(int p_index) const;
struct Parameter {
PropertyInfo property;
Variant default_value;
Parameter(const PropertyInfo &p_info = PropertyInfo(), const Variant &p_default_value = Variant()) {
property = p_info;
default_value = p_default_value;
}
};
virtual void get_parameter_list(List<Parameter> *r_parameters);
virtual bool can_be_sampled() const { return false; }
virtual Ref<AudioSample> generate_sample() const;
virtual bool is_meta_stream() const { return false; }
};
// Microphone
class AudioStreamPlaybackMicrophone;
class AudioStreamMicrophone : public AudioStream {
GDCLASS(AudioStreamMicrophone, AudioStream);
friend class AudioStreamPlaybackMicrophone;
HashSet<AudioStreamPlaybackMicrophone *> playbacks;
protected:
static void _bind_methods();
public:
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual double get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
AudioStreamMicrophone();
};
class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackMicrophone, AudioStreamPlaybackResampled);
friend class AudioStreamMicrophone;
bool active = false;
unsigned int input_ofs = 0;
Ref<AudioStreamMicrophone> microphone;
protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
virtual double get_playback_position() const override;
public:
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; //times it looped
virtual void seek(double p_time) override;
virtual void tag_used_streams() override;
~AudioStreamPlaybackMicrophone();
AudioStreamPlaybackMicrophone();
};
//
class AudioStreamPlaybackRandomizer;
class AudioStreamRandomizer : public AudioStream {
GDCLASS(AudioStreamRandomizer, AudioStream);
public:
enum PlaybackMode {
PLAYBACK_RANDOM_NO_REPEATS,
PLAYBACK_RANDOM,
PLAYBACK_SEQUENTIAL,
};
private:
friend class AudioStreamPlaybackRandomizer;
struct PoolEntry {
Ref<AudioStream> stream;
float weight = 1.0;
};
static inline PropertyListHelper base_property_helper;
PropertyListHelper property_helper;
HashSet<AudioStreamPlaybackRandomizer *> playbacks;
Vector<PoolEntry> audio_stream_pool;
float random_pitch_scale = 1.0f;
float random_volume_offset_db = 0.0f;
Ref<AudioStreamPlayback> instance_playback_random();
Ref<AudioStreamPlayback> instance_playback_no_repeats();
Ref<AudioStreamPlayback> instance_playback_sequential();
Ref<AudioStream> last_playback = nullptr;
PlaybackMode playback_mode = PLAYBACK_RANDOM_NO_REPEATS;
protected:
static void _bind_methods();
bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); }
bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); }
void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list); }
bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); }
bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); }
public:
void add_stream(int p_index, Ref<AudioStream> p_stream, float p_weight = 1.0);
void move_stream(int p_index_from, int p_index_to);
void remove_stream(int p_index);
void set_stream(int p_index, Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream(int p_index) const;
void set_stream_probability_weight(int p_index, float p_weight);
float get_stream_probability_weight(int p_index) const;
void set_streams_count(int p_count);
int get_streams_count() const;
void set_random_pitch(float p_pitch_scale);
float get_random_pitch() const;
void set_random_volume_offset_db(float p_volume_offset_db);
float get_random_volume_offset_db() const;
void set_playback_mode(PlaybackMode p_playback_mode);
PlaybackMode get_playback_mode() const;
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual double get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
virtual bool is_meta_stream() const override { return true; }
AudioStreamRandomizer();
};
class AudioStreamPlaybackRandomizer : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackRandomizer, AudioStreamPlayback);
friend class AudioStreamRandomizer;
Ref<AudioStreamRandomizer> randomizer;
Ref<AudioStreamPlayback> playback;
Ref<AudioStreamPlayback> playing;
float pitch_scale;
float volume_scale;
public:
virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; //times it looped
virtual double get_playback_position() const override;
virtual void seek(double p_time) override;
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
virtual void tag_used_streams() override;
~AudioStreamPlaybackRandomizer();
};
VARIANT_ENUM_CAST(AudioStreamRandomizer::PlaybackMode);
#endif // AUDIO_STREAM_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,72 @@
/**************************************************************************/
/* audio_effect_amplify.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_amplify.h"
void AudioEffectAmplifyInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
//multiply volume interpolating to avoid clicks if this changes
float volume_db = base->volume_db;
float vol = Math::db_to_linear(mix_volume_db);
float vol_inc = (Math::db_to_linear(volume_db) - vol) / float(p_frame_count);
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i] = p_src_frames[i] * vol;
vol += vol_inc;
}
//set volume for next mix
mix_volume_db = volume_db;
}
Ref<AudioEffectInstance> AudioEffectAmplify::instantiate() {
Ref<AudioEffectAmplifyInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectAmplify>(this);
ins->mix_volume_db = volume_db;
return ins;
}
void AudioEffectAmplify::set_volume_db(float p_volume) {
volume_db = p_volume;
}
float AudioEffectAmplify::get_volume_db() const {
return volume_db;
}
void AudioEffectAmplify::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_volume_db", "volume"), &AudioEffectAmplify::set_volume_db);
ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioEffectAmplify::get_volume_db);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01,suffix:dB"), "set_volume_db", "get_volume_db");
}
AudioEffectAmplify::AudioEffectAmplify() {
volume_db = 0;
}

View file

@ -0,0 +1,66 @@
/**************************************************************************/
/* audio_effect_amplify.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_AMPLIFY_H
#define AUDIO_EFFECT_AMPLIFY_H
#include "servers/audio/audio_effect.h"
class AudioEffectAmplify;
class AudioEffectAmplifyInstance : public AudioEffectInstance {
GDCLASS(AudioEffectAmplifyInstance, AudioEffectInstance);
friend class AudioEffectAmplify;
Ref<AudioEffectAmplify> base;
float mix_volume_db;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectAmplify : public AudioEffect {
GDCLASS(AudioEffectAmplify, AudioEffect);
friend class AudioEffectAmplifyInstance;
float volume_db;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_volume_db(float p_volume);
float get_volume_db() const;
AudioEffectAmplify();
};
#endif // AUDIO_EFFECT_AMPLIFY_H

View file

@ -0,0 +1,138 @@
/**************************************************************************/
/* audio_effect_capture.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_capture.h"
bool AudioEffectCapture::can_get_buffer(int p_frames) const {
return buffer.data_left() >= p_frames;
}
PackedVector2Array AudioEffectCapture::get_buffer(int p_frames) {
ERR_FAIL_COND_V(!buffer_initialized, PackedVector2Array());
ERR_FAIL_INDEX_V(p_frames, buffer.size(), PackedVector2Array());
int data_left = buffer.data_left();
if (data_left < p_frames || p_frames == 0) {
return PackedVector2Array();
}
PackedVector2Array ret;
ret.resize(p_frames);
Vector<AudioFrame> streaming_data;
streaming_data.resize(p_frames);
buffer.read(streaming_data.ptrw(), p_frames);
for (int32_t i = 0; i < p_frames; i++) {
ret.write[i] = Vector2(streaming_data[i].left, streaming_data[i].right);
}
return ret;
}
void AudioEffectCapture::clear_buffer() {
const int32_t data_left = buffer.data_left();
buffer.advance_read(data_left);
}
void AudioEffectCapture::_bind_methods() {
ClassDB::bind_method(D_METHOD("can_get_buffer", "frames"), &AudioEffectCapture::can_get_buffer);
ClassDB::bind_method(D_METHOD("get_buffer", "frames"), &AudioEffectCapture::get_buffer);
ClassDB::bind_method(D_METHOD("clear_buffer"), &AudioEffectCapture::clear_buffer);
ClassDB::bind_method(D_METHOD("set_buffer_length", "buffer_length_seconds"), &AudioEffectCapture::set_buffer_length);
ClassDB::bind_method(D_METHOD("get_buffer_length"), &AudioEffectCapture::get_buffer_length);
ClassDB::bind_method(D_METHOD("get_frames_available"), &AudioEffectCapture::get_frames_available);
ClassDB::bind_method(D_METHOD("get_discarded_frames"), &AudioEffectCapture::get_discarded_frames);
ClassDB::bind_method(D_METHOD("get_buffer_length_frames"), &AudioEffectCapture::get_buffer_length_frames);
ClassDB::bind_method(D_METHOD("get_pushed_frames"), &AudioEffectCapture::get_pushed_frames);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "buffer_length", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_buffer_length", "get_buffer_length");
}
Ref<AudioEffectInstance> AudioEffectCapture::instantiate() {
if (!buffer_initialized) {
float target_buffer_size = AudioServer::get_singleton()->get_mix_rate() * buffer_length_seconds;
ERR_FAIL_COND_V(target_buffer_size <= 0 || target_buffer_size >= (1 << 27), Ref<AudioEffectInstance>());
buffer.resize(nearest_shift((int)target_buffer_size));
buffer_initialized = true;
}
clear_buffer();
Ref<AudioEffectCaptureInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectCapture>(this);
return ins;
}
void AudioEffectCapture::set_buffer_length(float p_buffer_length_seconds) {
buffer_length_seconds = p_buffer_length_seconds;
}
float AudioEffectCapture::get_buffer_length() {
return buffer_length_seconds;
}
int AudioEffectCapture::get_frames_available() const {
ERR_FAIL_COND_V(!buffer_initialized, 0);
return buffer.data_left();
}
int64_t AudioEffectCapture::get_discarded_frames() const {
return discarded_frames.get();
}
int AudioEffectCapture::get_buffer_length_frames() const {
ERR_FAIL_COND_V(!buffer_initialized, 0);
return buffer.size();
}
int64_t AudioEffectCapture::get_pushed_frames() const {
return pushed_frames.get();
}
void AudioEffectCaptureInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
RingBuffer<AudioFrame> &buffer = base->buffer;
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i] = p_src_frames[i];
}
if (buffer.space_left() >= p_frame_count) {
// Add incoming audio frames to the IO ring buffer
int32_t ret = buffer.write(p_src_frames, p_frame_count);
ERR_FAIL_COND_MSG(ret != p_frame_count, "Failed to add data to effect capture ring buffer despite sufficient space.");
base->pushed_frames.add(p_frame_count);
} else {
base->discarded_frames.add(p_frame_count);
}
}
bool AudioEffectCaptureInstance::process_silence() const {
return true;
}

View file

@ -0,0 +1,83 @@
/**************************************************************************/
/* audio_effect_capture.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_CAPTURE_H
#define AUDIO_EFFECT_CAPTURE_H
#include "core/config/engine.h"
#include "core/math/audio_frame.h"
#include "core/object/ref_counted.h"
#include "core/templates/ring_buffer.h"
#include "core/templates/vector.h"
#include "servers/audio/audio_effect.h"
#include "servers/audio_server.h"
class AudioEffectCapture;
class AudioEffectCaptureInstance : public AudioEffectInstance {
GDCLASS(AudioEffectCaptureInstance, AudioEffectInstance);
friend class AudioEffectCapture;
Ref<AudioEffectCapture> base;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
virtual bool process_silence() const override;
};
class AudioEffectCapture : public AudioEffect {
GDCLASS(AudioEffectCapture, AudioEffect)
friend class AudioEffectCaptureInstance;
RingBuffer<AudioFrame> buffer;
SafeNumeric<uint64_t> discarded_frames;
SafeNumeric<uint64_t> pushed_frames;
float buffer_length_seconds = 0.1f;
bool buffer_initialized = false;
protected:
static void _bind_methods();
public:
virtual Ref<AudioEffectInstance> instantiate() override;
void set_buffer_length(float p_buffer_length_seconds);
float get_buffer_length();
bool can_get_buffer(int p_frames) const;
PackedVector2Array get_buffer(int p_len);
void clear_buffer();
int get_frames_available() const;
int64_t get_discarded_frames() const;
int get_buffer_length_frames() const;
int64_t get_pushed_frames() const;
};
#endif // AUDIO_EFFECT_CAPTURE_H

View file

@ -0,0 +1,360 @@
/**************************************************************************/
/* audio_effect_chorus.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_chorus.h"
#include "core/math/math_funcs.h"
#include "servers/audio_server.h"
void AudioEffectChorusInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
int todo = p_frame_count;
while (todo) {
int to_mix = MIN(todo, 256); //can't mix too much
_process_chunk(p_src_frames, p_dst_frames, to_mix);
p_src_frames += to_mix;
p_dst_frames += to_mix;
todo -= to_mix;
}
}
void AudioEffectChorusInstance::_process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
//fill ringbuffer
for (int i = 0; i < p_frame_count; i++) {
audio_buffer.write[(buffer_pos + i) & buffer_mask] = p_src_frames[i];
p_dst_frames[i] = p_src_frames[i] * base->dry;
}
float mix_rate = AudioServer::get_singleton()->get_mix_rate();
/* process voices */
for (int vc = 0; vc < base->voice_count; vc++) {
AudioEffectChorus::Voice &v = base->voice[vc];
double time_to_mix = (float)p_frame_count / mix_rate;
double cycles_to_mix = time_to_mix * v.rate;
unsigned int local_rb_pos = buffer_pos;
AudioFrame *dst_buff = p_dst_frames;
AudioFrame *rb_buff = audio_buffer.ptrw();
double delay_msec = v.delay;
unsigned int delay_frames = Math::fast_ftoi((delay_msec / 1000.0) * mix_rate);
float max_depth_frames = (v.depth / 1000.0) * mix_rate;
uint64_t local_cycles = cycles[vc];
uint64_t increment = llrint(cycles_to_mix / (double)p_frame_count * (double)(1 << AudioEffectChorus::CYCLES_FRAC));
//check the LFO doesn't read ahead of the write pos
if ((((unsigned int)max_depth_frames) + 10) > delay_frames) { //10 as some threshold to avoid precision stuff
delay_frames += (int)max_depth_frames - delay_frames;
delay_frames += 10; //threshold to avoid precision stuff
}
//low pass filter
if (v.cutoff == 0) {
continue;
}
float auxlp = expf(-Math_TAU * v.cutoff / mix_rate);
float c1 = 1.0 - auxlp;
float c2 = auxlp;
AudioFrame h = filter_h[vc];
if (v.cutoff >= AudioEffectChorus::MS_CUTOFF_MAX) {
c1 = 1.0;
c2 = 0.0;
}
//vol modifier
AudioFrame vol_modifier = AudioFrame(base->wet, base->wet) * Math::db_to_linear(v.level);
vol_modifier.left *= CLAMP(1.0 - v.pan, 0, 1);
vol_modifier.right *= CLAMP(1.0 + v.pan, 0, 1);
for (int i = 0; i < p_frame_count; i++) {
/** COMPUTE WAVEFORM **/
float phase = (float)(local_cycles & AudioEffectChorus::CYCLES_MASK) / (float)(1 << AudioEffectChorus::CYCLES_FRAC);
float wave_delay = sinf(phase * Math_TAU) * max_depth_frames;
int wave_delay_frames = lrint(floor(wave_delay));
float wave_delay_frac = wave_delay - (float)wave_delay_frames;
/** COMPUTE RINGBUFFER POS**/
unsigned int rb_source = local_rb_pos;
rb_source -= delay_frames;
rb_source -= wave_delay_frames;
/** READ FROM RINGBUFFER, LINEARLY INTERPOLATE */
AudioFrame val = rb_buff[rb_source & buffer_mask];
AudioFrame val_next = rb_buff[(rb_source - 1) & buffer_mask];
val += (val_next - val) * wave_delay_frac;
val = val * c1 + h * c2;
h = val;
/** MIX VALUE TO OUTPUT **/
dst_buff[i] += val * vol_modifier;
local_cycles += increment;
local_rb_pos++;
}
filter_h[vc] = h;
cycles[vc] += Math::fast_ftoi(cycles_to_mix * (double)(1 << AudioEffectChorus::CYCLES_FRAC));
}
buffer_pos += p_frame_count;
}
Ref<AudioEffectInstance> AudioEffectChorus::instantiate() {
Ref<AudioEffectChorusInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectChorus>(this);
for (int i = 0; i < 4; i++) {
ins->filter_h[i] = AudioFrame(0, 0);
ins->cycles[i] = 0;
}
float ring_buffer_max_size = AudioEffectChorus::MAX_DELAY_MS + AudioEffectChorus::MAX_DEPTH_MS + AudioEffectChorus::MAX_WIDTH_MS;
ring_buffer_max_size *= 2; //just to avoid complications
ring_buffer_max_size /= 1000.0; //convert to seconds
ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
int ringbuff_size = ring_buffer_max_size;
int bits = 0;
while (ringbuff_size > 0) {
bits++;
ringbuff_size /= 2;
}
ringbuff_size = 1 << bits;
ins->buffer_mask = ringbuff_size - 1;
ins->buffer_pos = 0;
ins->audio_buffer.resize(ringbuff_size);
for (int i = 0; i < ringbuff_size; i++) {
ins->audio_buffer.write[i] = AudioFrame(0, 0);
}
return ins;
}
void AudioEffectChorus::set_voice_count(int p_voices) {
ERR_FAIL_COND(p_voices < 1 || p_voices > MAX_VOICES);
voice_count = p_voices;
}
int AudioEffectChorus::get_voice_count() const {
return voice_count;
}
void AudioEffectChorus::set_voice_delay_ms(int p_voice, float p_delay_ms) {
ERR_FAIL_INDEX(p_voice, MAX_VOICES);
voice[p_voice].delay = p_delay_ms;
}
float AudioEffectChorus::get_voice_delay_ms(int p_voice) const {
ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
return voice[p_voice].delay;
}
void AudioEffectChorus::set_voice_rate_hz(int p_voice, float p_rate_hz) {
ERR_FAIL_INDEX(p_voice, MAX_VOICES);
voice[p_voice].rate = p_rate_hz;
}
float AudioEffectChorus::get_voice_rate_hz(int p_voice) const {
ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
return voice[p_voice].rate;
}
void AudioEffectChorus::set_voice_depth_ms(int p_voice, float p_depth_ms) {
ERR_FAIL_INDEX(p_voice, MAX_VOICES);
voice[p_voice].depth = p_depth_ms;
}
float AudioEffectChorus::get_voice_depth_ms(int p_voice) const {
ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
return voice[p_voice].depth;
}
void AudioEffectChorus::set_voice_level_db(int p_voice, float p_level_db) {
ERR_FAIL_INDEX(p_voice, MAX_VOICES);
voice[p_voice].level = p_level_db;
}
float AudioEffectChorus::get_voice_level_db(int p_voice) const {
ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
return voice[p_voice].level;
}
void AudioEffectChorus::set_voice_cutoff_hz(int p_voice, float p_cutoff_hz) {
ERR_FAIL_INDEX(p_voice, MAX_VOICES);
voice[p_voice].cutoff = p_cutoff_hz;
}
float AudioEffectChorus::get_voice_cutoff_hz(int p_voice) const {
ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
return voice[p_voice].cutoff;
}
void AudioEffectChorus::set_voice_pan(int p_voice, float p_pan) {
ERR_FAIL_INDEX(p_voice, MAX_VOICES);
voice[p_voice].pan = p_pan;
}
float AudioEffectChorus::get_voice_pan(int p_voice) const {
ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
return voice[p_voice].pan;
}
void AudioEffectChorus::set_wet(float amount) {
wet = amount;
}
float AudioEffectChorus::get_wet() const {
return wet;
}
void AudioEffectChorus::set_dry(float amount) {
dry = amount;
}
float AudioEffectChorus::get_dry() const {
return dry;
}
void AudioEffectChorus::_validate_property(PropertyInfo &p_property) const {
if (p_property.name.begins_with("voice/")) {
int voice_idx = p_property.name.get_slice("/", 1).to_int();
if (voice_idx > voice_count) {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
}
void AudioEffectChorus::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_voice_count", "voices"), &AudioEffectChorus::set_voice_count);
ClassDB::bind_method(D_METHOD("get_voice_count"), &AudioEffectChorus::get_voice_count);
ClassDB::bind_method(D_METHOD("set_voice_delay_ms", "voice_idx", "delay_ms"), &AudioEffectChorus::set_voice_delay_ms);
ClassDB::bind_method(D_METHOD("get_voice_delay_ms", "voice_idx"), &AudioEffectChorus::get_voice_delay_ms);
ClassDB::bind_method(D_METHOD("set_voice_rate_hz", "voice_idx", "rate_hz"), &AudioEffectChorus::set_voice_rate_hz);
ClassDB::bind_method(D_METHOD("get_voice_rate_hz", "voice_idx"), &AudioEffectChorus::get_voice_rate_hz);
ClassDB::bind_method(D_METHOD("set_voice_depth_ms", "voice_idx", "depth_ms"), &AudioEffectChorus::set_voice_depth_ms);
ClassDB::bind_method(D_METHOD("get_voice_depth_ms", "voice_idx"), &AudioEffectChorus::get_voice_depth_ms);
ClassDB::bind_method(D_METHOD("set_voice_level_db", "voice_idx", "level_db"), &AudioEffectChorus::set_voice_level_db);
ClassDB::bind_method(D_METHOD("get_voice_level_db", "voice_idx"), &AudioEffectChorus::get_voice_level_db);
ClassDB::bind_method(D_METHOD("set_voice_cutoff_hz", "voice_idx", "cutoff_hz"), &AudioEffectChorus::set_voice_cutoff_hz);
ClassDB::bind_method(D_METHOD("get_voice_cutoff_hz", "voice_idx"), &AudioEffectChorus::get_voice_cutoff_hz);
ClassDB::bind_method(D_METHOD("set_voice_pan", "voice_idx", "pan"), &AudioEffectChorus::set_voice_pan);
ClassDB::bind_method(D_METHOD("get_voice_pan", "voice_idx"), &AudioEffectChorus::get_voice_pan);
ClassDB::bind_method(D_METHOD("set_wet", "amount"), &AudioEffectChorus::set_wet);
ClassDB::bind_method(D_METHOD("get_wet"), &AudioEffectChorus::get_wet);
ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectChorus::set_dry);
ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectChorus::get_dry);
ADD_PROPERTY(PropertyInfo(Variant::INT, "voice_count", PROPERTY_HINT_RANGE, "1,4,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_voice_count", "get_voice_count");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wet", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_wet", "get_wet");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 0);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 0);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 0);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 0);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 0);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 0);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 2);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 2);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 2);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 2);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 2);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 2);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 3);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 3);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 3);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 3);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 3);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 3);
}
AudioEffectChorus::AudioEffectChorus() {
voice_count = 2;
voice[0].delay = 15;
voice[1].delay = 20;
voice[0].rate = 0.8;
voice[1].rate = 1.2;
voice[0].depth = 2;
voice[1].depth = 3;
voice[0].cutoff = 8000;
voice[1].cutoff = 8000;
voice[0].pan = -0.5;
voice[1].pan = 0.5;
wet = 0.5;
dry = 1.0;
}

View file

@ -0,0 +1,136 @@
/**************************************************************************/
/* audio_effect_chorus.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_CHORUS_H
#define AUDIO_EFFECT_CHORUS_H
#include "servers/audio/audio_effect.h"
class AudioEffectChorus;
class AudioEffectChorusInstance : public AudioEffectInstance {
GDCLASS(AudioEffectChorusInstance, AudioEffectInstance);
friend class AudioEffectChorus;
Ref<AudioEffectChorus> base;
Vector<AudioFrame> audio_buffer;
unsigned int buffer_pos;
unsigned int buffer_mask;
AudioFrame filter_h[4];
uint64_t cycles[4];
void _process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count);
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectChorus : public AudioEffect {
GDCLASS(AudioEffectChorus, AudioEffect);
friend class AudioEffectChorusInstance;
public:
enum {
MAX_DELAY_MS = 50,
MAX_DEPTH_MS = 20,
MAX_WIDTH_MS = 50,
MAX_VOICES = 4,
CYCLES_FRAC = 16,
CYCLES_MASK = (1 << CYCLES_FRAC) - 1,
MAX_CHANNELS = 4,
MS_CUTOFF_MAX = 16000
};
private:
struct Voice {
float delay;
float rate;
float depth;
float level;
float cutoff;
float pan;
Voice() {
delay = 12.0;
rate = 1;
depth = 0;
level = 0;
cutoff = MS_CUTOFF_MAX;
pan = 0;
}
} voice[MAX_VOICES];
int voice_count;
float wet;
float dry;
protected:
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
public:
void set_voice_count(int p_voices);
int get_voice_count() const;
void set_voice_delay_ms(int p_voice, float p_delay_ms);
float get_voice_delay_ms(int p_voice) const;
void set_voice_rate_hz(int p_voice, float p_rate_hz);
float get_voice_rate_hz(int p_voice) const;
void set_voice_depth_ms(int p_voice, float p_depth_ms);
float get_voice_depth_ms(int p_voice) const;
void set_voice_level_db(int p_voice, float p_level_db);
float get_voice_level_db(int p_voice) const;
void set_voice_cutoff_hz(int p_voice, float p_cutoff_hz);
float get_voice_cutoff_hz(int p_voice) const;
void set_voice_pan(int p_voice, float p_pan);
float get_voice_pan(int p_voice) const;
void set_wet(float amount);
float get_wet() const;
void set_dry(float amount);
float get_dry() const;
Ref<AudioEffectInstance> instantiate() override;
AudioEffectChorus();
};
#endif // AUDIO_EFFECT_CHORUS_H

View file

@ -0,0 +1,237 @@
/**************************************************************************/
/* audio_effect_compressor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_compressor.h"
#include "servers/audio_server.h"
void AudioEffectCompressorInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float threshold = Math::db_to_linear(base->threshold);
float sample_rate = AudioServer::get_singleton()->get_mix_rate();
float ratatcoef = exp(-1 / (0.00001f * sample_rate));
float ratrelcoef = exp(-1 / (0.5f * sample_rate));
float attime = base->attack_us / 1000000.0;
float reltime = base->release_ms / 1000.0;
float atcoef = exp(-1 / (attime * sample_rate));
float relcoef = exp(-1 / (reltime * sample_rate));
float makeup = Math::db_to_linear(base->gain);
float mix = base->mix;
float gr_meter_decay = exp(1 / (1 * sample_rate));
const AudioFrame *src = p_src_frames;
if (base->sidechain != StringName() && current_channel != -1) {
int bus = AudioServer::get_singleton()->thread_find_bus_index(base->sidechain);
if (bus >= 0) {
src = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus, current_channel);
}
}
for (int i = 0; i < p_frame_count; i++) {
AudioFrame s = src[i];
//convert to positive
s.left = Math::abs(s.left);
s.right = Math::abs(s.right);
float peak = MAX(s.left, s.right);
float overdb = 2.08136898f * Math::linear_to_db(peak / threshold);
if (overdb < 0.0) { //we only care about what goes over to compress
overdb = 0.0;
}
if (overdb - rundb > 5) { // diffeence is too large
averatio = 4;
}
if (overdb > rundb) {
rundb = overdb + atcoef * (rundb - overdb);
runratio = averatio + ratatcoef * (runratio - averatio);
} else {
rundb = overdb + relcoef * (rundb - overdb);
runratio = averatio + ratrelcoef * (runratio - averatio);
}
overdb = rundb;
averatio = runratio;
float cratio;
if (false) { //rato all-in
cratio = 12 + averatio;
} else {
cratio = base->ratio;
}
float gr = -overdb * (cratio - 1) / cratio;
float grv = Math::db_to_linear(gr);
runmax = maxover + relcoef * (runmax - maxover); // highest peak for setting att/rel decays in reltime
maxover = runmax;
if (grv < gr_meter) {
gr_meter = grv;
} else {
gr_meter *= gr_meter_decay;
if (gr_meter > 1) {
gr_meter = 1;
}
}
p_dst_frames[i] = p_src_frames[i] * grv * makeup * mix + p_src_frames[i] * (1.0 - mix);
}
}
Ref<AudioEffectInstance> AudioEffectCompressor::instantiate() {
Ref<AudioEffectCompressorInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectCompressor>(this);
ins->rundb = 0;
ins->runratio = 0;
ins->averatio = 0;
ins->runmax = 0;
ins->maxover = 0;
ins->gr_meter = 1.0;
ins->current_channel = -1;
return ins;
}
void AudioEffectCompressor::set_threshold(float p_threshold) {
threshold = p_threshold;
}
float AudioEffectCompressor::get_threshold() const {
return threshold;
}
void AudioEffectCompressor::set_ratio(float p_ratio) {
ratio = p_ratio;
}
float AudioEffectCompressor::get_ratio() const {
return ratio;
}
void AudioEffectCompressor::set_gain(float p_gain) {
gain = p_gain;
}
float AudioEffectCompressor::get_gain() const {
return gain;
}
void AudioEffectCompressor::set_attack_us(float p_attack_us) {
attack_us = p_attack_us;
}
float AudioEffectCompressor::get_attack_us() const {
return attack_us;
}
void AudioEffectCompressor::set_release_ms(float p_release_ms) {
release_ms = p_release_ms;
}
float AudioEffectCompressor::get_release_ms() const {
return release_ms;
}
void AudioEffectCompressor::set_mix(float p_mix) {
mix = p_mix;
}
float AudioEffectCompressor::get_mix() const {
return mix;
}
void AudioEffectCompressor::set_sidechain(const StringName &p_sidechain) {
AudioServer::get_singleton()->lock();
sidechain = p_sidechain;
AudioServer::get_singleton()->unlock();
}
StringName AudioEffectCompressor::get_sidechain() const {
return sidechain;
}
void AudioEffectCompressor::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "sidechain") {
String buses = "";
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
buses += ",";
buses += AudioServer::get_singleton()->get_bus_name(i);
}
p_property.hint_string = buses;
}
}
void AudioEffectCompressor::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_threshold", "threshold"), &AudioEffectCompressor::set_threshold);
ClassDB::bind_method(D_METHOD("get_threshold"), &AudioEffectCompressor::get_threshold);
ClassDB::bind_method(D_METHOD("set_ratio", "ratio"), &AudioEffectCompressor::set_ratio);
ClassDB::bind_method(D_METHOD("get_ratio"), &AudioEffectCompressor::get_ratio);
ClassDB::bind_method(D_METHOD("set_gain", "gain"), &AudioEffectCompressor::set_gain);
ClassDB::bind_method(D_METHOD("get_gain"), &AudioEffectCompressor::get_gain);
ClassDB::bind_method(D_METHOD("set_attack_us", "attack_us"), &AudioEffectCompressor::set_attack_us);
ClassDB::bind_method(D_METHOD("get_attack_us"), &AudioEffectCompressor::get_attack_us);
ClassDB::bind_method(D_METHOD("set_release_ms", "release_ms"), &AudioEffectCompressor::set_release_ms);
ClassDB::bind_method(D_METHOD("get_release_ms"), &AudioEffectCompressor::get_release_ms);
ClassDB::bind_method(D_METHOD("set_mix", "mix"), &AudioEffectCompressor::set_mix);
ClassDB::bind_method(D_METHOD("get_mix"), &AudioEffectCompressor::get_mix);
ClassDB::bind_method(D_METHOD("set_sidechain", "sidechain"), &AudioEffectCompressor::set_sidechain);
ClassDB::bind_method(D_METHOD("get_sidechain"), &AudioEffectCompressor::get_sidechain);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "-60,0,0.1"), "set_threshold", "get_threshold");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio", PROPERTY_HINT_RANGE, "1,48,0.1"), "set_ratio", "get_ratio");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gain", PROPERTY_HINT_RANGE, "-20,20,0.1"), "set_gain", "get_gain");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attack_us", PROPERTY_HINT_RANGE, U"20,2000,1,suffix:\u00B5s"), "set_attack_us", "get_attack_us");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "release_ms", PROPERTY_HINT_RANGE, "20,2000,1,suffix:ms"), "set_release_ms", "get_release_ms");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mix", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_mix", "get_mix");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "sidechain", PROPERTY_HINT_ENUM), "set_sidechain", "get_sidechain");
}
AudioEffectCompressor::AudioEffectCompressor() {
threshold = 0;
ratio = 4;
gain = 0;
attack_us = 20;
release_ms = 250;
mix = 1;
}

View file

@ -0,0 +1,94 @@
/**************************************************************************/
/* audio_effect_compressor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_COMPRESSOR_H
#define AUDIO_EFFECT_COMPRESSOR_H
#include "servers/audio/audio_effect.h"
class AudioEffectCompressor;
class AudioEffectCompressorInstance : public AudioEffectInstance {
GDCLASS(AudioEffectCompressorInstance, AudioEffectInstance);
friend class AudioEffectCompressor;
Ref<AudioEffectCompressor> base;
float rundb, averatio, runratio, runmax, maxover, gr_meter;
int current_channel;
public:
void set_current_channel(int p_channel) { current_channel = p_channel; }
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectCompressor : public AudioEffect {
GDCLASS(AudioEffectCompressor, AudioEffect);
friend class AudioEffectCompressorInstance;
float threshold;
float ratio;
float gain;
float attack_us;
float release_ms;
float mix;
StringName sidechain;
protected:
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_threshold(float p_threshold);
float get_threshold() const;
void set_ratio(float p_ratio);
float get_ratio() const;
void set_gain(float p_gain);
float get_gain() const;
void set_attack_us(float p_attack_us);
float get_attack_us() const;
void set_release_ms(float p_release_ms);
float get_release_ms() const;
void set_mix(float p_mix);
float get_mix() const;
void set_sidechain(const StringName &p_sidechain);
StringName get_sidechain() const;
AudioEffectCompressor();
};
#endif // AUDIO_EFFECT_COMPRESSOR_H

View file

@ -0,0 +1,307 @@
/**************************************************************************/
/* audio_effect_delay.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_delay.h"
#include "core/math/math_funcs.h"
#include "servers/audio_server.h"
void AudioEffectDelayInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
int todo = p_frame_count;
while (todo) {
int to_mix = MIN(todo, 256); //can't mix too much
_process_chunk(p_src_frames, p_dst_frames, to_mix);
p_src_frames += to_mix;
p_dst_frames += to_mix;
todo -= to_mix;
}
}
void AudioEffectDelayInstance::_process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float main_level_f = base->dry;
float mix_rate = AudioServer::get_singleton()->get_mix_rate();
float tap_1_level_f = base->tap_1_active ? Math::db_to_linear(base->tap_1_level) : 0.0;
int tap_1_delay_frames = int((base->tap_1_delay_ms / 1000.0) * mix_rate);
float tap_2_level_f = base->tap_2_active ? Math::db_to_linear(base->tap_2_level) : 0.0;
int tap_2_delay_frames = int((base->tap_2_delay_ms / 1000.0) * mix_rate);
float feedback_level_f = base->feedback_active ? Math::db_to_linear(base->feedback_level) : 0.0;
unsigned int feedback_delay_frames = int((base->feedback_delay_ms / 1000.0) * mix_rate);
AudioFrame tap1_vol = AudioFrame(tap_1_level_f, tap_1_level_f);
tap1_vol.left *= CLAMP(1.0 - base->tap_1_pan, 0, 1);
tap1_vol.right *= CLAMP(1.0 + base->tap_1_pan, 0, 1);
AudioFrame tap2_vol = AudioFrame(tap_2_level_f, tap_2_level_f);
tap2_vol.left *= CLAMP(1.0 - base->tap_2_pan, 0, 1);
tap2_vol.right *= CLAMP(1.0 + base->tap_2_pan, 0, 1);
// feedback lowpass here
float lpf_c = expf(-Math_TAU * base->feedback_lowpass / mix_rate); // 0 .. 10khz
float lpf_ic = 1.0 - lpf_c;
const AudioFrame *src = p_src_frames;
AudioFrame *dst = p_dst_frames;
AudioFrame *rb_buf = ring_buffer.ptrw();
AudioFrame *fb_buf = feedback_buffer.ptrw();
for (int i = 0; i < p_frame_count; i++) {
rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i];
AudioFrame main_val = src[i] * main_level_f;
AudioFrame tap_1_val = rb_buf[(ring_buffer_pos - tap_1_delay_frames) & ring_buffer_mask] * tap1_vol;
AudioFrame tap_2_val = rb_buf[(ring_buffer_pos - tap_2_delay_frames) & ring_buffer_mask] * tap2_vol;
AudioFrame out = main_val + tap_1_val + tap_2_val;
out += fb_buf[feedback_buffer_pos];
//apply lowpass and feedback gain
AudioFrame fb_in = out * feedback_level_f * lpf_ic + h * lpf_c;
fb_in.undenormalize(); //avoid denormals
h = fb_in;
fb_buf[feedback_buffer_pos] = fb_in;
dst[i] = out;
ring_buffer_pos++;
if ((++feedback_buffer_pos) >= feedback_delay_frames) {
feedback_buffer_pos = 0;
}
}
}
Ref<AudioEffectInstance> AudioEffectDelay::instantiate() {
Ref<AudioEffectDelayInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectDelay>(this);
float ring_buffer_max_size = MAX_DELAY_MS + 100; //add 100ms of extra room, just in case
ring_buffer_max_size /= 1000.0; //convert to seconds
ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
int ringbuff_size = ring_buffer_max_size;
int bits = 0;
while (ringbuff_size > 0) {
bits++;
ringbuff_size /= 2;
}
ringbuff_size = 1 << bits;
ins->ring_buffer_mask = ringbuff_size - 1;
ins->ring_buffer_pos = 0;
ins->ring_buffer.resize(ringbuff_size);
ins->feedback_buffer.resize(ringbuff_size);
ins->feedback_buffer_pos = 0;
ins->h = AudioFrame(0, 0);
return ins;
}
void AudioEffectDelay::set_dry(float p_dry) {
dry = p_dry;
}
float AudioEffectDelay::get_dry() {
return dry;
}
void AudioEffectDelay::set_tap1_active(bool p_active) {
tap_1_active = p_active;
}
bool AudioEffectDelay::is_tap1_active() const {
return tap_1_active;
}
void AudioEffectDelay::set_tap1_delay_ms(float p_delay_ms) {
tap_1_delay_ms = p_delay_ms;
}
float AudioEffectDelay::get_tap1_delay_ms() const {
return tap_1_delay_ms;
}
void AudioEffectDelay::set_tap1_level_db(float p_level_db) {
tap_1_level = p_level_db;
}
float AudioEffectDelay::get_tap1_level_db() const {
return tap_1_level;
}
void AudioEffectDelay::set_tap1_pan(float p_pan) {
tap_1_pan = p_pan;
}
float AudioEffectDelay::get_tap1_pan() const {
return tap_1_pan;
}
void AudioEffectDelay::set_tap2_active(bool p_active) {
tap_2_active = p_active;
}
bool AudioEffectDelay::is_tap2_active() const {
return tap_2_active;
}
void AudioEffectDelay::set_tap2_delay_ms(float p_delay_ms) {
tap_2_delay_ms = p_delay_ms;
}
float AudioEffectDelay::get_tap2_delay_ms() const {
return tap_2_delay_ms;
}
void AudioEffectDelay::set_tap2_level_db(float p_level_db) {
tap_2_level = p_level_db;
}
float AudioEffectDelay::get_tap2_level_db() const {
return tap_2_level;
}
void AudioEffectDelay::set_tap2_pan(float p_pan) {
tap_2_pan = p_pan;
}
float AudioEffectDelay::get_tap2_pan() const {
return tap_2_pan;
}
void AudioEffectDelay::set_feedback_active(bool p_active) {
feedback_active = p_active;
}
bool AudioEffectDelay::is_feedback_active() const {
return feedback_active;
}
void AudioEffectDelay::set_feedback_delay_ms(float p_delay_ms) {
feedback_delay_ms = p_delay_ms;
}
float AudioEffectDelay::get_feedback_delay_ms() const {
return feedback_delay_ms;
}
void AudioEffectDelay::set_feedback_level_db(float p_level_db) {
feedback_level = p_level_db;
}
float AudioEffectDelay::get_feedback_level_db() const {
return feedback_level;
}
void AudioEffectDelay::set_feedback_lowpass(float p_lowpass) {
feedback_lowpass = p_lowpass;
}
float AudioEffectDelay::get_feedback_lowpass() const {
return feedback_lowpass;
}
void AudioEffectDelay::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectDelay::set_dry);
ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectDelay::get_dry);
ClassDB::bind_method(D_METHOD("set_tap1_active", "amount"), &AudioEffectDelay::set_tap1_active);
ClassDB::bind_method(D_METHOD("is_tap1_active"), &AudioEffectDelay::is_tap1_active);
ClassDB::bind_method(D_METHOD("set_tap1_delay_ms", "amount"), &AudioEffectDelay::set_tap1_delay_ms);
ClassDB::bind_method(D_METHOD("get_tap1_delay_ms"), &AudioEffectDelay::get_tap1_delay_ms);
ClassDB::bind_method(D_METHOD("set_tap1_level_db", "amount"), &AudioEffectDelay::set_tap1_level_db);
ClassDB::bind_method(D_METHOD("get_tap1_level_db"), &AudioEffectDelay::get_tap1_level_db);
ClassDB::bind_method(D_METHOD("set_tap1_pan", "amount"), &AudioEffectDelay::set_tap1_pan);
ClassDB::bind_method(D_METHOD("get_tap1_pan"), &AudioEffectDelay::get_tap1_pan);
ClassDB::bind_method(D_METHOD("set_tap2_active", "amount"), &AudioEffectDelay::set_tap2_active);
ClassDB::bind_method(D_METHOD("is_tap2_active"), &AudioEffectDelay::is_tap2_active);
ClassDB::bind_method(D_METHOD("set_tap2_delay_ms", "amount"), &AudioEffectDelay::set_tap2_delay_ms);
ClassDB::bind_method(D_METHOD("get_tap2_delay_ms"), &AudioEffectDelay::get_tap2_delay_ms);
ClassDB::bind_method(D_METHOD("set_tap2_level_db", "amount"), &AudioEffectDelay::set_tap2_level_db);
ClassDB::bind_method(D_METHOD("get_tap2_level_db"), &AudioEffectDelay::get_tap2_level_db);
ClassDB::bind_method(D_METHOD("set_tap2_pan", "amount"), &AudioEffectDelay::set_tap2_pan);
ClassDB::bind_method(D_METHOD("get_tap2_pan"), &AudioEffectDelay::get_tap2_pan);
ClassDB::bind_method(D_METHOD("set_feedback_active", "amount"), &AudioEffectDelay::set_feedback_active);
ClassDB::bind_method(D_METHOD("is_feedback_active"), &AudioEffectDelay::is_feedback_active);
ClassDB::bind_method(D_METHOD("set_feedback_delay_ms", "amount"), &AudioEffectDelay::set_feedback_delay_ms);
ClassDB::bind_method(D_METHOD("get_feedback_delay_ms"), &AudioEffectDelay::get_feedback_delay_ms);
ClassDB::bind_method(D_METHOD("set_feedback_level_db", "amount"), &AudioEffectDelay::set_feedback_level_db);
ClassDB::bind_method(D_METHOD("get_feedback_level_db"), &AudioEffectDelay::get_feedback_level_db);
ClassDB::bind_method(D_METHOD("set_feedback_lowpass", "amount"), &AudioEffectDelay::set_feedback_lowpass);
ClassDB::bind_method(D_METHOD("get_feedback_lowpass"), &AudioEffectDelay::get_feedback_lowpass);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");
ADD_GROUP("Tap 1", "tap1_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tap1_active"), "set_tap1_active", "is_tap1_active");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_tap1_delay_ms", "get_tap1_delay_ms");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_tap1_level_db", "get_tap1_level_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_tap1_pan", "get_tap1_pan");
ADD_GROUP("Tap 2", "tap2_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tap2_active"), "set_tap2_active", "is_tap2_active");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_tap2_delay_ms", "get_tap2_delay_ms");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_tap2_level_db", "get_tap2_level_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_tap2_pan", "get_tap2_pan");
ADD_GROUP("Feedback", "feedback_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feedback_active"), "set_feedback_active", "is_feedback_active");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_feedback_delay_ms", "get_feedback_delay_ms");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_feedback_level_db", "get_feedback_level_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_lowpass", PROPERTY_HINT_RANGE, "1,16000,1"), "set_feedback_lowpass", "get_feedback_lowpass");
}

View file

@ -0,0 +1,135 @@
/**************************************************************************/
/* audio_effect_delay.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_DELAY_H
#define AUDIO_EFFECT_DELAY_H
#include "servers/audio/audio_effect.h"
class AudioEffectDelay;
class AudioEffectDelayInstance : public AudioEffectInstance {
GDCLASS(AudioEffectDelayInstance, AudioEffectInstance);
friend class AudioEffectDelay;
Ref<AudioEffectDelay> base;
Vector<AudioFrame> ring_buffer;
unsigned int ring_buffer_pos;
unsigned int ring_buffer_mask;
/* feedback buffer */
Vector<AudioFrame> feedback_buffer;
unsigned int feedback_buffer_pos;
AudioFrame h;
void _process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count);
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectDelay : public AudioEffect {
GDCLASS(AudioEffectDelay, AudioEffect);
friend class AudioEffectDelayInstance;
enum {
MAX_DELAY_MS = 3000,
MAX_TAPS = 2
};
float dry = 1.0f;
bool tap_1_active = true;
float tap_1_delay_ms = 250.0f;
float tap_1_level = -6.0f;
float tap_1_pan = 0.2f;
bool tap_2_active = true;
float tap_2_delay_ms = 500.0f;
float tap_2_level = -12.0f;
float tap_2_pan = -0.4f;
bool feedback_active = false;
float feedback_delay_ms = 340.0f;
float feedback_level = -6.0f;
float feedback_lowpass = 16000.0f;
protected:
static void _bind_methods();
public:
void set_dry(float p_dry);
float get_dry();
void set_tap1_active(bool p_active);
bool is_tap1_active() const;
void set_tap1_delay_ms(float p_delay_ms);
float get_tap1_delay_ms() const;
void set_tap1_level_db(float p_level_db);
float get_tap1_level_db() const;
void set_tap1_pan(float p_pan);
float get_tap1_pan() const;
void set_tap2_active(bool p_active);
bool is_tap2_active() const;
void set_tap2_delay_ms(float p_delay_ms);
float get_tap2_delay_ms() const;
void set_tap2_level_db(float p_level_db);
float get_tap2_level_db() const;
void set_tap2_pan(float p_pan);
float get_tap2_pan() const;
void set_feedback_active(bool p_active);
bool is_feedback_active() const;
void set_feedback_delay_ms(float p_delay_ms);
float get_feedback_delay_ms() const;
void set_feedback_level_db(float p_level_db);
float get_feedback_level_db() const;
void set_feedback_lowpass(float p_lowpass);
float get_feedback_lowpass() const;
Ref<AudioEffectInstance> instantiate() override;
AudioEffectDelay() {}
};
#endif // AUDIO_EFFECT_DELAY_H

View file

@ -0,0 +1,181 @@
/**************************************************************************/
/* audio_effect_distortion.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_distortion.h"
#include "core/math/math_funcs.h"
#include "servers/audio_server.h"
void AudioEffectDistortionInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
const float *src = (const float *)p_src_frames;
float *dst = (float *)p_dst_frames;
//float lpf_c=expf(-Math_TAU*keep_hf_hz.get()/(mix_rate*(float)OVERSAMPLE));
float lpf_c = expf(-Math_TAU * base->keep_hf_hz / (AudioServer::get_singleton()->get_mix_rate()));
float lpf_ic = 1.0 - lpf_c;
float drive_f = base->drive;
float pregain_f = Math::db_to_linear(base->pre_gain);
float postgain_f = Math::db_to_linear(base->post_gain);
float atan_mult = pow(10, drive_f * drive_f * 3.0) - 1.0 + 0.001;
float atan_div = 1.0 / (atanf(atan_mult) * (1.0 + drive_f * 8));
float lofi_mult = powf(2.0, 2.0 + (1.0 - drive_f) * 14); //goes from 16 to 2 bits
for (int i = 0; i < p_frame_count * 2; i++) {
float out = undenormalize(src[i] * lpf_ic + lpf_c * h[i & 1]);
h[i & 1] = out;
float a = out;
float ha = src[i] - out; //high freqs
a *= pregain_f;
switch (base->mode) {
case AudioEffectDistortion::MODE_CLIP: {
float a_sign = a < 0 ? -1.0f : 1.0f;
a = powf(abs(a), 1.0001 - drive_f) * a_sign;
if (a > 1.0) {
a = 1.0;
} else if (a < (-1.0)) {
a = -1.0;
}
} break;
case AudioEffectDistortion::MODE_ATAN: {
a = atanf(a * atan_mult) * atan_div;
} break;
case AudioEffectDistortion::MODE_LOFI: {
a = floorf(a * lofi_mult + 0.5) / lofi_mult;
} break;
case AudioEffectDistortion::MODE_OVERDRIVE: {
const double x = a * 0.686306;
const double z = 1 + exp(sqrt(fabs(x)) * -0.75);
a = (expf(x) - expf(-x * z)) / (expf(x) + expf(-x));
} break;
case AudioEffectDistortion::MODE_WAVESHAPE: {
float x = a;
float k = 2 * drive_f / (1.00001 - drive_f);
a = (1.0 + k) * x / (1.0 + k * fabsf(x));
} break;
}
dst[i] = a * postgain_f + ha;
}
}
Ref<AudioEffectInstance> AudioEffectDistortion::instantiate() {
Ref<AudioEffectDistortionInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectDistortion>(this);
ins->h[0] = 0;
ins->h[1] = 0;
return ins;
}
void AudioEffectDistortion::set_mode(Mode p_mode) {
mode = p_mode;
}
AudioEffectDistortion::Mode AudioEffectDistortion::get_mode() const {
return mode;
}
void AudioEffectDistortion::set_pre_gain(float p_pre_gain) {
pre_gain = p_pre_gain;
}
float AudioEffectDistortion::get_pre_gain() const {
return pre_gain;
}
void AudioEffectDistortion::set_keep_hf_hz(float p_keep_hf_hz) {
keep_hf_hz = p_keep_hf_hz;
}
float AudioEffectDistortion::get_keep_hf_hz() const {
return keep_hf_hz;
}
void AudioEffectDistortion::set_drive(float p_drive) {
drive = p_drive;
}
float AudioEffectDistortion::get_drive() const {
return drive;
}
void AudioEffectDistortion::set_post_gain(float p_post_gain) {
post_gain = p_post_gain;
}
float AudioEffectDistortion::get_post_gain() const {
return post_gain;
}
void AudioEffectDistortion::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &AudioEffectDistortion::set_mode);
ClassDB::bind_method(D_METHOD("get_mode"), &AudioEffectDistortion::get_mode);
ClassDB::bind_method(D_METHOD("set_pre_gain", "pre_gain"), &AudioEffectDistortion::set_pre_gain);
ClassDB::bind_method(D_METHOD("get_pre_gain"), &AudioEffectDistortion::get_pre_gain);
ClassDB::bind_method(D_METHOD("set_keep_hf_hz", "keep_hf_hz"), &AudioEffectDistortion::set_keep_hf_hz);
ClassDB::bind_method(D_METHOD("get_keep_hf_hz"), &AudioEffectDistortion::get_keep_hf_hz);
ClassDB::bind_method(D_METHOD("set_drive", "drive"), &AudioEffectDistortion::set_drive);
ClassDB::bind_method(D_METHOD("get_drive"), &AudioEffectDistortion::get_drive);
ClassDB::bind_method(D_METHOD("set_post_gain", "post_gain"), &AudioEffectDistortion::set_post_gain);
ClassDB::bind_method(D_METHOD("get_post_gain"), &AudioEffectDistortion::get_post_gain);
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Clip,ATan,LoFi,Overdrive,Wave Shape"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pre_gain", PROPERTY_HINT_RANGE, "-60,60,0.01,suffix:dB"), "set_pre_gain", "get_pre_gain");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "keep_hf_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_keep_hf_hz", "get_keep_hf_hz");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drive", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drive", "get_drive");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "post_gain", PROPERTY_HINT_RANGE, "-80,24,0.01,suffix:dB"), "set_post_gain", "get_post_gain");
BIND_ENUM_CONSTANT(MODE_CLIP);
BIND_ENUM_CONSTANT(MODE_ATAN);
BIND_ENUM_CONSTANT(MODE_LOFI);
BIND_ENUM_CONSTANT(MODE_OVERDRIVE);
BIND_ENUM_CONSTANT(MODE_WAVESHAPE);
}
AudioEffectDistortion::AudioEffectDistortion() {
mode = MODE_CLIP;
pre_gain = 0;
post_gain = 0;
keep_hf_hz = 16000;
drive = 0;
}

View file

@ -0,0 +1,93 @@
/**************************************************************************/
/* audio_effect_distortion.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_DISTORTION_H
#define AUDIO_EFFECT_DISTORTION_H
#include "servers/audio/audio_effect.h"
class AudioEffectDistortion;
class AudioEffectDistortionInstance : public AudioEffectInstance {
GDCLASS(AudioEffectDistortionInstance, AudioEffectInstance);
friend class AudioEffectDistortion;
Ref<AudioEffectDistortion> base;
float h[2];
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectDistortion : public AudioEffect {
GDCLASS(AudioEffectDistortion, AudioEffect);
public:
enum Mode {
MODE_CLIP,
MODE_ATAN,
MODE_LOFI,
MODE_OVERDRIVE,
MODE_WAVESHAPE,
};
friend class AudioEffectDistortionInstance;
Mode mode;
float pre_gain;
float post_gain;
float keep_hf_hz;
float drive;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_mode(Mode p_mode);
Mode get_mode() const;
void set_pre_gain(float p_pre_gain);
float get_pre_gain() const;
void set_keep_hf_hz(float p_keep_hf_hz);
float get_keep_hf_hz() const;
void set_drive(float p_drive);
float get_drive() const;
void set_post_gain(float p_post_gain);
float get_post_gain() const;
AudioEffectDistortion();
};
VARIANT_ENUM_CAST(AudioEffectDistortion::Mode)
#endif // AUDIO_EFFECT_DISTORTION_H

View file

@ -0,0 +1,135 @@
/**************************************************************************/
/* audio_effect_eq.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_eq.h"
#include "servers/audio_server.h"
void AudioEffectEQInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
int band_count = bands[0].size();
EQ::BandProcess *proc_l = bands[0].ptrw();
EQ::BandProcess *proc_r = bands[1].ptrw();
float *bgain = gains.ptrw();
for (int i = 0; i < band_count; i++) {
bgain[i] = Math::db_to_linear(base->gain[i]);
}
for (int i = 0; i < p_frame_count; i++) {
AudioFrame src = p_src_frames[i];
AudioFrame dst = AudioFrame(0, 0);
for (int j = 0; j < band_count; j++) {
float l = src.left;
float r = src.right;
proc_l[j].process_one(l);
proc_r[j].process_one(r);
dst.left += l * bgain[j];
dst.right += r * bgain[j];
}
p_dst_frames[i] = dst;
}
}
Ref<AudioEffectInstance> AudioEffectEQ::instantiate() {
Ref<AudioEffectEQInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectEQ>(this);
ins->gains.resize(eq.get_band_count());
for (int i = 0; i < 2; i++) {
ins->bands[i].resize(eq.get_band_count());
for (int j = 0; j < ins->bands[i].size(); j++) {
ins->bands[i].write[j] = eq.get_band_processor(j);
}
}
return ins;
}
void AudioEffectEQ::set_band_gain_db(int p_band, float p_volume) {
ERR_FAIL_INDEX(p_band, gain.size());
gain.write[p_band] = p_volume;
}
float AudioEffectEQ::get_band_gain_db(int p_band) const {
ERR_FAIL_INDEX_V(p_band, gain.size(), 0);
return gain[p_band];
}
int AudioEffectEQ::get_band_count() const {
return gain.size();
}
bool AudioEffectEQ::_set(const StringName &p_name, const Variant &p_value) {
HashMap<StringName, int>::ConstIterator E = prop_band_map.find(p_name);
if (E) {
set_band_gain_db(E->value, p_value);
return true;
}
return false;
}
bool AudioEffectEQ::_get(const StringName &p_name, Variant &r_ret) const {
HashMap<StringName, int>::ConstIterator E = prop_band_map.find(p_name);
if (E) {
r_ret = get_band_gain_db(E->value);
return true;
}
return false;
}
void AudioEffectEQ::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < band_names.size(); i++) {
p_list->push_back(PropertyInfo(Variant::FLOAT, band_names[i], PROPERTY_HINT_RANGE, "-60,24,0.1"));
}
}
void AudioEffectEQ::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_band_gain_db", "band_idx", "volume_db"), &AudioEffectEQ::set_band_gain_db);
ClassDB::bind_method(D_METHOD("get_band_gain_db", "band_idx"), &AudioEffectEQ::get_band_gain_db);
ClassDB::bind_method(D_METHOD("get_band_count"), &AudioEffectEQ::get_band_count);
}
AudioEffectEQ::AudioEffectEQ(EQ::Preset p_preset) {
eq.set_mix_rate(AudioServer::get_singleton()->get_mix_rate());
eq.set_preset_band_mode(p_preset);
gain.resize(eq.get_band_count());
for (int i = 0; i < gain.size(); i++) {
gain.write[i] = 0.0;
String band_name = "band_db/" + itos(eq.get_band_frequency(i)) + "_hz";
prop_band_map[band_name] = i;
band_names.push_back(band_name);
}
}

View file

@ -0,0 +1,101 @@
/**************************************************************************/
/* audio_effect_eq.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_EQ_H
#define AUDIO_EFFECT_EQ_H
#include "servers/audio/audio_effect.h"
#include "servers/audio/effects/eq_filter.h"
class AudioEffectEQ;
class AudioEffectEQInstance : public AudioEffectInstance {
GDCLASS(AudioEffectEQInstance, AudioEffectInstance);
friend class AudioEffectEQ;
Ref<AudioEffectEQ> base;
Vector<EQ::BandProcess> bands[2];
Vector<float> gains;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectEQ : public AudioEffect {
GDCLASS(AudioEffectEQ, AudioEffect);
friend class AudioEffectEQInstance;
EQ eq;
Vector<float> gain;
HashMap<StringName, int> prop_band_map;
Vector<String> band_names;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_band_gain_db(int p_band, float p_volume);
float get_band_gain_db(int p_band) const;
int get_band_count() const;
AudioEffectEQ(EQ::Preset p_preset = EQ::PRESET_6_BANDS);
};
class AudioEffectEQ6 : public AudioEffectEQ {
GDCLASS(AudioEffectEQ6, AudioEffectEQ);
public:
AudioEffectEQ6() :
AudioEffectEQ(EQ::PRESET_6_BANDS) {}
};
class AudioEffectEQ10 : public AudioEffectEQ {
GDCLASS(AudioEffectEQ10, AudioEffectEQ);
public:
AudioEffectEQ10() :
AudioEffectEQ(EQ::PRESET_10_BANDS) {}
};
class AudioEffectEQ21 : public AudioEffectEQ {
GDCLASS(AudioEffectEQ21, AudioEffectEQ);
public:
AudioEffectEQ21() :
AudioEffectEQ(EQ::PRESET_21_BANDS) {}
};
#endif // AUDIO_EFFECT_EQ_H

View file

@ -0,0 +1,173 @@
/**************************************************************************/
/* audio_effect_filter.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_filter.h"
#include "servers/audio_server.h"
template <int S>
void AudioEffectFilterInstance::_process_filter(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
for (int i = 0; i < p_frame_count; i++) {
float f = p_src_frames[i].left;
filter_process[0][0].process_one(f);
if constexpr (S > 1) {
filter_process[0][1].process_one(f);
}
if constexpr (S > 2) {
filter_process[0][2].process_one(f);
}
if constexpr (S > 3) {
filter_process[0][3].process_one(f);
}
p_dst_frames[i].left = f;
}
for (int i = 0; i < p_frame_count; i++) {
float f = p_src_frames[i].right;
filter_process[1][0].process_one(f);
if constexpr (S > 1) {
filter_process[1][1].process_one(f);
}
if constexpr (S > 2) {
filter_process[1][2].process_one(f);
}
if constexpr (S > 3) {
filter_process[1][3].process_one(f);
}
p_dst_frames[i].right = f;
}
}
void AudioEffectFilterInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
filter.set_cutoff(base->cutoff);
filter.set_gain(base->gain);
filter.set_resonance(base->resonance);
filter.set_mode(base->mode);
int stages = int(base->db) + 1;
filter.set_stages(stages);
filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate());
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 4; j++) {
filter_process[i][j].update_coeffs();
}
}
if (stages == 1) {
_process_filter<1>(p_src_frames, p_dst_frames, p_frame_count);
} else if (stages == 2) {
_process_filter<2>(p_src_frames, p_dst_frames, p_frame_count);
} else if (stages == 3) {
_process_filter<3>(p_src_frames, p_dst_frames, p_frame_count);
} else if (stages == 4) {
_process_filter<4>(p_src_frames, p_dst_frames, p_frame_count);
}
}
AudioEffectFilterInstance::AudioEffectFilterInstance() {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 4; j++) {
filter_process[i][j].set_filter(&filter);
}
}
}
Ref<AudioEffectInstance> AudioEffectFilter::instantiate() {
Ref<AudioEffectFilterInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectFilter>(this);
return ins;
}
void AudioEffectFilter::set_cutoff(float p_freq) {
cutoff = p_freq;
}
float AudioEffectFilter::get_cutoff() const {
return cutoff;
}
void AudioEffectFilter::set_resonance(float p_amount) {
resonance = p_amount;
}
float AudioEffectFilter::get_resonance() const {
return resonance;
}
void AudioEffectFilter::set_gain(float p_amount) {
gain = p_amount;
}
float AudioEffectFilter::get_gain() const {
return gain;
}
void AudioEffectFilter::set_db(FilterDB p_db) {
db = p_db;
}
AudioEffectFilter::FilterDB AudioEffectFilter::get_db() const {
return db;
}
void AudioEffectFilter::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cutoff", "freq"), &AudioEffectFilter::set_cutoff);
ClassDB::bind_method(D_METHOD("get_cutoff"), &AudioEffectFilter::get_cutoff);
ClassDB::bind_method(D_METHOD("set_resonance", "amount"), &AudioEffectFilter::set_resonance);
ClassDB::bind_method(D_METHOD("get_resonance"), &AudioEffectFilter::get_resonance);
ClassDB::bind_method(D_METHOD("set_gain", "amount"), &AudioEffectFilter::set_gain);
ClassDB::bind_method(D_METHOD("get_gain"), &AudioEffectFilter::get_gain);
ClassDB::bind_method(D_METHOD("set_db", "amount"), &AudioEffectFilter::set_db);
ClassDB::bind_method(D_METHOD("get_db"), &AudioEffectFilter::get_db);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_cutoff", "get_cutoff");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "resonance", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_resonance", "get_resonance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gain", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_gain", "get_gain");
ADD_PROPERTY(PropertyInfo(Variant::INT, "db", PROPERTY_HINT_ENUM, "6 dB,12 dB,18 dB,24 dB"), "set_db", "get_db");
BIND_ENUM_CONSTANT(FILTER_6DB);
BIND_ENUM_CONSTANT(FILTER_12DB);
BIND_ENUM_CONSTANT(FILTER_18DB);
BIND_ENUM_CONSTANT(FILTER_24DB);
}
AudioEffectFilter::AudioEffectFilter(AudioFilterSW::Mode p_mode) {
mode = p_mode;
cutoff = 2000;
resonance = 0.5;
gain = 1.0;
db = FILTER_6DB;
}

View file

@ -0,0 +1,170 @@
/**************************************************************************/
/* audio_effect_filter.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_FILTER_H
#define AUDIO_EFFECT_FILTER_H
#include "servers/audio/audio_effect.h"
#include "servers/audio/audio_filter_sw.h"
class AudioEffectFilter;
class AudioEffectFilterInstance : public AudioEffectInstance {
GDCLASS(AudioEffectFilterInstance, AudioEffectInstance);
friend class AudioEffectFilter;
Ref<AudioEffectFilter> base;
AudioFilterSW filter;
AudioFilterSW::Processor filter_process[2][4];
template <int S>
void _process_filter(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count);
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
AudioEffectFilterInstance();
};
class AudioEffectFilter : public AudioEffect {
GDCLASS(AudioEffectFilter, AudioEffect);
public:
enum FilterDB {
FILTER_6DB,
FILTER_12DB,
FILTER_18DB,
FILTER_24DB,
};
friend class AudioEffectFilterInstance;
AudioFilterSW::Mode mode;
float cutoff;
float resonance;
float gain;
FilterDB db;
protected:
static void _bind_methods();
public:
void set_cutoff(float p_freq);
float get_cutoff() const;
void set_resonance(float p_amount);
float get_resonance() const;
void set_gain(float p_amount);
float get_gain() const;
void set_db(FilterDB p_db);
FilterDB get_db() const;
Ref<AudioEffectInstance> instantiate() override;
AudioEffectFilter(AudioFilterSW::Mode p_mode = AudioFilterSW::LOWPASS);
};
VARIANT_ENUM_CAST(AudioEffectFilter::FilterDB)
class AudioEffectLowPassFilter : public AudioEffectFilter {
GDCLASS(AudioEffectLowPassFilter, AudioEffectFilter);
void _validate_property(PropertyInfo &p_property) const {
if (p_property.name == "gain") {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
public:
AudioEffectLowPassFilter() :
AudioEffectFilter(AudioFilterSW::LOWPASS) {}
};
class AudioEffectHighPassFilter : public AudioEffectFilter {
GDCLASS(AudioEffectHighPassFilter, AudioEffectFilter);
void _validate_property(PropertyInfo &p_property) const {
if (p_property.name == "gain") {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
public:
AudioEffectHighPassFilter() :
AudioEffectFilter(AudioFilterSW::HIGHPASS) {}
};
class AudioEffectBandPassFilter : public AudioEffectFilter {
GDCLASS(AudioEffectBandPassFilter, AudioEffectFilter);
void _validate_property(PropertyInfo &p_property) const {
if (p_property.name == "gain") {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
public:
AudioEffectBandPassFilter() :
AudioEffectFilter(AudioFilterSW::BANDPASS) {}
};
class AudioEffectNotchFilter : public AudioEffectFilter {
GDCLASS(AudioEffectNotchFilter, AudioEffectFilter);
public:
AudioEffectNotchFilter() :
AudioEffectFilter(AudioFilterSW::NOTCH) {}
};
class AudioEffectBandLimitFilter : public AudioEffectFilter {
GDCLASS(AudioEffectBandLimitFilter, AudioEffectFilter);
public:
AudioEffectBandLimitFilter() :
AudioEffectFilter(AudioFilterSW::BANDLIMIT) {}
};
class AudioEffectLowShelfFilter : public AudioEffectFilter {
GDCLASS(AudioEffectLowShelfFilter, AudioEffectFilter);
public:
AudioEffectLowShelfFilter() :
AudioEffectFilter(AudioFilterSW::LOWSHELF) {}
};
class AudioEffectHighShelfFilter : public AudioEffectFilter {
GDCLASS(AudioEffectHighShelfFilter, AudioEffectFilter);
public:
AudioEffectHighShelfFilter() :
AudioEffectFilter(AudioFilterSW::HIGHSHELF) {}
};
#endif // AUDIO_EFFECT_FILTER_H

View file

@ -0,0 +1,161 @@
/**************************************************************************/
/* audio_effect_hard_limiter.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_hard_limiter.h"
#include "servers/audio_server.h"
void AudioEffectHardLimiterInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float sample_rate = AudioServer::get_singleton()->get_mix_rate();
float ceiling = Math::db_to_linear(base->ceiling);
float release = base->release;
float attack = base->attack;
float pre_gain = Math::db_to_linear(base->pre_gain);
for (int i = 0; i < p_frame_count; i++) {
float sample_left = p_src_frames[i].left;
float sample_right = p_src_frames[i].right;
sample_left *= pre_gain;
sample_right *= pre_gain;
float largest_sample = MAX(ABS(sample_left), ABS(sample_right));
release_factor = MAX(0.0, release_factor - 1.0 / sample_rate);
release_factor = MIN(release_factor, release);
if (release_factor > 0.0) {
gain = Math::lerp(gain_target, 1.0f, 1.0f - release_factor / release);
}
if (largest_sample * gain > ceiling) {
gain_target = ceiling / largest_sample;
release_factor = release;
attack_factor = attack;
}
// Lerp gain over attack time to avoid distortion.
attack_factor = MAX(0.0f, attack_factor - 1.0f / sample_rate);
if (attack_factor > 0.0) {
gain = Math::lerp(gain_target, gain, 1.0f - attack_factor / attack);
}
int bucket_id = gain_bucket_cursor / gain_bucket_size;
// If first item within the current bucket, reset the bucket.
if (gain_bucket_cursor % gain_bucket_size == 0) {
gain_buckets[bucket_id] = 1.0f;
}
gain_buckets[bucket_id] = MIN(gain_buckets[bucket_id], gain);
gain_bucket_cursor = (gain_bucket_cursor + 1) % gain_samples_to_store;
for (int j = 0; j < (int)gain_buckets.size(); j++) {
gain = MIN(gain, gain_buckets[j]);
}
// Introduce latency by grabbing the AudioFrame stored previously,
// then overwrite it with current audioframe, then update circular
// buffer cursor.
float dst_buffer_left = sample_buffer_left[sample_cursor];
float dst_buffer_right = sample_buffer_right[sample_cursor];
sample_buffer_left[sample_cursor] = sample_left;
sample_buffer_right[sample_cursor] = sample_right;
sample_cursor = (sample_cursor + 1) % sample_buffer_left.size();
p_dst_frames[i].left = dst_buffer_left * gain;
p_dst_frames[i].right = dst_buffer_right * gain;
}
}
Ref<AudioEffectInstance> AudioEffectHardLimiter::instantiate() {
Ref<AudioEffectHardLimiterInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectHardLimiter>(this);
float mix_rate = AudioServer::get_singleton()->get_mix_rate();
for (int i = 0; i < (int)Math::ceil(mix_rate * attack) + 1; i++) {
ins->sample_buffer_left.push_back(0.0f);
ins->sample_buffer_right.push_back(0.0f);
}
ins->gain_samples_to_store = (int)Math::ceil(mix_rate * (attack + sustain) + 1);
ins->gain_bucket_size = (int)(mix_rate * attack);
for (int i = 0; i < ins->gain_samples_to_store; i += ins->gain_bucket_size) {
ins->gain_buckets.push_back(1.0f);
}
return ins;
}
void AudioEffectHardLimiter::set_ceiling_db(float p_ceiling) {
ceiling = p_ceiling;
}
float AudioEffectHardLimiter::get_ceiling_db() const {
return ceiling;
}
float AudioEffectHardLimiter::get_pre_gain_db() const {
return pre_gain;
}
void AudioEffectHardLimiter::set_pre_gain_db(const float p_pre_gain) {
pre_gain = p_pre_gain;
}
float AudioEffectHardLimiter::get_release() const {
return release;
}
void AudioEffectHardLimiter::set_release(const float p_release) {
release = p_release;
}
void AudioEffectHardLimiter::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ceiling_db", "ceiling"), &AudioEffectHardLimiter::set_ceiling_db);
ClassDB::bind_method(D_METHOD("get_ceiling_db"), &AudioEffectHardLimiter::get_ceiling_db);
ClassDB::bind_method(D_METHOD("set_pre_gain_db", "p_pre_gain"), &AudioEffectHardLimiter::set_pre_gain_db);
ClassDB::bind_method(D_METHOD("get_pre_gain_db"), &AudioEffectHardLimiter::get_pre_gain_db);
ClassDB::bind_method(D_METHOD("set_release", "p_release"), &AudioEffectHardLimiter::set_release);
ClassDB::bind_method(D_METHOD("get_release"), &AudioEffectHardLimiter::get_release);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pre_gain_db", PROPERTY_HINT_RANGE, "-24,24,0.01,suffix:dB"), "set_pre_gain_db", "get_pre_gain_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ceiling_db", PROPERTY_HINT_RANGE, "-24,0.0,0.01,suffix:dB"), "set_ceiling_db", "get_ceiling_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "release", PROPERTY_HINT_RANGE, "0.01,3,0.01"), "set_release", "get_release");
}

View file

@ -0,0 +1,89 @@
/**************************************************************************/
/* audio_effect_hard_limiter.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_HARD_LIMITER_H
#define AUDIO_EFFECT_HARD_LIMITER_H
#include "servers/audio/audio_effect.h"
class AudioEffectHardLimiter;
class AudioEffectHardLimiterInstance : public AudioEffectInstance {
GDCLASS(AudioEffectHardLimiterInstance, AudioEffectInstance);
friend class AudioEffectHardLimiter;
Ref<AudioEffectHardLimiter> base;
private:
int sample_cursor = 0;
float release_factor = 0;
float attack_factor = 0;
float gain = 1;
float gain_target = 1;
LocalVector<float> sample_buffer_left;
LocalVector<float> sample_buffer_right;
int gain_samples_to_store = 0;
int gain_bucket_cursor = 0;
int gain_bucket_size = 0;
LocalVector<float> gain_buckets;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectHardLimiter : public AudioEffect {
GDCLASS(AudioEffectHardLimiter, AudioEffect);
friend class AudioEffectHardLimiterInstance;
float pre_gain = 0.0f;
float ceiling = -0.3f;
float sustain = 0.02f;
float release = 0.1f;
const float attack = 0.002;
protected:
static void _bind_methods();
public:
void set_ceiling_db(float p_ceiling);
float get_ceiling_db() const;
void set_release(float p_release);
float get_release() const;
void set_pre_gain_db(float p_pre_gain);
float get_pre_gain_db() const;
Ref<AudioEffectInstance> instantiate() override;
};
#endif // AUDIO_EFFECT_HARD_LIMITER_H

View file

@ -0,0 +1,134 @@
/**************************************************************************/
/* audio_effect_limiter.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_limiter.h"
void AudioEffectLimiterInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float threshdb = base->threshold;
float ceiling = Math::db_to_linear(base->ceiling);
float ceildb = base->ceiling;
float makeup = Math::db_to_linear(ceildb - threshdb);
float sc = -base->soft_clip;
float scv = Math::db_to_linear(sc);
float peakdb = ceildb + 25;
float scmult = Math::abs((ceildb - sc) / (peakdb - sc));
for (int i = 0; i < p_frame_count; i++) {
float spl0 = p_src_frames[i].left;
float spl1 = p_src_frames[i].right;
spl0 = spl0 * makeup;
spl1 = spl1 * makeup;
float sign0 = (spl0 < 0.0 ? -1.0 : 1.0);
float sign1 = (spl1 < 0.0 ? -1.0 : 1.0);
float abs0 = Math::abs(spl0);
float abs1 = Math::abs(spl1);
float overdb0 = Math::linear_to_db(abs0) - ceildb;
float overdb1 = Math::linear_to_db(abs1) - ceildb;
if (abs0 > scv) {
spl0 = sign0 * (scv + Math::db_to_linear(overdb0 * scmult));
}
if (abs1 > scv) {
spl1 = sign1 * (scv + Math::db_to_linear(overdb1 * scmult));
}
spl0 = MIN(ceiling, Math::abs(spl0)) * (spl0 < 0.0 ? -1.0 : 1.0);
spl1 = MIN(ceiling, Math::abs(spl1)) * (spl1 < 0.0 ? -1.0 : 1.0);
p_dst_frames[i].left = spl0;
p_dst_frames[i].right = spl1;
}
}
Ref<AudioEffectInstance> AudioEffectLimiter::instantiate() {
Ref<AudioEffectLimiterInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectLimiter>(this);
return ins;
}
void AudioEffectLimiter::set_threshold_db(float p_threshold) {
threshold = p_threshold;
}
float AudioEffectLimiter::get_threshold_db() const {
return threshold;
}
void AudioEffectLimiter::set_ceiling_db(float p_ceiling) {
ceiling = p_ceiling;
}
float AudioEffectLimiter::get_ceiling_db() const {
return ceiling;
}
void AudioEffectLimiter::set_soft_clip_db(float p_soft_clip) {
soft_clip = p_soft_clip;
}
float AudioEffectLimiter::get_soft_clip_db() const {
return soft_clip;
}
void AudioEffectLimiter::set_soft_clip_ratio(float p_soft_clip) {
soft_clip_ratio = p_soft_clip;
}
float AudioEffectLimiter::get_soft_clip_ratio() const {
return soft_clip_ratio;
}
void AudioEffectLimiter::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ceiling_db", "ceiling"), &AudioEffectLimiter::set_ceiling_db);
ClassDB::bind_method(D_METHOD("get_ceiling_db"), &AudioEffectLimiter::get_ceiling_db);
ClassDB::bind_method(D_METHOD("set_threshold_db", "threshold"), &AudioEffectLimiter::set_threshold_db);
ClassDB::bind_method(D_METHOD("get_threshold_db"), &AudioEffectLimiter::get_threshold_db);
ClassDB::bind_method(D_METHOD("set_soft_clip_db", "soft_clip"), &AudioEffectLimiter::set_soft_clip_db);
ClassDB::bind_method(D_METHOD("get_soft_clip_db"), &AudioEffectLimiter::get_soft_clip_db);
ClassDB::bind_method(D_METHOD("set_soft_clip_ratio", "soft_clip"), &AudioEffectLimiter::set_soft_clip_ratio);
ClassDB::bind_method(D_METHOD("get_soft_clip_ratio"), &AudioEffectLimiter::get_soft_clip_ratio);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ceiling_db", PROPERTY_HINT_RANGE, "-20,-0.1,0.1,suffix:dB"), "set_ceiling_db", "get_ceiling_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "threshold_db", PROPERTY_HINT_RANGE, "-30,0,0.1,suffix:dB"), "set_threshold_db", "get_threshold_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "soft_clip_db", PROPERTY_HINT_RANGE, "0,6,0.1,suffix:dB"), "set_soft_clip_db", "get_soft_clip_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "soft_clip_ratio", PROPERTY_HINT_RANGE, "3,20,0.1"), "set_soft_clip_ratio", "get_soft_clip_ratio");
}
AudioEffectLimiter::AudioEffectLimiter() {
threshold = 0;
ceiling = -0.1;
soft_clip = 2;
soft_clip_ratio = 10;
}

View file

@ -0,0 +1,79 @@
/**************************************************************************/
/* audio_effect_limiter.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_LIMITER_H
#define AUDIO_EFFECT_LIMITER_H
#include "servers/audio/audio_effect.h"
class AudioEffectLimiter;
class AudioEffectLimiterInstance : public AudioEffectInstance {
GDCLASS(AudioEffectLimiterInstance, AudioEffectInstance);
friend class AudioEffectLimiter;
Ref<AudioEffectLimiter> base;
float mix_volume_db;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectLimiter : public AudioEffect {
GDCLASS(AudioEffectLimiter, AudioEffect);
friend class AudioEffectLimiterInstance;
float threshold;
float ceiling;
float soft_clip;
float soft_clip_ratio;
protected:
static void _bind_methods();
public:
void set_threshold_db(float p_threshold);
float get_threshold_db() const;
void set_ceiling_db(float p_ceiling);
float get_ceiling_db() const;
void set_soft_clip_db(float p_soft_clip);
float get_soft_clip_db() const;
void set_soft_clip_ratio(float p_soft_clip);
float get_soft_clip_ratio() const;
Ref<AudioEffectInstance> instantiate() override;
AudioEffectLimiter();
};
#endif // AUDIO_EFFECT_LIMITER_H

View file

@ -0,0 +1,67 @@
/**************************************************************************/
/* audio_effect_panner.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_panner.h"
void AudioEffectPannerInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float lvol = CLAMP(1.0 - base->pan, 0, 1);
float rvol = CLAMP(1.0 + base->pan, 0, 1);
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i].left = p_src_frames[i].left * lvol + p_src_frames[i].right * (1.0 - rvol);
p_dst_frames[i].right = p_src_frames[i].right * rvol + p_src_frames[i].left * (1.0 - lvol);
}
}
Ref<AudioEffectInstance> AudioEffectPanner::instantiate() {
Ref<AudioEffectPannerInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectPanner>(this);
return ins;
}
void AudioEffectPanner::set_pan(float p_cpanume) {
pan = p_cpanume;
}
float AudioEffectPanner::get_pan() const {
return pan;
}
void AudioEffectPanner::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pan", "cpanume"), &AudioEffectPanner::set_pan);
ClassDB::bind_method(D_METHOD("get_pan"), &AudioEffectPanner::get_pan);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_pan", "get_pan");
}
AudioEffectPanner::AudioEffectPanner() {
pan = 0;
}

View file

@ -0,0 +1,64 @@
/**************************************************************************/
/* audio_effect_panner.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_PANNER_H
#define AUDIO_EFFECT_PANNER_H
#include "servers/audio/audio_effect.h"
class AudioEffectPanner;
class AudioEffectPannerInstance : public AudioEffectInstance {
GDCLASS(AudioEffectPannerInstance, AudioEffectInstance);
friend class AudioEffectPanner;
Ref<AudioEffectPanner> base;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectPanner : public AudioEffect {
GDCLASS(AudioEffectPanner, AudioEffect);
friend class AudioEffectPannerInstance;
float pan;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_pan(float p_cpanume);
float get_pan() const;
AudioEffectPanner();
};
#endif // AUDIO_EFFECT_PANNER_H

View file

@ -0,0 +1,160 @@
/**************************************************************************/
/* audio_effect_phaser.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_phaser.h"
#include "core/math/math_funcs.h"
#include "servers/audio_server.h"
void AudioEffectPhaserInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float sampling_rate = AudioServer::get_singleton()->get_mix_rate();
float dmin = base->range_min / (sampling_rate / 2.0);
float dmax = base->range_max / (sampling_rate / 2.0);
float increment = Math_TAU * (base->rate / sampling_rate);
for (int i = 0; i < p_frame_count; i++) {
phase += increment;
while (phase >= Math_TAU) {
phase -= Math_TAU;
}
float d = dmin + (dmax - dmin) * ((sin(phase) + 1.f) / 2.f);
//update filter coeffs
for (int j = 0; j < 6; j++) {
allpass[0][j].delay(d);
allpass[1][j].delay(d);
}
//calculate output
float y = allpass[0][0].update(
allpass[0][1].update(
allpass[0][2].update(
allpass[0][3].update(
allpass[0][4].update(
allpass[0][5].update(p_src_frames[i].left + h.left * base->feedback))))));
h.left = y;
p_dst_frames[i].left = p_src_frames[i].left + y * base->depth;
y = allpass[1][0].update(
allpass[1][1].update(
allpass[1][2].update(
allpass[1][3].update(
allpass[1][4].update(
allpass[1][5].update(p_src_frames[i].right + h.right * base->feedback))))));
h.right = y;
p_dst_frames[i].right = p_src_frames[i].right + y * base->depth;
}
}
Ref<AudioEffectInstance> AudioEffectPhaser::instantiate() {
Ref<AudioEffectPhaserInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectPhaser>(this);
ins->phase = 0;
ins->h = AudioFrame(0, 0);
return ins;
}
void AudioEffectPhaser::set_range_min_hz(float p_hz) {
range_min = p_hz;
}
float AudioEffectPhaser::get_range_min_hz() const {
return range_min;
}
void AudioEffectPhaser::set_range_max_hz(float p_hz) {
range_max = p_hz;
}
float AudioEffectPhaser::get_range_max_hz() const {
return range_max;
}
void AudioEffectPhaser::set_rate_hz(float p_hz) {
rate = p_hz;
}
float AudioEffectPhaser::get_rate_hz() const {
return rate;
}
void AudioEffectPhaser::set_feedback(float p_fbk) {
feedback = p_fbk;
}
float AudioEffectPhaser::get_feedback() const {
return feedback;
}
void AudioEffectPhaser::set_depth(float p_depth) {
depth = p_depth;
}
float AudioEffectPhaser::get_depth() const {
return depth;
}
void AudioEffectPhaser::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_range_min_hz", "hz"), &AudioEffectPhaser::set_range_min_hz);
ClassDB::bind_method(D_METHOD("get_range_min_hz"), &AudioEffectPhaser::get_range_min_hz);
ClassDB::bind_method(D_METHOD("set_range_max_hz", "hz"), &AudioEffectPhaser::set_range_max_hz);
ClassDB::bind_method(D_METHOD("get_range_max_hz"), &AudioEffectPhaser::get_range_max_hz);
ClassDB::bind_method(D_METHOD("set_rate_hz", "hz"), &AudioEffectPhaser::set_rate_hz);
ClassDB::bind_method(D_METHOD("get_rate_hz"), &AudioEffectPhaser::get_rate_hz);
ClassDB::bind_method(D_METHOD("set_feedback", "fbk"), &AudioEffectPhaser::set_feedback);
ClassDB::bind_method(D_METHOD("get_feedback"), &AudioEffectPhaser::get_feedback);
ClassDB::bind_method(D_METHOD("set_depth", "depth"), &AudioEffectPhaser::set_depth);
ClassDB::bind_method(D_METHOD("get_depth"), &AudioEffectPhaser::get_depth);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range_min_hz", PROPERTY_HINT_RANGE, "10,10000,suffix:Hz"), "set_range_min_hz", "get_range_min_hz");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range_max_hz", PROPERTY_HINT_RANGE, "10,10000,suffix:Hz"), "set_range_max_hz", "get_range_max_hz");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rate_hz", PROPERTY_HINT_RANGE, "0.01,20,suffix:Hz"), "set_rate_hz", "get_rate_hz");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback", PROPERTY_HINT_RANGE, "0.1,0.9,0.1"), "set_feedback", "get_feedback");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.1,4,0.1"), "set_depth", "get_depth");
}
AudioEffectPhaser::AudioEffectPhaser() {
range_min = 440;
range_max = 1600;
rate = 0.5;
feedback = 0.7;
depth = 1;
}

View file

@ -0,0 +1,106 @@
/**************************************************************************/
/* audio_effect_phaser.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_PHASER_H
#define AUDIO_EFFECT_PHASER_H
#include "servers/audio/audio_effect.h"
class AudioEffectPhaser;
class AudioEffectPhaserInstance : public AudioEffectInstance {
GDCLASS(AudioEffectPhaserInstance, AudioEffectInstance);
friend class AudioEffectPhaser;
Ref<AudioEffectPhaser> base;
float phase;
AudioFrame h;
class AllpassDelay {
float a, h;
public:
_ALWAYS_INLINE_ void delay(float d) {
a = (1.f - d) / (1.f + d);
}
_ALWAYS_INLINE_ float update(float s) {
float y = s * -a + h;
h = y * a + s;
return y;
}
AllpassDelay() {
a = 0;
h = 0;
}
};
AllpassDelay allpass[2][6];
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectPhaser : public AudioEffect {
GDCLASS(AudioEffectPhaser, AudioEffect);
friend class AudioEffectPhaserInstance;
float range_min;
float range_max;
float rate;
float feedback;
float depth;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_range_min_hz(float p_hz);
float get_range_min_hz() const;
void set_range_max_hz(float p_hz);
float get_range_max_hz() const;
void set_rate_hz(float p_hz);
float get_rate_hz() const;
void set_feedback(float p_fbk);
float get_feedback() const;
void set_depth(float p_depth);
float get_depth() const;
AudioEffectPhaser();
};
#endif // AUDIO_EFFECT_PHASER_H

View file

@ -0,0 +1,358 @@
/**************************************************************************/
/* audio_effect_pitch_shift.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_pitch_shift.h"
#include "core/math/math_funcs.h"
#include "servers/audio_server.h"
/* Thirdparty code, so disable clang-format with Godot style */
/* clang-format off */
/****************************************************************************
*
* NAME: smbPitchShift.cpp
* VERSION: 1.2
* HOME URL: https://blogs.zynaptiq.com/bernsee
* KNOWN BUGS: none
*
* SYNOPSIS: Routine for doing pitch shifting while maintaining
* duration using the Short Time Fourier Transform.
*
* DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5
* (one octave down) and 2. (one octave up). A value of exactly 1 does not change
* the pitch. numSampsToProcess tells the routine how many samples in indata[0...
* numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ...
* numSampsToProcess-1]. The two buffers can be identical (ie. it can process the
* data in-place). fftFrameSize defines the FFT frame size used for the
* processing. Typical values are 1024, 2048 and 4096. It may be any value <=
* MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT
* oversampling factor which also determines the overlap between adjacent STFT
* frames. It should at least be 4 for moderate scaling ratios. A value of 32 is
* recommended for best quality. sampleRate takes the sample rate for the signal
* in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in
* indata[] should be in the range [-1.0, 1.0), which is also the output range
* for the data, make sure you scale the data accordingly (for 16bit signed integers
* you would have to divide (and multiply) by 32768).
*
* COPYRIGHT 1999-2015 Stephan M. Bernsee <s.bernsee [AT] zynaptiq [DOT] com>
*
* The Wide Open License (WOL)
*
* Permission to use, copy, modify, distribute and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice and this license appear in all source copies.
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
* ANY KIND. See https://dspguru.com/wide-open-license/ for more information.
*
*****************************************************************************/
void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, long osamp, float sampleRate, float *indata, float *outdata,int stride) {
/*
Routine smbPitchShift(). See top of file for explanation
Purpose: doing pitch shifting while maintaining duration using the Short
Time Fourier Transform.
Author: (c)1999-2015 Stephan M. Bernsee <s.bernsee [AT] zynaptiq [DOT] com>
*/
double magn, phase, tmp, window, real, imag;
double freqPerBin, expct;
long i,k, qpd, index, inFifoLatency, stepSize, fftFrameSize2;
/* set up some handy variables */
fftFrameSize2 = fftFrameSize/2;
stepSize = fftFrameSize/osamp;
freqPerBin = sampleRate/(double)fftFrameSize;
expct = 2.*Math_PI*(double)stepSize/(double)fftFrameSize;
inFifoLatency = fftFrameSize-stepSize;
if (gRover == 0) { gRover = inFifoLatency;
}
/* initialize our static arrays */
/* main processing loop */
for (i = 0; i < numSampsToProcess; i++){
/* As long as we have not yet collected enough data just read in */
gInFIFO[gRover] = indata[i*stride];
outdata[i*stride] = gOutFIFO[gRover-inFifoLatency];
gRover++;
/* now we have enough data for processing */
if (gRover >= fftFrameSize) {
gRover = inFifoLatency;
/* do windowing and re,im interleave */
for (k = 0; k < fftFrameSize;k++) {
window = -.5*cos(2.*Math_PI*(double)k/(double)fftFrameSize)+.5;
gFFTworksp[2*k] = gInFIFO[k] * window;
gFFTworksp[2*k+1] = 0.;
}
/* ***************** ANALYSIS ******************* */
/* do transform */
smbFft(gFFTworksp, fftFrameSize, -1);
/* this is the analysis step */
for (k = 0; k <= fftFrameSize2; k++) {
/* de-interlace FFT buffer */
real = gFFTworksp[2*k];
imag = gFFTworksp[2*k+1];
/* compute magnitude and phase */
magn = 2.*sqrt(real*real + imag*imag);
phase = atan2(imag,real);
/* compute phase difference */
tmp = phase - gLastPhase[k];
gLastPhase[k] = phase;
/* subtract expected phase difference */
tmp -= (double)k*expct;
/* map delta phase into +/- Pi interval */
qpd = tmp/Math_PI;
if (qpd >= 0) { qpd += qpd&1;
} else { qpd -= qpd&1;
}
tmp -= Math_PI*(double)qpd;
/* get deviation from bin frequency from the +/- Pi interval */
tmp = osamp*tmp/(2.*Math_PI);
/* compute the k-th partials' true frequency */
tmp = (double)k*freqPerBin + tmp*freqPerBin;
/* store magnitude and true frequency in analysis arrays */
gAnaMagn[k] = magn;
gAnaFreq[k] = tmp;
}
/* ***************** PROCESSING ******************* */
/* this does the actual pitch shifting */
memset(gSynMagn, 0, fftFrameSize*sizeof(float));
memset(gSynFreq, 0, fftFrameSize*sizeof(float));
for (k = 0; k <= fftFrameSize2; k++) {
index = k*pitchShift;
if (index <= fftFrameSize2) {
gSynMagn[index] += gAnaMagn[k];
gSynFreq[index] = gAnaFreq[k] * pitchShift;
}
}
/* ***************** SYNTHESIS ******************* */
/* this is the synthesis step */
for (k = 0; k <= fftFrameSize2; k++) {
/* get magnitude and true frequency from synthesis arrays */
magn = gSynMagn[k];
tmp = gSynFreq[k];
/* subtract bin mid frequency */
tmp -= (double)k*freqPerBin;
/* get bin deviation from freq deviation */
tmp /= freqPerBin;
/* take osamp into account */
tmp = 2.*Math_PI*tmp/osamp;
/* add the overlap phase advance back in */
tmp += (double)k*expct;
/* accumulate delta phase to get bin phase */
gSumPhase[k] += tmp;
phase = gSumPhase[k];
/* get real and imag part and re-interleave */
gFFTworksp[2*k] = magn*cos(phase);
gFFTworksp[2*k+1] = magn*sin(phase);
}
/* zero negative frequencies */
for (k = fftFrameSize+2; k < 2*fftFrameSize; k++) { gFFTworksp[k] = 0.;
}
/* do inverse transform */
smbFft(gFFTworksp, fftFrameSize, 1);
/* do windowing and add to output accumulator */
for(k=0; k < fftFrameSize; k++) {
window = -.5*cos(2.*Math_PI*(double)k/(double)fftFrameSize)+.5;
gOutputAccum[k] += 2.*window*gFFTworksp[2*k]/(fftFrameSize2*osamp);
}
for (k = 0; k < stepSize; k++) { gOutFIFO[k] = gOutputAccum[k];
}
/* shift accumulator */
memmove(gOutputAccum, gOutputAccum+stepSize, fftFrameSize*sizeof(float));
/* move input FIFO */
for (k = 0; k < inFifoLatency; k++) { gInFIFO[k] = gInFIFO[k+stepSize];
}
}
}
}
void SMBPitchShift::smbFft(float *fftBuffer, long fftFrameSize, long sign)
/*
FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse)
Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the
time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes
and returns the cosine and sine parts in an interleaved manner, ie.
fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize
must be a power of 2. It expects a complex input signal (see footnote 2),
ie. when working with 'common' audio signals our input signal has to be
passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform
of the frequencies of interest is in fftBuffer[0...fftFrameSize].
*/
{
float wr, wi, arg, *p1, *p2, temp;
float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i;
long i, bitm, j, le, le2, k;
for (i = 2; i < 2*fftFrameSize-2; i += 2) {
for (bitm = 2, j = 0; bitm < 2*fftFrameSize; bitm <<= 1) {
if (i & bitm) { j++;
}
j <<= 1;
}
if (i < j) {
p1 = fftBuffer+i; p2 = fftBuffer+j;
temp = *p1; *(p1++) = *p2;
*(p2++) = temp; temp = *p1;
*p1 = *p2; *p2 = temp;
}
}
for (k = 0, le = 2; k < (long)(log((double)fftFrameSize)/log(2.)+.5); k++) {
le <<= 1;
le2 = le>>1;
ur = 1.0;
ui = 0.0;
arg = Math_PI / (le2>>1);
wr = cos(arg);
wi = sign*sin(arg);
for (j = 0; j < le2; j += 2) {
p1r = fftBuffer+j; p1i = p1r+1;
p2r = p1r+le2; p2i = p2r+1;
for (i = j; i < 2*fftFrameSize; i += le) {
tr = *p2r * ur - *p2i * ui;
ti = *p2r * ui + *p2i * ur;
*p2r = *p1r - tr; *p2i = *p1i - ti;
*p1r += tr; *p1i += ti;
p1r += le; p1i += le;
p2r += le; p2i += le;
}
tr = ur*wr - ui*wi;
ui = ur*wi + ui*wr;
ur = tr;
}
}
}
/* Godot code again */
/* clang-format on */
void AudioEffectPitchShiftInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float sample_rate = AudioServer::get_singleton()->get_mix_rate();
float *in_l = (float *)p_src_frames;
float *in_r = in_l + 1;
float *out_l = (float *)p_dst_frames;
float *out_r = out_l + 1;
shift_l.PitchShift(base->pitch_scale, p_frame_count, fft_size, base->oversampling, sample_rate, in_l, out_l, 2);
shift_r.PitchShift(base->pitch_scale, p_frame_count, fft_size, base->oversampling, sample_rate, in_r, out_r, 2);
}
Ref<AudioEffectInstance> AudioEffectPitchShift::instantiate() {
Ref<AudioEffectPitchShiftInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectPitchShift>(this);
static const int fft_sizes[FFT_SIZE_MAX] = { 256, 512, 1024, 2048, 4096 };
ins->fft_size = fft_sizes[fft_size];
return ins;
}
void AudioEffectPitchShift::set_pitch_scale(float p_pitch_scale) {
ERR_FAIL_COND(!(p_pitch_scale > 0.0));
pitch_scale = p_pitch_scale;
}
float AudioEffectPitchShift::get_pitch_scale() const {
return pitch_scale;
}
void AudioEffectPitchShift::set_oversampling(int p_oversampling) {
ERR_FAIL_COND(p_oversampling < 4);
oversampling = p_oversampling;
}
int AudioEffectPitchShift::get_oversampling() const {
return oversampling;
}
void AudioEffectPitchShift::set_fft_size(FFTSize p_fft_size) {
ERR_FAIL_INDEX(p_fft_size, FFT_SIZE_MAX);
fft_size = p_fft_size;
}
AudioEffectPitchShift::FFTSize AudioEffectPitchShift::get_fft_size() const {
return fft_size;
}
void AudioEffectPitchShift::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pitch_scale", "rate"), &AudioEffectPitchShift::set_pitch_scale);
ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioEffectPitchShift::get_pitch_scale);
ClassDB::bind_method(D_METHOD("set_oversampling", "amount"), &AudioEffectPitchShift::set_oversampling);
ClassDB::bind_method(D_METHOD("get_oversampling"), &AudioEffectPitchShift::get_oversampling);
ClassDB::bind_method(D_METHOD("set_fft_size", "size"), &AudioEffectPitchShift::set_fft_size);
ClassDB::bind_method(D_METHOD("get_fft_size"), &AudioEffectPitchShift::get_fft_size);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "4,32,1"), "set_oversampling", "get_oversampling");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fft_size", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096"), "set_fft_size", "get_fft_size");
BIND_ENUM_CONSTANT(FFT_SIZE_256);
BIND_ENUM_CONSTANT(FFT_SIZE_512);
BIND_ENUM_CONSTANT(FFT_SIZE_1024);
BIND_ENUM_CONSTANT(FFT_SIZE_2048);
BIND_ENUM_CONSTANT(FFT_SIZE_4096);
BIND_ENUM_CONSTANT(FFT_SIZE_MAX);
}

View file

@ -0,0 +1,114 @@
/**************************************************************************/
/* audio_effect_pitch_shift.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_PITCH_SHIFT_H
#define AUDIO_EFFECT_PITCH_SHIFT_H
#include "servers/audio/audio_effect.h"
class SMBPitchShift {
enum {
MAX_FRAME_LENGTH = 8192
};
float gInFIFO[MAX_FRAME_LENGTH] = {};
float gOutFIFO[MAX_FRAME_LENGTH] = {};
float gFFTworksp[2 * MAX_FRAME_LENGTH] = {};
float gLastPhase[MAX_FRAME_LENGTH / 2 + 1] = {};
float gSumPhase[MAX_FRAME_LENGTH / 2 + 1] = {};
float gOutputAccum[2 * MAX_FRAME_LENGTH] = {};
float gAnaFreq[MAX_FRAME_LENGTH] = {};
float gAnaMagn[MAX_FRAME_LENGTH] = {};
float gSynFreq[MAX_FRAME_LENGTH] = {};
float gSynMagn[MAX_FRAME_LENGTH] = {};
long gRover = 0;
void smbFft(float *fftBuffer, long fftFrameSize, long sign);
public:
void PitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, long osamp, float sampleRate, float *indata, float *outdata, int stride);
};
class AudioEffectPitchShift;
class AudioEffectPitchShiftInstance : public AudioEffectInstance {
GDCLASS(AudioEffectPitchShiftInstance, AudioEffectInstance);
friend class AudioEffectPitchShift;
Ref<AudioEffectPitchShift> base;
int fft_size = 0;
SMBPitchShift shift_l;
SMBPitchShift shift_r;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
};
class AudioEffectPitchShift : public AudioEffect {
GDCLASS(AudioEffectPitchShift, AudioEffect);
public:
friend class AudioEffectPitchShiftInstance;
enum FFTSize {
FFT_SIZE_256,
FFT_SIZE_512,
FFT_SIZE_1024,
FFT_SIZE_2048,
FFT_SIZE_4096,
FFT_SIZE_MAX
};
float pitch_scale = 1.0;
int oversampling = 4;
FFTSize fft_size = FFT_SIZE_2048;
float wet = 0.0;
float dry = 0.0;
bool filter = false;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_pitch_scale(float p_pitch_scale);
float get_pitch_scale() const;
void set_oversampling(int p_oversampling);
int get_oversampling() const;
void set_fft_size(FFTSize);
FFTSize get_fft_size() const;
};
VARIANT_ENUM_CAST(AudioEffectPitchShift::FFTSize);
#endif // AUDIO_EFFECT_PITCH_SHIFT_H

View file

@ -0,0 +1,295 @@
/**************************************************************************/
/* audio_effect_record.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_record.h"
#ifdef TOOLS_ENABLED
// FIXME: This file shouldn't depend on editor stuff.
#include "editor/import/resource_importer_wav.h"
#endif
void AudioEffectRecordInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
if (!is_recording) {
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i] = p_src_frames[i];
}
return;
}
//Add incoming audio frames to the IO ring buffer
const AudioFrame *src = p_src_frames;
AudioFrame *rb_buf = ring_buffer.ptrw();
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i] = p_src_frames[i];
rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i];
ring_buffer_pos++;
}
}
void AudioEffectRecordInstance::_update_buffer() {
//Case: Frames are remaining in the buffer
while (ring_buffer_read_pos < ring_buffer_pos) {
//Read from the buffer into recording_data
_io_store_buffer();
}
}
void AudioEffectRecordInstance::_update(void *userdata) {
AudioEffectRecordInstance *ins = static_cast<AudioEffectRecordInstance *>(userdata);
ins->_update_buffer();
}
bool AudioEffectRecordInstance::process_silence() const {
return true;
}
void AudioEffectRecordInstance::_io_thread_process() {
while (is_recording) {
_update_buffer();
if (is_recording) {
//Wait to avoid too much busy-wait
OS::get_singleton()->delay_usec(500);
}
}
}
void AudioEffectRecordInstance::_io_store_buffer() {
int to_read = ring_buffer_pos - ring_buffer_read_pos;
AudioFrame *rb_buf = ring_buffer.ptrw();
while (to_read) {
AudioFrame buffered_frame = rb_buf[ring_buffer_read_pos & ring_buffer_mask];
recording_data.push_back(buffered_frame.left);
recording_data.push_back(buffered_frame.right);
ring_buffer_read_pos++;
to_read--;
}
}
void AudioEffectRecordInstance::_thread_callback(void *_instance) {
AudioEffectRecordInstance *aeri = reinterpret_cast<AudioEffectRecordInstance *>(_instance);
aeri->_io_thread_process();
}
void AudioEffectRecordInstance::init() {
//Reset recorder status
ring_buffer_pos = 0;
ring_buffer_read_pos = 0;
//We start a new recording
recording_data.clear(); //Clear data completely and reset length
is_recording = true;
io_thread.start(_thread_callback, this);
}
void AudioEffectRecordInstance::finish() {
is_recording = false;
if (io_thread.is_started()) {
io_thread.wait_to_finish();
}
}
Ref<AudioEffectInstance> AudioEffectRecord::instantiate() {
Ref<AudioEffectRecordInstance> ins;
ins.instantiate();
ins->is_recording = false;
//Re-using the buffer size calculations from audio_effect_delay.cpp
float ring_buffer_max_size = IO_BUFFER_SIZE_MS;
ring_buffer_max_size /= 1000.0; //convert to seconds
ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
int ringbuff_size = ring_buffer_max_size;
int bits = 0;
while (ringbuff_size > 0) {
bits++;
ringbuff_size /= 2;
}
ringbuff_size = 1 << bits;
ins->ring_buffer_mask = ringbuff_size - 1;
ins->ring_buffer_pos = 0;
ins->ring_buffer.resize(ringbuff_size);
ins->ring_buffer_read_pos = 0;
ensure_thread_stopped();
bool is_currently_recording = false;
if (current_instance != nullptr) {
is_currently_recording = current_instance->is_recording;
}
if (is_currently_recording) {
ins->init();
}
current_instance = ins;
return ins;
}
void AudioEffectRecord::ensure_thread_stopped() {
if (current_instance != nullptr) {
current_instance->finish();
}
}
void AudioEffectRecord::set_recording_active(bool p_record) {
if (p_record) {
if (current_instance == nullptr) {
WARN_PRINT("Recording should not be set as active before Godot has initialized.");
return;
}
ensure_thread_stopped();
current_instance->init();
} else {
if (current_instance != nullptr) {
current_instance->is_recording = false;
}
}
}
bool AudioEffectRecord::is_recording_active() const {
if (current_instance == nullptr) {
return false;
} else {
return current_instance->is_recording;
}
}
void AudioEffectRecord::set_format(AudioStreamWAV::Format p_format) {
format = p_format;
}
AudioStreamWAV::Format AudioEffectRecord::get_format() const {
return format;
}
Ref<AudioStreamWAV> AudioEffectRecord::get_recording() const {
AudioStreamWAV::Format dst_format = format;
bool stereo = true; //forcing mono is not implemented
Vector<uint8_t> dst_data;
ERR_FAIL_COND_V(current_instance.is_null(), nullptr);
ERR_FAIL_COND_V(current_instance->recording_data.is_empty(), nullptr);
if (dst_format == AudioStreamWAV::FORMAT_8_BITS) {
int data_size = current_instance->recording_data.size();
dst_data.resize(data_size);
uint8_t *w = dst_data.ptrw();
for (int i = 0; i < data_size; i++) {
int8_t v = CLAMP(current_instance->recording_data[i] * 128, -128, 127);
w[i] = v;
}
} else if (dst_format == AudioStreamWAV::FORMAT_16_BITS) {
int data_size = current_instance->recording_data.size();
dst_data.resize(data_size * 2);
uint8_t *w = dst_data.ptrw();
for (int i = 0; i < data_size; i++) {
int16_t v = CLAMP(current_instance->recording_data[i] * 32768, -32768, 32767);
encode_uint16(v, &w[i * 2]);
}
} else if (dst_format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
//byte interleave
Vector<float> left;
Vector<float> right;
int tframes = current_instance->recording_data.size() / 2;
left.resize(tframes);
right.resize(tframes);
for (int i = 0; i < tframes; i++) {
left.set(i, current_instance->recording_data[i * 2 + 0]);
right.set(i, current_instance->recording_data[i * 2 + 1]);
}
Vector<uint8_t> bleft;
Vector<uint8_t> bright;
#ifdef TOOLS_ENABLED
ResourceImporterWAV::_compress_ima_adpcm(left, bleft);
ResourceImporterWAV::_compress_ima_adpcm(right, bright);
#else
ERR_PRINT("AudioEffectRecord cannot do IMA ADPCM compression at runtime.");
#endif
int dl = bleft.size();
dst_data.resize(dl * 2);
uint8_t *w = dst_data.ptrw();
const uint8_t *rl = bleft.ptr();
const uint8_t *rr = bright.ptr();
for (int i = 0; i < dl; i++) {
w[i * 2 + 0] = rl[i];
w[i * 2 + 1] = rr[i];
}
} else {
ERR_PRINT("Format not implemented.");
}
Ref<AudioStreamWAV> sample;
sample.instantiate();
sample->set_data(dst_data);
sample->set_format(dst_format);
sample->set_mix_rate(AudioServer::get_singleton()->get_mix_rate());
sample->set_loop_mode(AudioStreamWAV::LOOP_DISABLED);
sample->set_loop_begin(0);
sample->set_loop_end(0);
sample->set_stereo(stereo);
return sample;
}
void AudioEffectRecord::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_recording_active", "record"), &AudioEffectRecord::set_recording_active);
ClassDB::bind_method(D_METHOD("is_recording_active"), &AudioEffectRecord::is_recording_active);
ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioEffectRecord::set_format);
ClassDB::bind_method(D_METHOD("get_format"), &AudioEffectRecord::get_format);
ClassDB::bind_method(D_METHOD("get_recording"), &AudioEffectRecord::get_recording);
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
}
AudioEffectRecord::AudioEffectRecord() {
format = AudioStreamWAV::FORMAT_16_BITS;
}
AudioEffectRecord::~AudioEffectRecord() {
ensure_thread_stopped();
}

View file

@ -0,0 +1,101 @@
/**************************************************************************/
/* audio_effect_record.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_RECORD_H
#define AUDIO_EFFECT_RECORD_H
#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/os/thread.h"
#include "scene/resources/audio_stream_wav.h"
#include "servers/audio/audio_effect.h"
#include "servers/audio_server.h"
class AudioEffectRecord;
class AudioEffectRecordInstance : public AudioEffectInstance {
GDCLASS(AudioEffectRecordInstance, AudioEffectInstance);
friend class AudioEffectRecord;
bool is_recording;
Thread io_thread;
Vector<AudioFrame> ring_buffer;
Vector<float> recording_data;
unsigned int ring_buffer_pos;
unsigned int ring_buffer_mask;
unsigned int ring_buffer_read_pos;
void _io_thread_process();
void _io_store_buffer();
static void _thread_callback(void *_instance);
void _init_recording();
void _update_buffer();
static void _update(void *userdata);
public:
void init();
void finish();
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
virtual bool process_silence() const override;
};
class AudioEffectRecord : public AudioEffect {
GDCLASS(AudioEffectRecord, AudioEffect);
friend class AudioEffectRecordInstance;
enum {
IO_BUFFER_SIZE_MS = 1500
};
Ref<AudioEffectRecordInstance> current_instance;
AudioStreamWAV::Format format;
void ensure_thread_stopped();
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_recording_active(bool p_record);
bool is_recording_active() const;
void set_format(AudioStreamWAV::Format p_format);
AudioStreamWAV::Format get_format() const;
Ref<AudioStreamWAV> get_recording() const;
AudioEffectRecord();
~AudioEffectRecord();
};
#endif // AUDIO_EFFECT_RECORD_H

View file

@ -0,0 +1,199 @@
/**************************************************************************/
/* audio_effect_reverb.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_reverb.h"
#include "servers/audio_server.h"
void AudioEffectReverbInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
for (int i = 0; i < 2; i++) {
Reverb &r = reverb[i];
r.set_predelay(base->predelay);
r.set_predelay_feedback(base->predelay_fb);
r.set_highpass(base->hpf);
r.set_room_size(base->room_size);
r.set_damp(base->damping);
r.set_extra_spread(base->spread);
r.set_wet(base->wet);
r.set_dry(base->dry);
}
int todo = p_frame_count;
int offset = 0;
while (todo) {
int to_mix = MIN(todo, Reverb::INPUT_BUFFER_MAX_SIZE);
for (int j = 0; j < to_mix; j++) {
tmp_src[j] = p_src_frames[offset + j].left;
}
reverb[0].process(tmp_src, tmp_dst, to_mix);
for (int j = 0; j < to_mix; j++) {
p_dst_frames[offset + j].left = tmp_dst[j];
tmp_src[j] = p_src_frames[offset + j].right;
}
reverb[1].process(tmp_src, tmp_dst, to_mix);
for (int j = 0; j < to_mix; j++) {
p_dst_frames[offset + j].right = tmp_dst[j];
}
offset += to_mix;
todo -= to_mix;
}
}
AudioEffectReverbInstance::AudioEffectReverbInstance() {
reverb[0].set_mix_rate(AudioServer::get_singleton()->get_mix_rate());
reverb[0].set_extra_spread_base(0);
reverb[1].set_mix_rate(AudioServer::get_singleton()->get_mix_rate());
reverb[1].set_extra_spread_base(0.000521); //for stereo effect
}
Ref<AudioEffectInstance> AudioEffectReverb::instantiate() {
Ref<AudioEffectReverbInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectReverb>(this);
return ins;
}
void AudioEffectReverb::set_predelay_msec(float p_msec) {
predelay = p_msec;
}
void AudioEffectReverb::set_predelay_feedback(float p_feedback) {
predelay_fb = CLAMP(p_feedback, 0, 0.98);
}
void AudioEffectReverb::set_room_size(float p_size) {
room_size = p_size;
}
void AudioEffectReverb::set_damping(float p_damping) {
damping = p_damping;
}
void AudioEffectReverb::set_spread(float p_spread) {
spread = p_spread;
}
void AudioEffectReverb::set_dry(float p_dry) {
dry = p_dry;
}
void AudioEffectReverb::set_wet(float p_wet) {
wet = p_wet;
}
void AudioEffectReverb::set_hpf(float p_hpf) {
hpf = p_hpf;
}
float AudioEffectReverb::get_predelay_msec() const {
return predelay;
}
float AudioEffectReverb::get_predelay_feedback() const {
return predelay_fb;
}
float AudioEffectReverb::get_room_size() const {
return room_size;
}
float AudioEffectReverb::get_damping() const {
return damping;
}
float AudioEffectReverb::get_spread() const {
return spread;
}
float AudioEffectReverb::get_dry() const {
return dry;
}
float AudioEffectReverb::get_wet() const {
return wet;
}
float AudioEffectReverb::get_hpf() const {
return hpf;
}
void AudioEffectReverb::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_predelay_msec", "msec"), &AudioEffectReverb::set_predelay_msec);
ClassDB::bind_method(D_METHOD("get_predelay_msec"), &AudioEffectReverb::get_predelay_msec);
ClassDB::bind_method(D_METHOD("set_predelay_feedback", "feedback"), &AudioEffectReverb::set_predelay_feedback);
ClassDB::bind_method(D_METHOD("get_predelay_feedback"), &AudioEffectReverb::get_predelay_feedback);
ClassDB::bind_method(D_METHOD("set_room_size", "size"), &AudioEffectReverb::set_room_size);
ClassDB::bind_method(D_METHOD("get_room_size"), &AudioEffectReverb::get_room_size);
ClassDB::bind_method(D_METHOD("set_damping", "amount"), &AudioEffectReverb::set_damping);
ClassDB::bind_method(D_METHOD("get_damping"), &AudioEffectReverb::get_damping);
ClassDB::bind_method(D_METHOD("set_spread", "amount"), &AudioEffectReverb::set_spread);
ClassDB::bind_method(D_METHOD("get_spread"), &AudioEffectReverb::get_spread);
ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectReverb::set_dry);
ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectReverb::get_dry);
ClassDB::bind_method(D_METHOD("set_wet", "amount"), &AudioEffectReverb::set_wet);
ClassDB::bind_method(D_METHOD("get_wet"), &AudioEffectReverb::get_wet);
ClassDB::bind_method(D_METHOD("set_hpf", "amount"), &AudioEffectReverb::set_hpf);
ClassDB::bind_method(D_METHOD("get_hpf"), &AudioEffectReverb::get_hpf);
ADD_GROUP("Predelay", "predelay_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "predelay_msec", PROPERTY_HINT_RANGE, "20,500,1,suffix:ms"), "set_predelay_msec", "get_predelay_msec");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "predelay_feedback", PROPERTY_HINT_RANGE, "0,0.98,0.01"), "set_predelay_feedback", "get_predelay_feedback");
ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "room_size", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_room_size", "get_room_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping", "get_damping");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_spread", "get_spread");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "hipass", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_hpf", "get_hpf");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wet", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_wet", "get_wet");
}
AudioEffectReverb::AudioEffectReverb() {
predelay = 150;
predelay_fb = 0.4;
hpf = 0;
room_size = 0.8;
damping = 0.5;
spread = 1.0;
dry = 1.0;
wet = 0.5;
}

View file

@ -0,0 +1,97 @@
/**************************************************************************/
/* audio_effect_reverb.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_REVERB_H
#define AUDIO_EFFECT_REVERB_H
#include "servers/audio/audio_effect.h"
#include "servers/audio/effects/reverb_filter.h"
class AudioEffectReverb;
class AudioEffectReverbInstance : public AudioEffectInstance {
GDCLASS(AudioEffectReverbInstance, AudioEffectInstance);
Ref<AudioEffectReverb> base;
float tmp_src[Reverb::INPUT_BUFFER_MAX_SIZE];
float tmp_dst[Reverb::INPUT_BUFFER_MAX_SIZE];
friend class AudioEffectReverb;
Reverb reverb[2];
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
AudioEffectReverbInstance();
};
class AudioEffectReverb : public AudioEffect {
GDCLASS(AudioEffectReverb, AudioEffect);
friend class AudioEffectReverbInstance;
float predelay;
float predelay_fb;
float hpf;
float room_size;
float damping;
float spread;
float dry;
float wet;
protected:
static void _bind_methods();
public:
void set_predelay_msec(float p_msec);
void set_predelay_feedback(float p_feedback);
void set_room_size(float p_size);
void set_damping(float p_damping);
void set_spread(float p_spread);
void set_dry(float p_dry);
void set_wet(float p_wet);
void set_hpf(float p_hpf);
float get_predelay_msec() const;
float get_predelay_feedback() const;
float get_room_size() const;
float get_damping() const;
float get_spread() const;
float get_dry() const;
float get_wet() const;
float get_hpf() const;
Ref<AudioEffectInstance> instantiate() override;
AudioEffectReverb();
};
#endif // AUDIO_EFFECT_REVERB_H

View file

@ -0,0 +1,283 @@
/**************************************************************************/
/* audio_effect_spectrum_analyzer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_spectrum_analyzer.h"
#include "servers/audio_server.h"
static void smbFft(float *fftBuffer, long fftFrameSize, long sign)
/*
FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse)
Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the
time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes
and returns the cosine and sine parts in an interleaved manner, ie.
fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize
must be a power of 2. It expects a complex input signal (see footnote 2),
ie. when working with 'common' audio signals our input signal has to be
passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform
of the frequencies of interest is in fftBuffer[0...fftFrameSize].
*/
{
float wr, wi, arg, *p1, *p2, temp;
float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i;
long i, bitm, j, le, le2, k;
for (i = 2; i < 2 * fftFrameSize - 2; i += 2) {
for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1) {
if (i & bitm) {
j++;
}
j <<= 1;
}
if (i < j) {
p1 = fftBuffer + i;
p2 = fftBuffer + j;
temp = *p1;
*(p1++) = *p2;
*(p2++) = temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
}
for (k = 0, le = 2; k < (long)(log((double)fftFrameSize) / log(2.) + .5); k++) {
le <<= 1;
le2 = le >> 1;
ur = 1.0;
ui = 0.0;
arg = Math_PI / (le2 >> 1);
wr = cos(arg);
wi = sign * sin(arg);
for (j = 0; j < le2; j += 2) {
p1r = fftBuffer + j;
p1i = p1r + 1;
p2r = p1r + le2;
p2i = p2r + 1;
for (i = j; i < 2 * fftFrameSize; i += le) {
tr = *p2r * ur - *p2i * ui;
ti = *p2r * ui + *p2i * ur;
*p2r = *p1r - tr;
*p2i = *p1i - ti;
*p1r += tr;
*p1i += ti;
p1r += le;
p1i += le;
p2r += le;
p2i += le;
}
tr = ur * wr - ui * wi;
ui = ur * wi + ui * wr;
ur = tr;
}
}
}
void AudioEffectSpectrumAnalyzerInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
uint64_t time = OS::get_singleton()->get_ticks_usec();
//copy everything over first, since this only really does capture
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i] = p_src_frames[i];
}
//capture spectrum
while (p_frame_count) {
int to_fill = fft_size * 2 - temporal_fft_pos;
to_fill = MIN(to_fill, p_frame_count);
const double to_fill_step = Math_TAU / (double)fft_size;
float *fftw = temporal_fft.ptrw();
for (int i = 0; i < to_fill; i++) { //left and right buffers
float window = -0.5 * Math::cos(to_fill_step * (double)temporal_fft_pos) + 0.5;
fftw[temporal_fft_pos * 2] = window * p_src_frames->left;
fftw[temporal_fft_pos * 2 + 1] = 0;
fftw[(temporal_fft_pos + fft_size * 2) * 2] = window * p_src_frames->right;
fftw[(temporal_fft_pos + fft_size * 2) * 2 + 1] = 0;
++p_src_frames;
++temporal_fft_pos;
}
p_frame_count -= to_fill;
if (temporal_fft_pos == fft_size * 2) {
//time to do a FFT
smbFft(fftw, fft_size * 2, -1);
smbFft(fftw + fft_size * 4, fft_size * 2, -1);
int next = (fft_pos + 1) % fft_count;
AudioFrame *hw = (AudioFrame *)fft_history[next].ptr(); //do not use write, avoid cow
for (int i = 0; i < fft_size; i++) {
//abs(vec)/fft_size normalizes each frequency
hw[i].left = Vector2(fftw[i * 2], fftw[i * 2 + 1]).length() / float(fft_size);
hw[i].right = Vector2(fftw[fft_size * 4 + i * 2], fftw[fft_size * 4 + i * 2 + 1]).length() / float(fft_size);
}
fft_pos = next; //swap
temporal_fft_pos = 0;
}
}
//determine time of capture
double remainer_sec = (temporal_fft_pos / mix_rate); //subtract remainder from mix time
last_fft_time = time - uint64_t(remainer_sec * 1000000.0);
}
void AudioEffectSpectrumAnalyzerInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_magnitude_for_frequency_range", "from_hz", "to_hz", "mode"), &AudioEffectSpectrumAnalyzerInstance::get_magnitude_for_frequency_range, DEFVAL(MAGNITUDE_MAX));
BIND_ENUM_CONSTANT(MAGNITUDE_AVERAGE);
BIND_ENUM_CONSTANT(MAGNITUDE_MAX);
}
Vector2 AudioEffectSpectrumAnalyzerInstance::get_magnitude_for_frequency_range(float p_begin, float p_end, MagnitudeMode p_mode) const {
if (last_fft_time == 0) {
return Vector2();
}
uint64_t time = OS::get_singleton()->get_ticks_usec();
float diff = double(time - last_fft_time) / 1000000.0 + base->get_tap_back_pos();
diff -= AudioServer::get_singleton()->get_output_latency();
float fft_time_size = float(fft_size) / mix_rate;
int fft_index = fft_pos;
while (diff > fft_time_size) {
diff -= fft_time_size;
fft_index -= 1;
if (fft_index < 0) {
fft_index = fft_count - 1;
}
}
int begin_pos = p_begin * fft_size / (mix_rate * 0.5);
int end_pos = p_end * fft_size / (mix_rate * 0.5);
begin_pos = CLAMP(begin_pos, 0, fft_size - 1);
end_pos = CLAMP(end_pos, 0, fft_size - 1);
if (begin_pos > end_pos) {
SWAP(begin_pos, end_pos);
}
const AudioFrame *r = fft_history[fft_index].ptr();
if (p_mode == MAGNITUDE_AVERAGE) {
Vector2 avg;
for (int i = begin_pos; i <= end_pos; i++) {
avg += Vector2(r[i]);
}
avg /= float(end_pos - begin_pos + 1);
return avg;
} else {
Vector2 max;
for (int i = begin_pos; i <= end_pos; i++) {
max.x = MAX(max.x, r[i].left);
max.y = MAX(max.y, r[i].right);
}
return max;
}
}
Ref<AudioEffectInstance> AudioEffectSpectrumAnalyzer::instantiate() {
Ref<AudioEffectSpectrumAnalyzerInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectSpectrumAnalyzer>(this);
static const int fft_sizes[FFT_SIZE_MAX] = { 256, 512, 1024, 2048, 4096 };
ins->fft_size = fft_sizes[fft_size];
ins->mix_rate = AudioServer::get_singleton()->get_mix_rate();
ins->fft_count = (buffer_length / (float(ins->fft_size) / ins->mix_rate)) + 1;
ins->fft_pos = 0;
ins->last_fft_time = 0;
ins->fft_history.resize(ins->fft_count);
ins->temporal_fft.resize(ins->fft_size * 8); //x2 stereo, x2 amount of samples for freqs, x2 for input
ins->temporal_fft_pos = 0;
for (int i = 0; i < ins->fft_count; i++) {
ins->fft_history.write[i].resize(ins->fft_size); //only magnitude matters
for (int j = 0; j < ins->fft_size; j++) {
ins->fft_history.write[i].write[j] = AudioFrame(0, 0);
}
}
return ins;
}
void AudioEffectSpectrumAnalyzer::set_buffer_length(float p_seconds) {
buffer_length = p_seconds;
}
float AudioEffectSpectrumAnalyzer::get_buffer_length() const {
return buffer_length;
}
void AudioEffectSpectrumAnalyzer::set_tap_back_pos(float p_seconds) {
tapback_pos = p_seconds;
}
float AudioEffectSpectrumAnalyzer::get_tap_back_pos() const {
return tapback_pos;
}
void AudioEffectSpectrumAnalyzer::set_fft_size(FFTSize p_fft_size) {
ERR_FAIL_INDEX(p_fft_size, FFT_SIZE_MAX);
fft_size = p_fft_size;
}
AudioEffectSpectrumAnalyzer::FFTSize AudioEffectSpectrumAnalyzer::get_fft_size() const {
return fft_size;
}
void AudioEffectSpectrumAnalyzer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_buffer_length", "seconds"), &AudioEffectSpectrumAnalyzer::set_buffer_length);
ClassDB::bind_method(D_METHOD("get_buffer_length"), &AudioEffectSpectrumAnalyzer::get_buffer_length);
ClassDB::bind_method(D_METHOD("set_tap_back_pos", "seconds"), &AudioEffectSpectrumAnalyzer::set_tap_back_pos);
ClassDB::bind_method(D_METHOD("get_tap_back_pos"), &AudioEffectSpectrumAnalyzer::get_tap_back_pos);
ClassDB::bind_method(D_METHOD("set_fft_size", "size"), &AudioEffectSpectrumAnalyzer::set_fft_size);
ClassDB::bind_method(D_METHOD("get_fft_size"), &AudioEffectSpectrumAnalyzer::get_fft_size);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "buffer_length", PROPERTY_HINT_RANGE, "0.1,4,0.1,suffix:s"), "set_buffer_length", "get_buffer_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap_back_pos", PROPERTY_HINT_RANGE, "0.1,4,0.1"), "set_tap_back_pos", "get_tap_back_pos");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fft_size", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096"), "set_fft_size", "get_fft_size");
BIND_ENUM_CONSTANT(FFT_SIZE_256);
BIND_ENUM_CONSTANT(FFT_SIZE_512);
BIND_ENUM_CONSTANT(FFT_SIZE_1024);
BIND_ENUM_CONSTANT(FFT_SIZE_2048);
BIND_ENUM_CONSTANT(FFT_SIZE_4096);
BIND_ENUM_CONSTANT(FFT_SIZE_MAX);
}
AudioEffectSpectrumAnalyzer::AudioEffectSpectrumAnalyzer() {
buffer_length = 2;
tapback_pos = 0.01;
fft_size = FFT_SIZE_1024;
}

View file

@ -0,0 +1,107 @@
/**************************************************************************/
/* audio_effect_spectrum_analyzer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_SPECTRUM_ANALYZER_H
#define AUDIO_EFFECT_SPECTRUM_ANALYZER_H
#include "servers/audio/audio_effect.h"
class AudioEffectSpectrumAnalyzer;
class AudioEffectSpectrumAnalyzerInstance : public AudioEffectInstance {
GDCLASS(AudioEffectSpectrumAnalyzerInstance, AudioEffectInstance);
public:
enum MagnitudeMode {
MAGNITUDE_AVERAGE,
MAGNITUDE_MAX,
};
private:
friend class AudioEffectSpectrumAnalyzer;
Ref<AudioEffectSpectrumAnalyzer> base;
Vector<Vector<AudioFrame>> fft_history;
Vector<float> temporal_fft;
int temporal_fft_pos;
int fft_size;
int fft_count;
int fft_pos;
float mix_rate;
uint64_t last_fft_time;
protected:
static void _bind_methods();
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
Vector2 get_magnitude_for_frequency_range(float p_begin, float p_end, MagnitudeMode p_mode = MAGNITUDE_MAX) const;
};
VARIANT_ENUM_CAST(AudioEffectSpectrumAnalyzerInstance::MagnitudeMode)
class AudioEffectSpectrumAnalyzer : public AudioEffect {
GDCLASS(AudioEffectSpectrumAnalyzer, AudioEffect);
public:
enum FFTSize {
FFT_SIZE_256,
FFT_SIZE_512,
FFT_SIZE_1024,
FFT_SIZE_2048,
FFT_SIZE_4096,
FFT_SIZE_MAX
};
public:
friend class AudioEffectSpectrumAnalyzerInstance;
float buffer_length;
float tapback_pos;
FFTSize fft_size;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_buffer_length(float p_seconds);
float get_buffer_length() const;
void set_tap_back_pos(float p_seconds);
float get_tap_back_pos() const;
void set_fft_size(FFTSize);
FFTSize get_fft_size() const;
AudioEffectSpectrumAnalyzer();
};
VARIANT_ENUM_CAST(AudioEffectSpectrumAnalyzer::FFTSize);
#endif // AUDIO_EFFECT_SPECTRUM_ANALYZER_H

View file

@ -0,0 +1,145 @@
/**************************************************************************/
/* audio_effect_stereo_enhance.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_effect_stereo_enhance.h"
#include "servers/audio_server.h"
void AudioEffectStereoEnhanceInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
float intensity = base->pan_pullout;
bool surround_mode = base->surround > 0;
float surround_amount = base->surround;
unsigned int delay_frames = (base->time_pullout / 1000.0) * AudioServer::get_singleton()->get_mix_rate();
for (int i = 0; i < p_frame_count; i++) {
float l = p_src_frames[i].left;
float r = p_src_frames[i].right;
float center = (l + r) / 2.0f;
l = (center + (l - center) * intensity);
r = (center + (r - center) * intensity);
if (surround_mode) {
float val = (l + r) / 2.0;
delay_ringbuff[ringbuff_pos & ringbuff_mask] = val;
float out = delay_ringbuff[(ringbuff_pos - delay_frames) & ringbuff_mask] * surround_amount;
l += out;
r += -out;
} else {
float val = r;
delay_ringbuff[ringbuff_pos & ringbuff_mask] = val;
//r is delayed
r = delay_ringbuff[(ringbuff_pos - delay_frames) & ringbuff_mask];
}
p_dst_frames[i].left = l;
p_dst_frames[i].right = r;
ringbuff_pos++;
}
}
AudioEffectStereoEnhanceInstance::~AudioEffectStereoEnhanceInstance() {
memdelete_arr(delay_ringbuff);
}
Ref<AudioEffectInstance> AudioEffectStereoEnhance::instantiate() {
Ref<AudioEffectStereoEnhanceInstance> ins;
ins.instantiate();
ins->base = Ref<AudioEffectStereoEnhance>(this);
float ring_buffer_max_size = AudioEffectStereoEnhanceInstance::MAX_DELAY_MS + 2;
ring_buffer_max_size /= 1000.0; //convert to seconds
ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
int ringbuff_size = (int)ring_buffer_max_size;
int bits = 0;
while (ringbuff_size > 0) {
bits++;
ringbuff_size /= 2;
}
ringbuff_size = 1 << bits;
ins->ringbuff_mask = ringbuff_size - 1;
ins->ringbuff_pos = 0;
ins->delay_ringbuff = memnew_arr(float, ringbuff_size);
return ins;
}
void AudioEffectStereoEnhance::set_pan_pullout(float p_amount) {
pan_pullout = p_amount;
}
float AudioEffectStereoEnhance::get_pan_pullout() const {
return pan_pullout;
}
void AudioEffectStereoEnhance::set_time_pullout(float p_amount) {
time_pullout = p_amount;
}
float AudioEffectStereoEnhance::get_time_pullout() const {
return time_pullout;
}
void AudioEffectStereoEnhance::set_surround(float p_amount) {
surround = p_amount;
}
float AudioEffectStereoEnhance::get_surround() const {
return surround;
}
void AudioEffectStereoEnhance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pan_pullout", "amount"), &AudioEffectStereoEnhance::set_pan_pullout);
ClassDB::bind_method(D_METHOD("get_pan_pullout"), &AudioEffectStereoEnhance::get_pan_pullout);
ClassDB::bind_method(D_METHOD("set_time_pullout", "amount"), &AudioEffectStereoEnhance::set_time_pullout);
ClassDB::bind_method(D_METHOD("get_time_pullout"), &AudioEffectStereoEnhance::get_time_pullout);
ClassDB::bind_method(D_METHOD("set_surround", "amount"), &AudioEffectStereoEnhance::set_surround);
ClassDB::bind_method(D_METHOD("get_surround"), &AudioEffectStereoEnhance::get_surround);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pan_pullout", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_pan_pullout", "get_pan_pullout");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_pullout_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_time_pullout", "get_time_pullout");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "surround", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_surround", "get_surround");
}
AudioEffectStereoEnhance::AudioEffectStereoEnhance() {}

View file

@ -0,0 +1,85 @@
/**************************************************************************/
/* audio_effect_stereo_enhance.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_EFFECT_STEREO_ENHANCE_H
#define AUDIO_EFFECT_STEREO_ENHANCE_H
#include "servers/audio/audio_effect.h"
class AudioEffectStereoEnhance;
class AudioEffectStereoEnhanceInstance : public AudioEffectInstance {
GDCLASS(AudioEffectStereoEnhanceInstance, AudioEffectInstance);
friend class AudioEffectStereoEnhance;
Ref<AudioEffectStereoEnhance> base;
enum {
MAX_DELAY_MS = 50
};
float *delay_ringbuff = nullptr;
unsigned int ringbuff_pos = 0;
unsigned int ringbuff_mask = 0;
public:
virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) override;
~AudioEffectStereoEnhanceInstance();
};
class AudioEffectStereoEnhance : public AudioEffect {
GDCLASS(AudioEffectStereoEnhance, AudioEffect);
friend class AudioEffectStereoEnhanceInstance;
float volume_db = 0.0f;
float pan_pullout = 1.0f;
float time_pullout = 0.0f;
float surround = 0.0f;
protected:
static void _bind_methods();
public:
Ref<AudioEffectInstance> instantiate() override;
void set_pan_pullout(float p_amount);
float get_pan_pullout() const;
void set_time_pullout(float p_amount);
float get_time_pullout() const;
void set_surround(float p_amount);
float get_surround() const;
AudioEffectStereoEnhance();
};
#endif // AUDIO_EFFECT_STEREO_ENHANCE_H

View file

@ -0,0 +1,220 @@
/**************************************************************************/
/* audio_stream_generator.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "audio_stream_generator.h"
void AudioStreamGenerator::set_mix_rate(float p_mix_rate) {
mix_rate = p_mix_rate;
}
float AudioStreamGenerator::get_mix_rate() const {
return mix_rate;
}
void AudioStreamGenerator::set_buffer_length(float p_seconds) {
buffer_len = p_seconds;
}
float AudioStreamGenerator::get_buffer_length() const {
return buffer_len;
}
Ref<AudioStreamPlayback> AudioStreamGenerator::instantiate_playback() {
Ref<AudioStreamGeneratorPlayback> playback;
playback.instantiate();
playback->generator = this;
int target_buffer_size = mix_rate * buffer_len;
playback->buffer.resize(nearest_shift(target_buffer_size));
playback->buffer.clear();
return playback;
}
String AudioStreamGenerator::get_stream_name() const {
return "UserFeed";
}
double AudioStreamGenerator::get_length() const {
return 0;
}
bool AudioStreamGenerator::is_monophonic() const {
return true;
}
void AudioStreamGenerator::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate);
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate);
ClassDB::bind_method(D_METHOD("set_buffer_length", "seconds"), &AudioStreamGenerator::set_buffer_length);
ClassDB::bind_method(D_METHOD("get_buffer_length"), &AudioStreamGenerator::get_buffer_length);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mix_rate", PROPERTY_HINT_RANGE, "20,192000,1,suffix:Hz"), "set_mix_rate", "get_mix_rate");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "buffer_length", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_buffer_length", "get_buffer_length");
}
AudioStreamGenerator::AudioStreamGenerator() {
mix_rate = 44100;
buffer_len = 0.5;
}
////////////////
bool AudioStreamGeneratorPlayback::push_frame(const Vector2 &p_frame) {
if (buffer.space_left() < 1) {
return false;
}
AudioFrame f = p_frame;
buffer.write(&f, 1);
return true;
}
bool AudioStreamGeneratorPlayback::can_push_buffer(int p_frames) const {
return buffer.space_left() >= p_frames;
}
bool AudioStreamGeneratorPlayback::push_buffer(const PackedVector2Array &p_frames) {
int to_write = p_frames.size();
if (buffer.space_left() < to_write) {
return false;
}
const Vector2 *r = p_frames.ptr();
if constexpr (sizeof(real_t) == 4) {
//write directly
buffer.write((const AudioFrame *)r, to_write);
} else {
//convert from double
AudioFrame buf[2048];
int ofs = 0;
while (to_write) {
int w = MIN(to_write, 2048);
for (int i = 0; i < w; i++) {
buf[i] = r[i + ofs];
}
buffer.write(buf, w);
ofs += w;
to_write -= w;
}
}
return true;
}
int AudioStreamGeneratorPlayback::get_frames_available() const {
return buffer.space_left();
}
int AudioStreamGeneratorPlayback::get_skips() const {
return skips;
}
void AudioStreamGeneratorPlayback::clear_buffer() {
ERR_FAIL_COND(active);
buffer.clear();
mixed = 0;
}
int AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) {
if (!active) {
return 0;
}
int read_amount = buffer.data_left();
if (p_frames < read_amount) {
read_amount = p_frames;
}
buffer.read(p_buffer, read_amount);
if (read_amount < p_frames) {
// Fill with zeros as fallback in case of buffer underrun.
for (int i = read_amount; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
skips++;
}
mixed += p_frames / generator->get_mix_rate();
return p_frames;
}
float AudioStreamGeneratorPlayback::get_stream_sampling_rate() {
return generator->get_mix_rate();
}
void AudioStreamGeneratorPlayback::start(double p_from_pos) {
if (mixed == 0.0) {
begin_resample();
}
skips = 0;
active = true;
mixed = 0.0;
}
void AudioStreamGeneratorPlayback::stop() {
active = false;
}
bool AudioStreamGeneratorPlayback::is_playing() const {
return active;
}
int AudioStreamGeneratorPlayback::get_loop_count() const {
return 0;
}
double AudioStreamGeneratorPlayback::get_playback_position() const {
return mixed;
}
void AudioStreamGeneratorPlayback::seek(double p_time) {
//no seek possible
}
void AudioStreamGeneratorPlayback::tag_used_streams() {
generator->tag_used(0);
}
void AudioStreamGeneratorPlayback::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_frame", "frame"), &AudioStreamGeneratorPlayback::push_frame);
ClassDB::bind_method(D_METHOD("can_push_buffer", "amount"), &AudioStreamGeneratorPlayback::can_push_buffer);
ClassDB::bind_method(D_METHOD("push_buffer", "frames"), &AudioStreamGeneratorPlayback::push_buffer);
ClassDB::bind_method(D_METHOD("get_frames_available"), &AudioStreamGeneratorPlayback::get_frames_available);
ClassDB::bind_method(D_METHOD("get_skips"), &AudioStreamGeneratorPlayback::get_skips);
ClassDB::bind_method(D_METHOD("clear_buffer"), &AudioStreamGeneratorPlayback::clear_buffer);
}
AudioStreamGeneratorPlayback::AudioStreamGeneratorPlayback() {
generator = nullptr;
skips = 0;
active = false;
mixed = 0;
}

View file

@ -0,0 +1,99 @@
/**************************************************************************/
/* audio_stream_generator.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_STREAM_GENERATOR_H
#define AUDIO_STREAM_GENERATOR_H
#include "core/templates/ring_buffer.h"
#include "servers/audio/audio_stream.h"
class AudioStreamGenerator : public AudioStream {
GDCLASS(AudioStreamGenerator, AudioStream);
float mix_rate;
float buffer_len;
protected:
static void _bind_methods();
public:
void set_mix_rate(float p_mix_rate);
float get_mix_rate() const;
void set_buffer_length(float p_seconds);
float get_buffer_length() const;
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual double get_length() const override;
virtual bool is_monophonic() const override;
AudioStreamGenerator();
};
class AudioStreamGeneratorPlayback : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamGeneratorPlayback, AudioStreamPlaybackResampled);
friend class AudioStreamGenerator;
RingBuffer<AudioFrame> buffer;
int skips;
bool active;
float mixed;
AudioStreamGenerator *generator = nullptr;
protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
static void _bind_methods();
public:
virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; //times it looped
virtual double get_playback_position() const override;
virtual void seek(double p_time) override;
bool push_frame(const Vector2 &p_frame);
bool can_push_buffer(int p_frames) const;
bool push_buffer(const PackedVector2Array &p_frames);
int get_frames_available() const;
int get_skips() const;
virtual void tag_used_streams() override;
void clear_buffer();
AudioStreamGeneratorPlayback();
};
#endif // AUDIO_STREAM_GENERATOR_H

View file

@ -0,0 +1,203 @@
/**************************************************************************/
/* eq_filter.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "eq_filter.h"
#include "core/error/error_macros.h"
#include "core/math/math_funcs.h"
#include <math.h>
#define POW2(v) ((v) * (v))
/* Helper */
static int solve_quadratic(double a, double b, double c, double *r1, double *r2) {
//solves quadractic and returns number of roots
double base = 2 * a;
if (base == 0.0f) {
return 0;
}
double squared = b * b - 4 * a * c;
if (squared < 0.0) {
return 0;
}
squared = sqrt(squared);
*r1 = (-b + squared) / base;
*r2 = (-b - squared) / base;
if (*r1 == *r2) {
return 1;
} else {
return 2;
}
}
EQ::BandProcess::BandProcess() {
c1 = c2 = c3 = history.a1 = history.a2 = history.a3 = 0;
history.b1 = history.b2 = history.b3 = 0;
}
void EQ::recalculate_band_coefficients() {
#define BAND_LOG(m_f) (log((m_f)) / log(2.))
for (int i = 0; i < band.size(); i++) {
double octave_size;
double frq = band[i].freq;
if (i == 0) {
octave_size = BAND_LOG(band[1].freq) - BAND_LOG(frq);
} else if (i == (band.size() - 1)) {
octave_size = BAND_LOG(frq) - BAND_LOG(band[i - 1].freq);
} else {
double next = BAND_LOG(band[i + 1].freq) - BAND_LOG(frq);
double prev = BAND_LOG(frq) - BAND_LOG(band[i - 1].freq);
octave_size = (next + prev) / 2.0;
}
double frq_l = round(frq / pow(2.0, octave_size / 2.0));
double side_gain2 = POW2(Math_SQRT12);
double th = Math_TAU * frq / mix_rate;
double th_l = Math_TAU * frq_l / mix_rate;
double c2a = side_gain2 * POW2(cos(th)) - 2.0 * side_gain2 * cos(th_l) * cos(th) + side_gain2 - POW2(sin(th_l));
double c2b = 2.0 * side_gain2 * POW2(cos(th_l)) + side_gain2 * POW2(cos(th)) - 2.0 * side_gain2 * cos(th_l) * cos(th) - side_gain2 + POW2(sin(th_l));
double c2c = 0.25 * side_gain2 * POW2(cos(th)) - 0.5 * side_gain2 * cos(th_l) * cos(th) + 0.25 * side_gain2 - 0.25 * POW2(sin(th_l));
//printf("band %i, precoefs = %f,%f,%f\n",i,c2a,c2b,c2c);
// Default initializing to silence compiler warning about potential uninitialized use.
// Both variables are properly set in _solve_quadratic before use, or we continue if roots == 0.
double r1 = 0, r2 = 0; //roots
int roots = solve_quadratic(c2a, c2b, c2c, &r1, &r2);
ERR_CONTINUE(roots == 0);
band.write[i].c1 = 2.0 * ((0.5 - r1) / 2.0);
band.write[i].c2 = 2.0 * r1;
band.write[i].c3 = 2.0 * (0.5 + r1) * cos(th);
//printf("band %i, coefs = %f,%f,%f\n",i,(float)bands[i].c1,(float)bands[i].c2,(float)bands[i].c3);
}
}
void EQ::set_preset_band_mode(Preset p_preset) {
band.clear();
#define PUSH_BANDS(m_bands) \
for (int i = 0; i < m_bands; i++) { \
Band b; \
b.freq = bands[i]; \
b.c1 = b.c2 = b.c3 = 0; \
band.push_back(b); \
}
switch (p_preset) {
case PRESET_6_BANDS: {
static const double bands[] = { 32, 100, 320, 1e3, 3200, 10e3 };
PUSH_BANDS(6);
} break;
case PRESET_8_BANDS: {
static const double bands[] = { 32, 72, 192, 512, 1200, 3000, 7500, 16e3 };
PUSH_BANDS(8);
} break;
case PRESET_10_BANDS: {
static const double bands[] = { 31.25, 62.5, 125, 250, 500, 1e3, 2e3, 4e3, 8e3, 16e3 };
PUSH_BANDS(10);
} break;
case PRESET_21_BANDS: {
static const double bands[] = { 22, 32, 44, 63, 90, 125, 175, 250, 350, 500, 700, 1e3, 1400, 2e3, 2800, 4e3, 5600, 8e3, 11e3, 16e3, 22e3 };
PUSH_BANDS(21);
} break;
case PRESET_31_BANDS: {
static const double bands[] = { 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1e3, 1250, 1600, 2e3, 2500, 3150, 4e3, 5e3, 6300, 8e3, 10e3, 12500, 16e3, 20e3 };
PUSH_BANDS(31);
} break;
};
recalculate_band_coefficients();
}
int EQ::get_band_count() const {
return band.size();
}
float EQ::get_band_frequency(int p_band) {
ERR_FAIL_INDEX_V(p_band, band.size(), 0);
return band[p_band].freq;
}
void EQ::set_bands(const Vector<float> &p_bands) {
band.resize(p_bands.size());
for (int i = 0; i < p_bands.size(); i++) {
band.write[i].freq = p_bands[i];
}
recalculate_band_coefficients();
}
void EQ::set_mix_rate(float p_mix_rate) {
mix_rate = p_mix_rate;
recalculate_band_coefficients();
}
EQ::BandProcess EQ::get_band_processor(int p_band) const {
EQ::BandProcess band_proc;
ERR_FAIL_INDEX_V(p_band, band.size(), band_proc);
band_proc.c1 = band[p_band].c1;
band_proc.c2 = band[p_band].c2;
band_proc.c3 = band[p_band].c3;
return band_proc;
}
EQ::EQ() {
mix_rate = 44100;
}
EQ::~EQ() {
}

View file

@ -0,0 +1,102 @@
/**************************************************************************/
/* eq_filter.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 EQ_FILTER_H
#define EQ_FILTER_H
#include "core/templates/vector.h"
#include "core/typedefs.h"
class EQ {
public:
enum Preset {
PRESET_6_BANDS,
PRESET_8_BANDS,
PRESET_10_BANDS,
PRESET_21_BANDS,
PRESET_31_BANDS
};
class BandProcess {
friend class EQ;
float c1, c2, c3;
struct History {
float a1, a2, a3;
float b1, b2, b3;
} history;
public:
inline void process_one(float &p_data);
BandProcess();
};
private:
struct Band {
float freq;
float c1, c2, c3;
};
Vector<Band> band;
float mix_rate;
void recalculate_band_coefficients();
public:
void set_mix_rate(float p_mix_rate);
int get_band_count() const;
void set_preset_band_mode(Preset p_preset);
void set_bands(const Vector<float> &p_bands);
BandProcess get_band_processor(int p_band) const;
float get_band_frequency(int p_band);
EQ();
~EQ();
};
/* Inline Function */
inline void EQ::BandProcess::process_one(float &p_data) {
history.a1 = p_data;
history.b1 = c1 * (history.a1 - history.a3) + c3 * history.b2 - c2 * history.b3;
p_data = history.b1;
history.a3 = history.a2;
history.a2 = history.a1;
history.b3 = history.b2;
history.b2 = history.b1;
}
#endif // EQ_FILTER_H

View file

@ -0,0 +1,341 @@
/**************************************************************************/
/* reverb_filter.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "reverb_filter.h"
#include "core/math/math_funcs.h"
#include <math.h>
const float Reverb::comb_tunings[MAX_COMBS] = {
//freeverb comb tunings
0.025306122448979593f,
0.026938775510204082f,
0.028956916099773241f,
0.03074829931972789f,
0.032244897959183672f,
0.03380952380952381f,
0.035306122448979592f,
0.036666666666666667f
};
const float Reverb::allpass_tunings[MAX_ALLPASS] = {
//freeverb allpass tunings
0.0051020408163265302f,
0.007732426303854875f,
0.01f,
0.012607709750566893f
};
void Reverb::process(float *p_src, float *p_dst, int p_frames) {
if (p_frames > INPUT_BUFFER_MAX_SIZE) {
p_frames = INPUT_BUFFER_MAX_SIZE;
}
int predelay_frames = lrint((params.predelay / 1000.0) * params.mix_rate);
if (predelay_frames < 10) {
predelay_frames = 10;
}
if (predelay_frames >= echo_buffer_size) {
predelay_frames = echo_buffer_size - 1;
}
for (int i = 0; i < p_frames; i++) {
if (echo_buffer_pos >= echo_buffer_size) {
echo_buffer_pos = 0;
}
int read_pos = echo_buffer_pos - predelay_frames;
while (read_pos < 0) {
read_pos += echo_buffer_size;
}
float in = undenormalize(echo_buffer[read_pos] * params.predelay_fb + p_src[i]);
echo_buffer[echo_buffer_pos] = in;
input_buffer[i] = in;
p_dst[i] = 0; //take the chance and clear this
echo_buffer_pos++;
}
if (params.hpf > 0) {
float hpaux = expf(-Math_TAU * params.hpf * 6000 / params.mix_rate);
float hp_a1 = (1.0 + hpaux) / 2.0;
float hp_a2 = -(1.0 + hpaux) / 2.0;
float hp_b1 = hpaux;
for (int i = 0; i < p_frames; i++) {
float in = input_buffer[i];
input_buffer[i] = in * hp_a1 + hpf_h1 * hp_a2 + hpf_h2 * hp_b1;
hpf_h2 = input_buffer[i];
hpf_h1 = in;
}
}
for (int i = 0; i < MAX_COMBS; i++) {
Comb &c = comb[i];
int size_limit = c.size - lrintf((float)c.extra_spread_frames * (1.0 - params.extra_spread));
for (int j = 0; j < p_frames; j++) {
if (c.pos >= size_limit) { //reset this now just in case
c.pos = 0;
}
float out = undenormalize(c.buffer[c.pos] * c.feedback);
out = out * (1.0 - c.damp) + c.damp_h * c.damp; //lowpass
c.damp_h = out;
c.buffer[c.pos] = input_buffer[j] + out;
p_dst[j] += out;
c.pos++;
}
}
static const float allpass_feedback = 0.7;
/* this one works, but the other version is just nicer....
int ap_size_limit[MAX_ALLPASS];
for (int i=0;i<MAX_ALLPASS;i++) {
AllPass &a=allpass[i];
ap_size_limit[i]=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread));
}
for (int i=0;i<p_frames;i++) {
float sample=p_dst[i];
float aux,in;
float AllPass*ap;
#define PROCESS_ALLPASS(m_ap) \
ap=&allpass[m_ap]; \
if (ap->pos>=ap_size_limit[m_ap]) \
ap->pos=0; \
aux=undenormalize(ap->buffer[ap->pos]); \
in=sample; \
sample=-in+aux; \
ap->pos++;
PROCESS_ALLPASS(0);
PROCESS_ALLPASS(1);
PROCESS_ALLPASS(2);
PROCESS_ALLPASS(3);
p_dst[i]=sample;
}
*/
for (int i = 0; i < MAX_ALLPASS; i++) {
AllPass &a = allpass[i];
int size_limit = a.size - lrintf((float)a.extra_spread_frames * (1.0 - params.extra_spread));
for (int j = 0; j < p_frames; j++) {
if (a.pos >= size_limit) {
a.pos = 0;
}
float aux = a.buffer[a.pos];
a.buffer[a.pos] = undenormalize(allpass_feedback * aux + p_dst[j]);
p_dst[j] = aux - allpass_feedback * a.buffer[a.pos];
a.pos++;
}
}
static const float wet_scale = 0.6;
for (int i = 0; i < p_frames; i++) {
p_dst[i] = p_dst[i] * params.wet * wet_scale + p_src[i] * params.dry;
}
}
void Reverb::set_room_size(float p_size) {
params.room_size = p_size;
update_parameters();
}
void Reverb::set_damp(float p_damp) {
params.damp = p_damp;
update_parameters();
}
void Reverb::set_wet(float p_wet) {
params.wet = p_wet;
}
void Reverb::set_dry(float p_dry) {
params.dry = p_dry;
}
void Reverb::set_predelay(float p_predelay) {
params.predelay = p_predelay;
}
void Reverb::set_predelay_feedback(float p_predelay_fb) {
params.predelay_fb = p_predelay_fb;
}
void Reverb::set_highpass(float p_frq) {
if (p_frq > 1) {
p_frq = 1;
}
if (p_frq < 0) {
p_frq = 0;
}
params.hpf = p_frq;
}
void Reverb::set_extra_spread(float p_spread) {
params.extra_spread = p_spread;
}
void Reverb::set_mix_rate(float p_mix_rate) {
params.mix_rate = p_mix_rate;
configure_buffers();
}
void Reverb::set_extra_spread_base(float p_sec) {
params.extra_spread_base = p_sec;
configure_buffers();
}
void Reverb::configure_buffers() {
clear_buffers(); //clear if necessary
for (int i = 0; i < MAX_COMBS; i++) {
Comb &c = comb[i];
c.extra_spread_frames = lrint(params.extra_spread_base * params.mix_rate);
int len = lrint(comb_tunings[i] * params.mix_rate) + c.extra_spread_frames;
if (len < 5) {
len = 5; //may this happen?
}
c.buffer = memnew_arr(float, len);
c.pos = 0;
for (int j = 0; j < len; j++) {
c.buffer[j] = 0;
}
c.size = len;
}
for (int i = 0; i < MAX_ALLPASS; i++) {
AllPass &a = allpass[i];
a.extra_spread_frames = lrint(params.extra_spread_base * params.mix_rate);
int len = lrint(allpass_tunings[i] * params.mix_rate) + a.extra_spread_frames;
if (len < 5) {
len = 5; //may this happen?
}
a.buffer = memnew_arr(float, len);
a.pos = 0;
for (int j = 0; j < len; j++) {
a.buffer[j] = 0;
}
a.size = len;
}
echo_buffer_size = (int)(((float)MAX_ECHO_MS / 1000.0) * params.mix_rate + 1.0);
echo_buffer = memnew_arr(float, echo_buffer_size);
for (int i = 0; i < echo_buffer_size; i++) {
echo_buffer[i] = 0;
}
echo_buffer_pos = 0;
}
void Reverb::update_parameters() {
//more freeverb derived constants
static const float room_scale = 0.28f;
static const float room_offset = 0.7f;
for (int i = 0; i < MAX_COMBS; i++) {
Comb &c = comb[i];
c.feedback = room_offset + params.room_size * room_scale;
if (c.feedback < room_offset) {
c.feedback = room_offset;
} else if (c.feedback > (room_offset + room_scale)) {
c.feedback = (room_offset + room_scale);
}
float auxdmp = params.damp / 2.0 + 0.5; //only half the range (0.5 .. 1.0 is enough)
auxdmp *= auxdmp;
c.damp = expf(-Math_TAU * auxdmp * 10000 / params.mix_rate); // 0 .. 10khz
}
}
void Reverb::clear_buffers() {
if (echo_buffer) {
memdelete_arr(echo_buffer);
}
for (int i = 0; i < MAX_COMBS; i++) {
if (comb[i].buffer) {
memdelete_arr(comb[i].buffer);
}
comb[i].buffer = nullptr;
}
for (int i = 0; i < MAX_ALLPASS; i++) {
if (allpass[i].buffer) {
memdelete_arr(allpass[i].buffer);
}
allpass[i].buffer = nullptr;
}
}
Reverb::Reverb() {
params.room_size = 0.8;
params.damp = 0.5;
params.dry = 1.0;
params.wet = 0.0;
params.mix_rate = 44100;
params.extra_spread_base = 0;
params.extra_spread = 1.0;
params.predelay = 150;
params.predelay_fb = 0.4;
params.hpf = 0;
input_buffer = memnew_arr(float, INPUT_BUFFER_MAX_SIZE);
configure_buffers();
update_parameters();
}
Reverb::~Reverb() {
memdelete_arr(input_buffer);
clear_buffers();
}

View file

@ -0,0 +1,122 @@
/**************************************************************************/
/* reverb_filter.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 REVERB_FILTER_H
#define REVERB_FILTER_H
#include "core/math/audio_frame.h"
#include "core/os/memory.h"
#include "core/typedefs.h"
class Reverb {
public:
enum {
INPUT_BUFFER_MAX_SIZE = 1024,
};
private:
enum {
MAX_COMBS = 8,
MAX_ALLPASS = 4,
MAX_ECHO_MS = 500
};
static const float comb_tunings[MAX_COMBS];
static const float allpass_tunings[MAX_ALLPASS];
struct Comb {
int size = 0;
float *buffer = nullptr;
float feedback = 0;
float damp = 0; //lowpass
float damp_h = 0; //history
int pos = 0;
int extra_spread_frames = 0;
Comb() {}
};
struct AllPass {
int size = 0;
float *buffer = nullptr;
int pos = 0;
int extra_spread_frames = 0;
AllPass() {}
};
Comb comb[MAX_COMBS];
AllPass allpass[MAX_ALLPASS];
float *input_buffer = nullptr;
float *echo_buffer = nullptr;
int echo_buffer_size = 0;
int echo_buffer_pos = 0;
float hpf_h1 = 0.0f;
float hpf_h2 = 0.0f;
struct Parameters {
float room_size;
float damp;
float wet;
float dry;
float mix_rate;
float extra_spread_base;
float extra_spread;
float predelay;
float predelay_fb;
float hpf;
} params;
void configure_buffers();
void update_parameters();
void clear_buffers();
public:
void set_room_size(float p_size);
void set_damp(float p_damp);
void set_wet(float p_wet);
void set_dry(float p_dry);
void set_predelay(float p_predelay); // in ms
void set_predelay_feedback(float p_predelay_fb); // in ms
void set_highpass(float p_frq);
void set_mix_rate(float p_mix_rate);
void set_extra_spread(float p_spread);
void set_extra_spread_base(float p_sec);
void process(float *p_src, float *p_dst, int p_frames);
Reverb();
~Reverb();
};
#endif // REVERB_FILTER_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,533 @@
/**************************************************************************/
/* audio_server.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 AUDIO_SERVER_H
#define AUDIO_SERVER_H
#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 AudioSample;
class AudioStream;
class AudioStreamWAV;
class AudioStreamPlayback;
class AudioSamplePlayback;
class AudioDriver {
static AudioDriver *singleton;
uint64_t _last_mix_time = 0;
uint64_t _last_mix_frames = 0;
#ifdef DEBUG_ENABLED
SafeNumeric<uint64_t> prof_ticks;
SafeNumeric<uint64_t> prof_time;
#endif
protected:
Vector<int32_t> input_buffer;
unsigned int input_position = 0;
unsigned int input_size = 0;
void audio_server_process(int p_frames, int32_t *p_buffer, bool p_update_mix_time = true);
void update_mix_time(int p_frames);
void input_buffer_init(int driver_buffer_frames);
void input_buffer_write(int32_t sample);
int _get_configured_mix_rate();
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ void start_counting_ticks() { prof_ticks.set(OS::get_singleton()->get_ticks_usec()); }
_FORCE_INLINE_ void stop_counting_ticks() { prof_time.add(OS::get_singleton()->get_ticks_usec() - prof_ticks.get()); }
#else
_FORCE_INLINE_ void start_counting_ticks() {}
_FORCE_INLINE_ void stop_counting_ticks() {}
#endif
public:
double get_time_since_last_mix(); //useful for video -> audio sync
double get_time_to_next_mix();
enum SpeakerMode {
SPEAKER_MODE_STEREO,
SPEAKER_SURROUND_31,
SPEAKER_SURROUND_51,
SPEAKER_SURROUND_71,
};
static AudioDriver *get_singleton();
void set_singleton();
// Virtual API to implement.
virtual const char *get_name() const = 0;
virtual Error init() = 0;
virtual void start() = 0;
virtual int get_mix_rate() const = 0;
virtual SpeakerMode get_speaker_mode() const = 0;
virtual float get_latency() { return 0; }
virtual void lock() = 0;
virtual void unlock() = 0;
virtual void finish() = 0;
virtual PackedStringArray get_output_device_list();
virtual String get_output_device();
virtual void set_output_device(const String &p_name) {}
virtual Error input_start() { return FAILED; }
virtual Error input_stop() { return FAILED; }
virtual PackedStringArray get_input_device_list();
virtual String get_input_device() { return "Default"; }
virtual void set_input_device(const String &p_name) {}
//
SpeakerMode get_speaker_mode_by_total_channels(int p_channels) const;
int get_total_channels_by_speaker_mode(SpeakerMode) const;
Vector<int32_t> get_input_buffer() { return input_buffer; }
unsigned int get_input_position() { return input_position; }
unsigned int get_input_size() { return input_size; }
#ifdef DEBUG_ENABLED
uint64_t get_profiling_time() const { return prof_time.get(); }
void reset_profiling_time() { prof_time.set(0); }
#endif
// Samples handling.
virtual bool is_stream_registered_as_sample(const Ref<AudioStream> &p_stream) const {
return false;
}
virtual void register_sample(const Ref<AudioSample> &p_sample) {}
virtual void unregister_sample(const Ref<AudioSample> &p_sample) {}
virtual void start_sample_playback(const Ref<AudioSamplePlayback> &p_playback);
virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {}
virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) {}
virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) { return false; }
virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) {}
virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) {}
virtual void set_sample_bus_count(int p_count) {}
virtual void remove_sample_bus(int p_bus) {}
virtual void add_sample_bus(int p_at_pos = -1) {}
virtual void move_sample_bus(int p_bus, int p_to_pos) {}
virtual void set_sample_bus_send(int p_bus, const StringName &p_send) {}
virtual void set_sample_bus_volume_db(int p_bus, float p_volume_db) {}
virtual void set_sample_bus_solo(int p_bus, bool p_enable) {}
virtual void set_sample_bus_mute(int p_bus, bool p_enable) {}
AudioDriver() {}
virtual ~AudioDriver() {}
};
class AudioDriverManager {
enum {
MAX_DRIVERS = 10
};
static AudioDriver *drivers[MAX_DRIVERS];
static int driver_count;
static AudioDriverDummy dummy_driver;
public:
static const int DEFAULT_MIX_RATE = 44100;
static void add_driver(AudioDriver *p_driver);
static void initialize(int p_driver);
static int get_driver_count();
static AudioDriver *get_driver(int p_driver);
};
class AudioBusLayout;
class AudioServer : public Object {
GDCLASS(AudioServer, Object);
public:
//re-expose this here, as AudioDriver is not exposed to script
enum SpeakerMode {
SPEAKER_MODE_STEREO,
SPEAKER_SURROUND_31,
SPEAKER_SURROUND_51,
SPEAKER_SURROUND_71,
};
enum PlaybackType {
PLAYBACK_TYPE_DEFAULT,
PLAYBACK_TYPE_STREAM,
PLAYBACK_TYPE_SAMPLE,
PLAYBACK_TYPE_MAX
};
enum {
AUDIO_DATA_INVALID_ID = -1,
MAX_CHANNELS_PER_BUS = 4,
MAX_BUSES_PER_PLAYBACK = 6,
LOOKAHEAD_BUFFER_SIZE = 64,
};
typedef void (*AudioCallback)(void *p_userdata);
private:
uint64_t mix_time = 0;
int mix_size = 0;
uint32_t buffer_size = 0;
uint64_t mix_count = 0;
uint64_t mix_frames = 0;
#ifdef DEBUG_ENABLED
SafeNumeric<uint64_t> prof_time;
#endif
float channel_disable_threshold_db = 0.0f;
uint32_t channel_disable_frames = 0;
int channel_count = 0;
int to_mix = 0;
float playback_speed_scale = 1.0f;
bool tag_used_audio_streams = false;
struct Bus {
StringName name;
bool solo = false;
bool mute = false;
bool bypass = false;
bool soloed = false;
// Each channel is a stereo pair.
struct Channel {
bool used = false;
bool active = false;
AudioFrame peak_volume = AudioFrame(AUDIO_MIN_PEAK_DB, AUDIO_MIN_PEAK_DB);
Vector<AudioFrame> buffer;
Vector<Ref<AudioEffectInstance>> effect_instances;
uint64_t last_mix_with_audio = 0;
Channel() {}
};
Vector<Channel> channels;
struct Effect {
Ref<AudioEffect> effect;
bool enabled = false;
#ifdef DEBUG_ENABLED
uint64_t prof_time = 0;
#endif
};
Vector<Effect> effects;
float volume_db = 0.0f;
StringName send;
int index_cache = 0;
};
struct AudioStreamPlaybackBusDetails {
bool bus_active[MAX_BUSES_PER_PLAYBACK] = {};
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;
// TODO document if this is necessary.
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old;
Vector<Vector<AudioFrame>> temp_buffer; //temp_buffer for each level
Vector<AudioFrame> mix_buffer;
Vector<Bus *> buses;
HashMap<StringName, Bus *> bus_map;
void _update_bus_effects(int p_bus);
static AudioServer *singleton;
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 = nullptr;
};
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);
LocalVector<Ref<AudioSamplePlayback>> sample_playback_list;
protected:
static void _bind_methods();
public:
_FORCE_INLINE_ int get_channel_count() const {
switch (get_speaker_mode()) {
case SPEAKER_MODE_STEREO:
return 1;
case SPEAKER_SURROUND_31:
return 2;
case SPEAKER_SURROUND_51:
return 3;
case SPEAKER_SURROUND_71:
return 4;
}
ERR_FAIL_V(1);
}
// Do not use from outside audio thread.
bool thread_has_channel_mix_buffer(int p_bus, int p_buffer) const;
AudioFrame *thread_get_channel_mix_buffer(int p_bus, int p_buffer);
int thread_get_mix_buffer_size() const;
int thread_find_bus_index(const StringName &p_name);
void set_bus_count(int p_count);
int get_bus_count() const;
void remove_bus(int p_index);
void add_bus(int p_at_pos = -1);
void move_bus(int p_bus, int p_to_pos);
void set_bus_name(int p_bus, const String &p_name);
String get_bus_name(int p_bus) const;
int get_bus_index(const StringName &p_bus_name) const;
int get_bus_channels(int p_bus) const;
void set_bus_volume_db(int p_bus, float p_volume_db);
float get_bus_volume_db(int p_bus) const;
void set_bus_send(int p_bus, const StringName &p_send);
StringName get_bus_send(int p_bus) const;
void set_bus_solo(int p_bus, bool p_enable);
bool is_bus_solo(int p_bus) const;
void set_bus_mute(int p_bus, bool p_enable);
bool is_bus_mute(int p_bus) const;
void set_bus_bypass_effects(int p_bus, bool p_enable);
bool is_bus_bypassing_effects(int p_bus) const;
void add_bus_effect(int p_bus, const Ref<AudioEffect> &p_effect, int p_at_pos = -1);
void remove_bus_effect(int p_bus, int p_effect);
int get_bus_effect_count(int p_bus);
Ref<AudioEffect> get_bus_effect(int p_bus, int p_effect);
Ref<AudioEffectInstance> get_bus_effect_instance(int p_bus, int p_effect, int p_channel = 0);
void swap_bus_effects(int p_bus, int p_effect, int p_by_effect);
void set_bus_effect_enabled(int p_bus, int p_effect, bool p_enabled);
bool is_bus_effect_enabled(int p_bus, int p_effect) const;
float get_bus_peak_volume_left_db(int p_bus, int p_channel) const;
float get_bus_peak_volume_right_db(int p_bus, int p_channel) const;
bool is_bus_channel_active(int p_bus, int p_channel) const;
void set_playback_speed_scale(float p_scale);
float get_playback_speed_scale() const;
// Convenience method.
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, const StringName &p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0, float p_pitch_scale = 1);
// Expose all parameters.
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes, float p_start_time = 0, float p_pitch_scale = 1, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0);
void stop_playback_stream(Ref<AudioStreamPlayback> p_playback);
void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, const StringName &p_bus, Vector<AudioFrame> p_volumes);
void set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, const HashMap<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;
uint64_t get_mixed_frames() const;
void notify_listener_changed();
virtual void init();
virtual void finish();
virtual void update();
virtual void load_default_bus_layout();
/* MISC config */
virtual void lock();
virtual void unlock();
virtual SpeakerMode get_speaker_mode() const;
virtual float get_mix_rate() const;
virtual float read_output_peak_db() const;
static AudioServer *get_singleton();
virtual double get_output_latency() const;
virtual double get_time_to_next_mix() const;
virtual double get_time_since_last_mix() const;
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;
PackedStringArray get_output_device_list();
String get_output_device();
void set_output_device(const String &p_name);
PackedStringArray get_input_device_list();
String get_input_device();
void set_input_device(const String &p_name);
void set_enable_tagging_used_audio_streams(bool p_enable);
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif
PlaybackType get_default_playback_type() const;
bool is_stream_registered_as_sample(const Ref<AudioStream> &p_stream);
void register_stream_as_sample(const Ref<AudioStream> &p_stream);
void unregister_stream_as_sample(const Ref<AudioStream> &p_stream);
void register_sample(const Ref<AudioSample> &p_sample);
void unregister_sample(const Ref<AudioSample> &p_sample);
void start_sample_playback(const Ref<AudioSamplePlayback> &p_playback);
void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback);
void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused);
bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback);
void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f);
AudioServer();
virtual ~AudioServer();
};
VARIANT_ENUM_CAST(AudioServer::SpeakerMode)
VARIANT_ENUM_CAST(AudioServer::PlaybackType)
class AudioBusLayout : public Resource {
GDCLASS(AudioBusLayout, Resource);
friend class AudioServer;
struct Bus {
StringName name;
bool solo = false;
bool mute = false;
bool bypass = false;
struct Effect {
Ref<AudioEffect> effect;
bool enabled = false;
};
Vector<Effect> effects;
float volume_db = 0.0f;
StringName send;
Bus() {}
};
Vector<Bus> buses;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
AudioBusLayout();
};
typedef AudioServer AS;
#endif // AUDIO_SERVER_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,254 @@
/**************************************************************************/
/* camera_feed.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "camera_feed.h"
#include "servers/rendering_server.h"
void CameraFeed::_bind_methods() {
// The setters prefixed with _ are only exposed so we can have feeds through GDExtension!
// They should not be called by the end user.
ClassDB::bind_method(D_METHOD("get_id"), &CameraFeed::get_id);
ClassDB::bind_method(D_METHOD("is_active"), &CameraFeed::is_active);
ClassDB::bind_method(D_METHOD("set_active", "active"), &CameraFeed::set_active);
ClassDB::bind_method(D_METHOD("get_name"), &CameraFeed::get_name);
ClassDB::bind_method(D_METHOD("_set_name", "name"), &CameraFeed::set_name);
ClassDB::bind_method(D_METHOD("get_position"), &CameraFeed::get_position);
ClassDB::bind_method(D_METHOD("_set_position", "position"), &CameraFeed::set_position);
// Note, for transform some feeds may override what the user sets (such as ARKit)
ClassDB::bind_method(D_METHOD("get_transform"), &CameraFeed::get_transform);
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CameraFeed::set_transform);
ClassDB::bind_method(D_METHOD("_set_RGB_img", "rgb_img"), &CameraFeed::set_RGB_img);
ClassDB::bind_method(D_METHOD("_set_YCbCr_img", "ycbcr_img"), &CameraFeed::set_YCbCr_img);
ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype);
ADD_GROUP("Feed", "feed_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feed_is_active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "feed_transform"), "set_transform", "get_transform");
BIND_ENUM_CONSTANT(FEED_NOIMAGE);
BIND_ENUM_CONSTANT(FEED_RGB);
BIND_ENUM_CONSTANT(FEED_YCBCR);
BIND_ENUM_CONSTANT(FEED_YCBCR_SEP);
BIND_ENUM_CONSTANT(FEED_UNSPECIFIED);
BIND_ENUM_CONSTANT(FEED_FRONT);
BIND_ENUM_CONSTANT(FEED_BACK);
}
int CameraFeed::get_id() const {
return id;
}
bool CameraFeed::is_active() const {
return active;
}
void CameraFeed::set_active(bool p_is_active) {
if (p_is_active == active) {
// all good
} else if (p_is_active) {
// attempt to activate this feed
if (activate_feed()) {
print_line("Activate " + name);
active = true;
}
} else {
// just deactivate it
deactivate_feed();
print_line("Deactivate " + name);
active = false;
}
}
String CameraFeed::get_name() const {
return name;
}
void CameraFeed::set_name(String p_name) {
name = p_name;
}
int CameraFeed::get_base_width() const {
return base_width;
}
int CameraFeed::get_base_height() const {
return base_height;
}
CameraFeed::FeedDataType CameraFeed::get_datatype() const {
return datatype;
}
CameraFeed::FeedPosition CameraFeed::get_position() const {
return position;
}
void CameraFeed::set_position(CameraFeed::FeedPosition p_position) {
position = p_position;
}
Transform2D CameraFeed::get_transform() const {
return transform;
}
void CameraFeed::set_transform(const Transform2D &p_transform) {
transform = p_transform;
}
RID CameraFeed::get_texture(CameraServer::FeedImage p_which) {
return texture[p_which];
}
CameraFeed::CameraFeed() {
// initialize our feed
id = CameraServer::get_singleton()->get_free_id();
base_width = 0;
base_height = 0;
name = "???";
active = false;
datatype = CameraFeed::FEED_RGB;
position = CameraFeed::FEED_UNSPECIFIED;
transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
texture[CameraServer::FEED_Y_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
texture[CameraServer::FEED_CBCR_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
}
CameraFeed::CameraFeed(String p_name, FeedPosition p_position) {
// initialize our feed
id = CameraServer::get_singleton()->get_free_id();
base_width = 0;
base_height = 0;
name = p_name;
active = false;
datatype = CameraFeed::FEED_NOIMAGE;
position = p_position;
transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
texture[CameraServer::FEED_Y_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
texture[CameraServer::FEED_CBCR_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
}
CameraFeed::~CameraFeed() {
// Free our textures
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(texture[CameraServer::FEED_Y_IMAGE]);
RenderingServer::get_singleton()->free(texture[CameraServer::FEED_CBCR_IMAGE]);
}
void CameraFeed::set_RGB_img(const Ref<Image> &p_rgb_img) {
ERR_FAIL_COND(p_rgb_img.is_null());
if (active) {
int new_width = p_rgb_img->get_width();
int new_height = p_rgb_img->get_height();
if ((base_width != new_width) || (base_height != new_height)) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = new_width;
base_height = new_height;
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_rgb_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img);
}
datatype = CameraFeed::FEED_RGB;
}
}
void CameraFeed::set_YCbCr_img(const Ref<Image> &p_ycbcr_img) {
ERR_FAIL_COND(p_ycbcr_img.is_null());
if (active) {
int new_width = p_ycbcr_img->get_width();
int new_height = p_ycbcr_img->get_height();
if ((base_width != new_width) || (base_height != new_height)) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = new_width;
base_height = new_height;
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_ycbcr_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img);
}
datatype = CameraFeed::FEED_YCBCR;
}
}
void CameraFeed::set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img) {
ERR_FAIL_COND(p_y_img.is_null());
ERR_FAIL_COND(p_cbcr_img.is_null());
if (active) {
///@TODO investigate whether we can use thirdparty/misc/yuv2rgb.h here to convert our YUV data to RGB, our shader approach is potentially faster though..
// Wondering about including that into multiple projects, may cause issues.
// That said, if we convert to RGB, we could enable using texture resources again...
int new_y_width = p_y_img->get_width();
int new_y_height = p_y_img->get_height();
if ((base_width != new_y_width) || (base_height != new_y_height)) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = new_y_width;
base_height = new_y_height;
{
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_y_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_Y_IMAGE], new_texture);
}
{
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_cbcr_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_CBCR_IMAGE], new_texture);
}
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_Y_IMAGE], p_y_img);
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_CBCR_IMAGE], p_cbcr_img);
}
datatype = CameraFeed::FEED_YCBCR_SEP;
}
}
bool CameraFeed::activate_feed() {
// nothing to do here
return true;
}
void CameraFeed::deactivate_feed() {
// nothing to do here
}

View file

@ -0,0 +1,112 @@
/**************************************************************************/
/* camera_feed.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 CAMERA_FEED_H
#define CAMERA_FEED_H
#include "core/io/image.h"
#include "core/math/transform_2d.h"
#include "servers/camera_server.h"
#include "servers/rendering_server.h"
/**
The camera server is a singleton object that gives access to the various
camera feeds that can be used as the background for our environment.
**/
class CameraFeed : public RefCounted {
GDCLASS(CameraFeed, RefCounted);
public:
enum FeedDataType {
FEED_NOIMAGE, // we don't have an image yet
FEED_RGB, // our texture will contain a normal RGB texture that can be used directly
FEED_YCBCR, // our texture will contain a YCbCr texture that needs to be converted to RGB before output
FEED_YCBCR_SEP // our camera is split into two textures, first plane contains Y data, second plane contains CbCr data
};
enum FeedPosition {
FEED_UNSPECIFIED, // we have no idea
FEED_FRONT, // this is a camera on the front of the device
FEED_BACK // this is a camera on the back of the device
};
private:
int id; // unique id for this, for internal use in case feeds are removed
int base_width;
int base_height;
protected:
String name; // name of our camera feed
FeedDataType datatype; // type of texture data stored
FeedPosition position; // position of camera on the device
Transform2D transform; // display transform
bool active; // only when active do we actually update the camera texture each frame
RID texture[CameraServer::FEED_IMAGES]; // texture images needed for this
static void _bind_methods();
public:
int get_id() const;
bool is_active() const;
void set_active(bool p_is_active);
String get_name() const;
void set_name(String p_name);
int get_base_width() const;
int get_base_height() const;
FeedPosition get_position() const;
void set_position(FeedPosition p_position);
Transform2D get_transform() const;
void set_transform(const Transform2D &p_transform);
RID get_texture(CameraServer::FeedImage p_which);
CameraFeed();
CameraFeed(String p_name, FeedPosition p_position = CameraFeed::FEED_UNSPECIFIED);
virtual ~CameraFeed();
FeedDataType get_datatype() const;
void set_RGB_img(const Ref<Image> &p_rgb_img);
void set_YCbCr_img(const Ref<Image> &p_ycbcr_img);
void set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img);
virtual bool activate_feed();
virtual void deactivate_feed();
};
VARIANT_ENUM_CAST(CameraFeed::FeedDataType);
VARIANT_ENUM_CAST(CameraFeed::FeedPosition);
#endif // CAMERA_FEED_H

View file

@ -0,0 +1,168 @@
/**************************************************************************/
/* camera_server.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "camera_server.h"
#include "core/variant/typed_array.h"
#include "rendering_server.h"
#include "servers/camera/camera_feed.h"
////////////////////////////////////////////////////////
// CameraServer
CameraServer::CreateFunc CameraServer::create_func = nullptr;
void CameraServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_feed", "index"), &CameraServer::get_feed);
ClassDB::bind_method(D_METHOD("get_feed_count"), &CameraServer::get_feed_count);
ClassDB::bind_method(D_METHOD("feeds"), &CameraServer::get_feeds);
ClassDB::bind_method(D_METHOD("add_feed", "feed"), &CameraServer::add_feed);
ClassDB::bind_method(D_METHOD("remove_feed", "feed"), &CameraServer::remove_feed);
ADD_SIGNAL(MethodInfo("camera_feed_added", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("camera_feed_removed", PropertyInfo(Variant::INT, "id")));
BIND_ENUM_CONSTANT(FEED_RGBA_IMAGE);
BIND_ENUM_CONSTANT(FEED_YCBCR_IMAGE);
BIND_ENUM_CONSTANT(FEED_Y_IMAGE);
BIND_ENUM_CONSTANT(FEED_CBCR_IMAGE);
};
CameraServer *CameraServer::singleton = nullptr;
CameraServer *CameraServer::get_singleton() {
return singleton;
};
int CameraServer::get_free_id() {
bool id_exists = true;
int newid = 0;
// find a free id
while (id_exists) {
newid++;
id_exists = false;
for (int i = 0; i < feeds.size() && !id_exists; i++) {
if (feeds[i]->get_id() == newid) {
id_exists = true;
};
};
};
return newid;
};
int CameraServer::get_feed_index(int p_id) {
for (int i = 0; i < feeds.size(); i++) {
if (feeds[i]->get_id() == p_id) {
return i;
};
};
return -1;
};
Ref<CameraFeed> CameraServer::get_feed_by_id(int p_id) {
int index = get_feed_index(p_id);
if (index == -1) {
return nullptr;
} else {
return feeds[index];
}
};
void CameraServer::add_feed(const Ref<CameraFeed> &p_feed) {
ERR_FAIL_COND(p_feed.is_null());
// add our feed
feeds.push_back(p_feed);
print_verbose("CameraServer: Registered camera " + p_feed->get_name() + " with ID " + itos(p_feed->get_id()) + " and position " + itos(p_feed->get_position()) + " at index " + itos(feeds.size() - 1));
// let whomever is interested know
emit_signal(SNAME("camera_feed_added"), p_feed->get_id());
};
void CameraServer::remove_feed(const Ref<CameraFeed> &p_feed) {
for (int i = 0; i < feeds.size(); i++) {
if (feeds[i] == p_feed) {
int feed_id = p_feed->get_id();
print_verbose("CameraServer: Removed camera " + p_feed->get_name() + " with ID " + itos(feed_id) + " and position " + itos(p_feed->get_position()));
// remove it from our array, if this results in our feed being unreferenced it will be destroyed
feeds.remove_at(i);
// let whomever is interested know
emit_signal(SNAME("camera_feed_removed"), feed_id);
return;
};
};
};
Ref<CameraFeed> CameraServer::get_feed(int p_index) {
ERR_FAIL_INDEX_V(p_index, feeds.size(), nullptr);
return feeds[p_index];
};
int CameraServer::get_feed_count() {
return feeds.size();
};
TypedArray<CameraFeed> CameraServer::get_feeds() {
TypedArray<CameraFeed> return_feeds;
int cc = get_feed_count();
return_feeds.resize(cc);
for (int i = 0; i < feeds.size(); i++) {
return_feeds[i] = get_feed(i);
};
return return_feeds;
};
RID CameraServer::feed_texture(int p_id, CameraServer::FeedImage p_texture) {
int index = get_feed_index(p_id);
ERR_FAIL_COND_V(index == -1, RID());
Ref<CameraFeed> feed = get_feed(index);
return feed->get_texture(p_texture);
};
CameraServer::CameraServer() {
singleton = this;
};
CameraServer::~CameraServer() {
singleton = nullptr;
};

View file

@ -0,0 +1,116 @@
/**************************************************************************/
/* camera_server.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 CAMERA_SERVER_H
#define CAMERA_SERVER_H
#include "core/object/class_db.h"
#include "core/object/ref_counted.h"
#include "core/os/thread_safe.h"
#include "core/templates/rid.h"
#include "core/variant/variant.h"
/**
The camera server is a singleton object that gives access to the various
camera feeds that can be used as the background for our environment.
**/
class CameraFeed;
template <typename T>
class TypedArray;
class CameraServer : public Object {
GDCLASS(CameraServer, Object);
_THREAD_SAFE_CLASS_
public:
enum FeedImage {
FEED_RGBA_IMAGE = 0,
FEED_YCBCR_IMAGE = 0,
FEED_Y_IMAGE = 0,
FEED_CBCR_IMAGE = 1,
FEED_IMAGES = 2
};
typedef CameraServer *(*CreateFunc)();
private:
protected:
static CreateFunc create_func;
Vector<Ref<CameraFeed>> feeds;
static CameraServer *singleton;
static void _bind_methods();
template <typename T>
static CameraServer *_create_builtin() {
return memnew(T);
}
public:
static CameraServer *get_singleton();
template <typename T>
static void make_default() {
create_func = _create_builtin<T>;
}
static CameraServer *create() {
CameraServer *server = create_func ? create_func() : memnew(CameraServer);
return server;
};
// Right now we identify our feed by it's ID when it's used in the background.
// May see if we can change this to purely relying on CameraFeed objects or by name.
int get_free_id();
int get_feed_index(int p_id);
Ref<CameraFeed> get_feed_by_id(int p_id);
// Add and remove feeds.
void add_feed(const Ref<CameraFeed> &p_feed);
void remove_feed(const Ref<CameraFeed> &p_feed);
// Get our feeds.
Ref<CameraFeed> get_feed(int p_index);
int get_feed_count();
TypedArray<CameraFeed> get_feeds();
// Intended for use with custom CameraServer implementation.
RID feed_texture(int p_id, FeedImage p_texture);
CameraServer();
~CameraServer();
};
VARIANT_ENUM_CAST(CameraServer::FeedImage);
#endif // CAMERA_SERVER_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,472 @@
/**************************************************************************/
/* servers_debugger.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "servers_debugger.h"
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/engine_profiler.h"
#include "core/io/marshalls.h"
#include "servers/display_server.h"
#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
Array ServersDebugger::ResourceUsage::serialize() {
infos.sort();
Array arr;
arr.push_back(infos.size() * 4);
for (const ResourceInfo &E : infos) {
arr.push_back(E.path);
arr.push_back(E.format);
arr.push_back(E.type);
arr.push_back(E.vram);
}
return arr;
}
bool ServersDebugger::ResourceUsage::deserialize(const Array &p_arr) {
CHECK_SIZE(p_arr, 1, "ResourceUsage");
uint32_t size = p_arr[0];
ERR_FAIL_COND_V(size % 4, false);
CHECK_SIZE(p_arr, 1 + size, "ResourceUsage");
uint32_t idx = 1;
while (idx < 1 + size) {
ResourceInfo info;
info.path = p_arr[idx];
info.format = p_arr[idx + 1];
info.type = p_arr[idx + 2];
info.vram = p_arr[idx + 3];
infos.push_back(info);
idx += 4;
}
CHECK_END(p_arr, idx, "ResourceUsage");
return true;
}
Array ServersDebugger::ScriptFunctionSignature::serialize() {
Array arr;
arr.push_back(name);
arr.push_back(id);
return arr;
}
bool ServersDebugger::ScriptFunctionSignature::deserialize(const Array &p_arr) {
CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");
name = p_arr[0];
id = p_arr[1];
CHECK_END(p_arr, 2, "ScriptFunctionSignature");
return true;
}
Array ServersDebugger::ServersProfilerFrame::serialize() {
Array arr;
arr.push_back(frame_number);
arr.push_back(frame_time);
arr.push_back(process_time);
arr.push_back(physics_time);
arr.push_back(physics_frame_time);
arr.push_back(script_time);
arr.push_back(servers.size());
for (const ServerInfo &s : servers) {
arr.push_back(s.name);
arr.push_back(s.functions.size() * 2);
for (const ServerFunctionInfo &f : s.functions) {
arr.push_back(f.name);
arr.push_back(f.time);
}
}
arr.push_back(script_functions.size() * 5);
for (int i = 0; i < script_functions.size(); i++) {
arr.push_back(script_functions[i].sig_id);
arr.push_back(script_functions[i].call_count);
arr.push_back(script_functions[i].self_time);
arr.push_back(script_functions[i].total_time);
arr.push_back(script_functions[i].internal_time);
}
return arr;
}
bool ServersDebugger::ServersProfilerFrame::deserialize(const Array &p_arr) {
CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");
frame_number = p_arr[0];
frame_time = p_arr[1];
process_time = p_arr[2];
physics_time = p_arr[3];
physics_frame_time = p_arr[4];
script_time = p_arr[5];
int servers_size = p_arr[6];
int idx = 7;
while (servers_size) {
CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");
servers_size--;
ServerInfo si;
si.name = p_arr[idx];
int sub_data_size = p_arr[idx + 1];
idx += 2;
CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");
for (int j = 0; j < sub_data_size / 2; j++) {
ServerFunctionInfo sf;
sf.name = p_arr[idx];
sf.time = p_arr[idx + 1];
idx += 2;
si.functions.push_back(sf);
}
servers.push_back(si);
}
CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame");
int func_size = p_arr[idx];
idx += 1;
CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");
for (int i = 0; i < func_size / 5; i++) {
ScriptFunctionInfo fi;
fi.sig_id = p_arr[idx];
fi.call_count = p_arr[idx + 1];
fi.self_time = p_arr[idx + 2];
fi.total_time = p_arr[idx + 3];
fi.internal_time = p_arr[idx + 4];
script_functions.push_back(fi);
idx += 5;
}
CHECK_END(p_arr, idx, "ServersProfilerFrame");
return true;
}
Array ServersDebugger::VisualProfilerFrame::serialize() {
Array arr;
arr.push_back(frame_number);
arr.push_back(areas.size() * 3);
for (int i = 0; i < areas.size(); i++) {
arr.push_back(areas[i].name);
arr.push_back(areas[i].cpu_msec);
arr.push_back(areas[i].gpu_msec);
}
return arr;
}
bool ServersDebugger::VisualProfilerFrame::deserialize(const Array &p_arr) {
CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");
frame_number = p_arr[0];
int size = p_arr[1];
CHECK_SIZE(p_arr, size, "VisualProfilerFrame");
int idx = 2;
areas.resize(size / 3);
RS::FrameProfileArea *w = areas.ptrw();
for (int i = 0; i < size / 3; i++) {
w[i].name = p_arr[idx];
w[i].cpu_msec = p_arr[idx + 1];
w[i].gpu_msec = p_arr[idx + 2];
idx += 3;
}
CHECK_END(p_arr, idx, "VisualProfilerFrame");
return true;
}
class ServersDebugger::ScriptsProfiler : public EngineProfiler {
typedef ServersDebugger::ScriptFunctionSignature FunctionSignature;
typedef ServersDebugger::ScriptFunctionInfo FunctionInfo;
struct ProfileInfoSort {
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
return A->total_time > B->total_time;
}
};
Vector<ScriptLanguage::ProfilingInfo> info;
Vector<ScriptLanguage::ProfilingInfo *> ptrs;
HashMap<StringName, int> sig_map;
int max_frame_functions = 16;
public:
void toggle(bool p_enable, const Array &p_opts) {
if (p_enable) {
sig_map.clear();
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->profiling_start();
if (p_opts.size() == 2 && p_opts[1].get_type() == Variant::BOOL) {
ScriptServer::get_language(i)->profiling_set_save_native_calls(p_opts[1]);
}
}
if (p_opts.size() > 0 && p_opts[0].get_type() == Variant::INT) {
max_frame_functions = MAX(0, int(p_opts[0]));
}
} else {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->profiling_stop();
}
}
}
void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
int ofs = 0;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
if (p_accumulated) {
ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
} else {
ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
}
}
for (int i = 0; i < ofs; i++) {
ptrs.write[i] = &info.write[i];
}
SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
sa.sort(ptrs.ptrw(), ofs);
int to_send = MIN(ofs, max_frame_functions);
// Check signatures first, and compute total time.
r_total = 0;
for (int i = 0; i < to_send; i++) {
if (!sig_map.has(ptrs[i]->signature)) {
int idx = sig_map.size();
FunctionSignature sig;
sig.name = ptrs[i]->signature;
sig.id = idx;
EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
sig_map[ptrs[i]->signature] = idx;
}
r_total += ptrs[i]->self_time;
}
// Send frame, script time, functions information then
r_funcs.resize(to_send);
FunctionInfo *w = r_funcs.ptrw();
for (int i = 0; i < to_send; i++) {
if (sig_map.has(ptrs[i]->signature)) {
w[i].sig_id = sig_map[ptrs[i]->signature];
}
w[i].call_count = ptrs[i]->call_count;
w[i].total_time = ptrs[i]->total_time / 1000000.0;
w[i].self_time = ptrs[i]->self_time / 1000000.0;
w[i].internal_time = ptrs[i]->internal_time / 1000000.0;
}
}
ScriptsProfiler() {
info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
ptrs.resize(info.size());
}
};
class ServersDebugger::ServersProfiler : public EngineProfiler {
bool skip_profile_frame = false;
typedef ServersDebugger::ServerInfo ServerInfo;
typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;
HashMap<StringName, ServerInfo> server_data;
ScriptsProfiler scripts_profiler;
double frame_time = 0;
double process_time = 0;
double physics_time = 0;
double physics_frame_time = 0;
void _send_frame_data(bool p_final) {
ServersDebugger::ServersProfilerFrame frame;
frame.frame_number = Engine::get_singleton()->get_process_frames();
frame.frame_time = frame_time;
frame.process_time = process_time;
frame.physics_time = physics_time;
frame.physics_frame_time = physics_frame_time;
HashMap<StringName, ServerInfo>::Iterator E = server_data.begin();
while (E) {
if (!p_final) {
frame.servers.push_back(E->value);
}
E->value.functions.clear();
++E;
}
uint64_t time = 0;
scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
frame.script_time = USEC_TO_SEC(time);
if (skip_profile_frame) {
skip_profile_frame = false;
return;
}
if (p_final) {
EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
} else {
EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
}
}
public:
void toggle(bool p_enable, const Array &p_opts) {
skip_profile_frame = false;
if (p_enable) {
server_data.clear(); // Clear old profiling data.
} else {
_send_frame_data(true); // Send final frame.
}
scripts_profiler.toggle(p_enable, p_opts);
}
void add(const Array &p_data) {
String name = p_data[0];
if (!server_data.has(name)) {
ServerInfo info;
info.name = name;
server_data[name] = info;
}
ServerInfo &srv = server_data[name];
for (int idx = 1; idx < p_data.size() - 1; idx += 2) {
ServerFunctionInfo fi;
fi.name = p_data[idx];
fi.time = p_data[idx + 1];
srv.functions.push_back(fi);
}
}
void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
frame_time = p_frame_time;
process_time = p_process_time;
physics_time = p_physics_time;
physics_frame_time = p_physics_frame_time;
_send_frame_data(false);
}
void skip_frame() {
skip_profile_frame = true;
}
};
class ServersDebugger::VisualProfiler : public EngineProfiler {
typedef ServersDebugger::ServerInfo ServerInfo;
typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;
HashMap<StringName, ServerInfo> server_data;
public:
void toggle(bool p_enable, const Array &p_opts) {
RS::get_singleton()->set_frame_profiling_enabled(p_enable);
}
void add(const Array &p_data) {}
void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();
ServersDebugger::VisualProfilerFrame frame;
if (!profile_areas.size()) {
return;
}
frame.frame_number = RS::get_singleton()->get_frame_profile_frame();
frame.areas.append_array(profile_areas);
EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
}
};
ServersDebugger *ServersDebugger::singleton = nullptr;
void ServersDebugger::initialize() {
if (EngineDebugger::is_active()) {
memnew(ServersDebugger);
}
}
void ServersDebugger::deinitialize() {
if (singleton) {
memdelete(singleton);
}
}
Error ServersDebugger::_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
ERR_FAIL_NULL_V(singleton, ERR_BUG);
r_captured = true;
if (p_cmd == "memory") {
singleton->_send_resource_usage();
} else if (p_cmd == "draw") { // Forced redraw.
// For camera override to stay live when the game is paused from the editor.
double delta = 0.0;
if (singleton->last_draw_time) {
delta = (OS::get_singleton()->get_ticks_usec() - singleton->last_draw_time) / 1000000.0;
}
singleton->last_draw_time = OS::get_singleton()->get_ticks_usec();
RenderingServer::get_singleton()->sync();
if (RenderingServer::get_singleton()->has_changed()) {
RenderingServer::get_singleton()->draw(true, delta);
}
EngineDebugger::get_singleton()->send_message("servers:drawn", Array());
} else if (p_cmd == "foreground") {
singleton->last_draw_time = 0.0;
DisplayServer::get_singleton()->window_move_to_foreground();
singleton->servers_profiler->skip_frame();
} else {
r_captured = false;
}
return OK;
}
void ServersDebugger::_send_resource_usage() {
ServersDebugger::ResourceUsage usage;
List<RS::TextureInfo> tinfo;
RS::get_singleton()->texture_debug_usage(&tinfo);
for (const RS::TextureInfo &E : tinfo) {
ServersDebugger::ResourceInfo info;
info.path = E.path;
info.vram = E.bytes;
info.id = E.texture;
info.type = "Texture";
if (E.depth == 0) {
info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
} else {
info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);
}
usage.infos.push_back(info);
}
EngineDebugger::get_singleton()->send_message("servers:memory_usage", usage.serialize());
}
ServersDebugger::ServersDebugger() {
singleton = this;
// Generic servers profiler (audio/physics/...)
servers_profiler.instantiate();
servers_profiler->bind("servers");
// Visual Profiler (cpu/gpu times)
visual_profiler.instantiate();
visual_profiler->bind("visual");
EngineDebugger::Capture servers_cap(nullptr, &_capture);
EngineDebugger::register_message_capture("servers", servers_cap);
}
ServersDebugger::~ServersDebugger() {
EngineDebugger::unregister_message_capture("servers");
singleton = nullptr;
}

View file

@ -0,0 +1,133 @@
/**************************************************************************/
/* servers_debugger.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 SERVERS_DEBUGGER_H
#define SERVERS_DEBUGGER_H
#include "core/debugger/debugger_marshalls.h"
#include "servers/rendering_server.h"
class ServersDebugger {
public:
// Memory usage
struct ResourceInfo {
String path;
String format;
String type;
RID id;
int vram = 0;
bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
};
struct ResourceUsage {
List<ResourceInfo> infos;
Array serialize();
bool deserialize(const Array &p_arr);
};
// Script Profiler
struct ScriptFunctionSignature {
StringName name;
int id = -1;
Array serialize();
bool deserialize(const Array &p_arr);
};
struct ScriptFunctionInfo {
StringName name;
int sig_id = -1;
int call_count = 0;
double self_time = 0;
double total_time = 0;
double internal_time = 0;
};
// Servers profiler
struct ServerFunctionInfo {
StringName name;
double time = 0;
};
struct ServerInfo {
StringName name;
List<ServerFunctionInfo> functions;
};
struct ServersProfilerFrame {
int frame_number = 0;
double frame_time = 0;
double process_time = 0;
double physics_time = 0;
double physics_frame_time = 0;
double script_time = 0;
List<ServerInfo> servers;
Vector<ScriptFunctionInfo> script_functions;
Array serialize();
bool deserialize(const Array &p_arr);
};
// Visual Profiler
struct VisualProfilerFrame {
uint64_t frame_number = 0;
Vector<RS::FrameProfileArea> areas;
Array serialize();
bool deserialize(const Array &p_arr);
};
private:
class ScriptsProfiler;
class ServersProfiler;
class VisualProfiler;
double last_draw_time = 0.0;
Ref<ServersProfiler> servers_profiler;
Ref<VisualProfiler> visual_profiler;
static ServersDebugger *singleton;
static Error _capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
void _send_resource_usage();
ServersDebugger();
public:
static void initialize();
static void deinitialize();
~ServersDebugger();
};
#endif // SERVERS_DEBUGGER_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,451 @@
/**************************************************************************/
/* native_menu.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "native_menu.h"
#include "scene/resources/image_texture.h"
NativeMenu *NativeMenu::singleton = nullptr;
void NativeMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_feature", "feature"), &NativeMenu::has_feature);
ClassDB::bind_method(D_METHOD("has_system_menu", "menu_id"), &NativeMenu::has_system_menu);
ClassDB::bind_method(D_METHOD("get_system_menu", "menu_id"), &NativeMenu::get_system_menu);
ClassDB::bind_method(D_METHOD("get_system_menu_name", "menu_id"), &NativeMenu::get_system_menu_name);
ClassDB::bind_method(D_METHOD("create_menu"), &NativeMenu::create_menu);
ClassDB::bind_method(D_METHOD("has_menu", "rid"), &NativeMenu::has_menu);
ClassDB::bind_method(D_METHOD("free_menu", "rid"), &NativeMenu::free_menu);
ClassDB::bind_method(D_METHOD("get_size", "rid"), &NativeMenu::get_size);
ClassDB::bind_method(D_METHOD("popup", "rid", "position"), &NativeMenu::popup);
ClassDB::bind_method(D_METHOD("set_interface_direction", "rid", "is_rtl"), &NativeMenu::set_interface_direction);
ClassDB::bind_method(D_METHOD("set_popup_open_callback", "rid", "callback"), &NativeMenu::set_popup_open_callback);
ClassDB::bind_method(D_METHOD("get_popup_open_callback", "rid"), &NativeMenu::get_popup_open_callback);
ClassDB::bind_method(D_METHOD("set_popup_close_callback", "rid", "callback"), &NativeMenu::set_popup_close_callback);
ClassDB::bind_method(D_METHOD("get_popup_close_callback", "rid"), &NativeMenu::get_popup_close_callback);
ClassDB::bind_method(D_METHOD("set_minimum_width", "rid", "width"), &NativeMenu::set_minimum_width);
ClassDB::bind_method(D_METHOD("get_minimum_width", "rid"), &NativeMenu::get_minimum_width);
ClassDB::bind_method(D_METHOD("is_opened", "rid"), &NativeMenu::is_opened);
ClassDB::bind_method(D_METHOD("add_submenu_item", "rid", "label", "submenu_rid", "tag", "index"), &NativeMenu::add_submenu_item, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_check_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_radio_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_radio_check_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_multistate_item", "rid", "label", "max_states", "default_state", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_multistate_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_separator", "rid", "index"), &NativeMenu::add_separator, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("find_item_index_with_text", "rid", "text"), &NativeMenu::find_item_index_with_text);
ClassDB::bind_method(D_METHOD("find_item_index_with_tag", "rid", "tag"), &NativeMenu::find_item_index_with_tag);
ClassDB::bind_method(D_METHOD("find_item_index_with_submenu", "rid", "submenu_rid"), &NativeMenu::find_item_index_with_submenu);
ClassDB::bind_method(D_METHOD("is_item_checked", "rid", "idx"), &NativeMenu::is_item_checked);
ClassDB::bind_method(D_METHOD("is_item_checkable", "rid", "idx"), &NativeMenu::is_item_checkable);
ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "rid", "idx"), &NativeMenu::is_item_radio_checkable);
ClassDB::bind_method(D_METHOD("get_item_callback", "rid", "idx"), &NativeMenu::get_item_callback);
ClassDB::bind_method(D_METHOD("get_item_key_callback", "rid", "idx"), &NativeMenu::get_item_key_callback);
ClassDB::bind_method(D_METHOD("get_item_tag", "rid", "idx"), &NativeMenu::get_item_tag);
ClassDB::bind_method(D_METHOD("get_item_text", "rid", "idx"), &NativeMenu::get_item_text);
ClassDB::bind_method(D_METHOD("get_item_submenu", "rid", "idx"), &NativeMenu::get_item_submenu);
ClassDB::bind_method(D_METHOD("get_item_accelerator", "rid", "idx"), &NativeMenu::get_item_accelerator);
ClassDB::bind_method(D_METHOD("is_item_disabled", "rid", "idx"), &NativeMenu::is_item_disabled);
ClassDB::bind_method(D_METHOD("is_item_hidden", "rid", "idx"), &NativeMenu::is_item_hidden);
ClassDB::bind_method(D_METHOD("get_item_tooltip", "rid", "idx"), &NativeMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_state", "rid", "idx"), &NativeMenu::get_item_state);
ClassDB::bind_method(D_METHOD("get_item_max_states", "rid", "idx"), &NativeMenu::get_item_max_states);
ClassDB::bind_method(D_METHOD("get_item_icon", "rid", "idx"), &NativeMenu::get_item_icon);
ClassDB::bind_method(D_METHOD("get_item_indentation_level", "rid", "idx"), &NativeMenu::get_item_indentation_level);
ClassDB::bind_method(D_METHOD("set_item_checked", "rid", "idx", "checked"), &NativeMenu::set_item_checked);
ClassDB::bind_method(D_METHOD("set_item_checkable", "rid", "idx", "checkable"), &NativeMenu::set_item_checkable);
ClassDB::bind_method(D_METHOD("set_item_radio_checkable", "rid", "idx", "checkable"), &NativeMenu::set_item_radio_checkable);
ClassDB::bind_method(D_METHOD("set_item_callback", "rid", "idx", "callback"), &NativeMenu::set_item_callback);
ClassDB::bind_method(D_METHOD("set_item_hover_callbacks", "rid", "idx", "callback"), &NativeMenu::set_item_hover_callbacks);
ClassDB::bind_method(D_METHOD("set_item_key_callback", "rid", "idx", "key_callback"), &NativeMenu::set_item_key_callback);
ClassDB::bind_method(D_METHOD("set_item_tag", "rid", "idx", "tag"), &NativeMenu::set_item_tag);
ClassDB::bind_method(D_METHOD("set_item_text", "rid", "idx", "text"), &NativeMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_submenu", "rid", "idx", "submenu_rid"), &NativeMenu::set_item_submenu);
ClassDB::bind_method(D_METHOD("set_item_accelerator", "rid", "idx", "keycode"), &NativeMenu::set_item_accelerator);
ClassDB::bind_method(D_METHOD("set_item_disabled", "rid", "idx", "disabled"), &NativeMenu::set_item_disabled);
ClassDB::bind_method(D_METHOD("set_item_hidden", "rid", "idx", "hidden"), &NativeMenu::set_item_hidden);
ClassDB::bind_method(D_METHOD("set_item_tooltip", "rid", "idx", "tooltip"), &NativeMenu::set_item_tooltip);
ClassDB::bind_method(D_METHOD("set_item_state", "rid", "idx", "state"), &NativeMenu::set_item_state);
ClassDB::bind_method(D_METHOD("set_item_max_states", "rid", "idx", "max_states"), &NativeMenu::set_item_max_states);
ClassDB::bind_method(D_METHOD("set_item_icon", "rid", "idx", "icon"), &NativeMenu::set_item_icon);
ClassDB::bind_method(D_METHOD("set_item_indentation_level", "rid", "idx", "level"), &NativeMenu::set_item_indentation_level);
ClassDB::bind_method(D_METHOD("get_item_count", "rid"), &NativeMenu::get_item_count);
ClassDB::bind_method(D_METHOD("is_system_menu", "rid"), &NativeMenu::is_system_menu);
ClassDB::bind_method(D_METHOD("remove_item", "rid", "idx"), &NativeMenu::remove_item);
ClassDB::bind_method(D_METHOD("clear", "rid"), &NativeMenu::clear);
BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU);
BIND_ENUM_CONSTANT(FEATURE_POPUP_MENU);
BIND_ENUM_CONSTANT(FEATURE_OPEN_CLOSE_CALLBACK);
BIND_ENUM_CONSTANT(FEATURE_HOVER_CALLBACK);
BIND_ENUM_CONSTANT(FEATURE_KEY_CALLBACK);
BIND_ENUM_CONSTANT(INVALID_MENU_ID);
BIND_ENUM_CONSTANT(MAIN_MENU_ID);
BIND_ENUM_CONSTANT(APPLICATION_MENU_ID);
BIND_ENUM_CONSTANT(WINDOW_MENU_ID);
BIND_ENUM_CONSTANT(HELP_MENU_ID);
BIND_ENUM_CONSTANT(DOCK_MENU_ID);
}
bool NativeMenu::has_feature(Feature p_feature) const {
return false;
}
bool NativeMenu::has_system_menu(SystemMenus p_menu_id) const {
return false;
}
RID NativeMenu::get_system_menu(SystemMenus p_menu_id) const {
WARN_PRINT("Global menus are not supported on this platform.");
return RID();
}
String NativeMenu::get_system_menu_name(SystemMenus p_menu_id) const {
switch (p_menu_id) {
case MAIN_MENU_ID:
return "Main menu";
case APPLICATION_MENU_ID:
return "Application menu";
case WINDOW_MENU_ID:
return "Window menu";
case HELP_MENU_ID:
return "Help menu";
case DOCK_MENU_ID:
return "Dock menu";
default:
return "Invalid";
}
}
RID NativeMenu::create_menu() {
WARN_PRINT("Global menus are not supported on this platform.");
return RID();
}
bool NativeMenu::has_menu(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
void NativeMenu::free_menu(const RID &p_rid) {
WARN_PRINT("Global menus are not supported on this platform.");
}
Size2 NativeMenu::get_size(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Size2();
}
void NativeMenu::popup(const RID &p_rid, const Vector2i &p_position) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_interface_direction(const RID &p_rid, bool p_is_rtl) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_popup_open_callback(const RID &p_rid, const Callable &p_callback) {
WARN_PRINT("Global menus are not supported on this platform.");
}
Callable NativeMenu::get_popup_open_callback(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Callable();
}
void NativeMenu::set_popup_close_callback(const RID &p_rid, const Callable &p_callback) {
WARN_PRINT("Global menus are not supported on this platform.");
}
Callable NativeMenu::get_popup_close_callback(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Callable();
}
bool NativeMenu::is_opened(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
void NativeMenu::set_minimum_width(const RID &p_rid, float p_width) {
WARN_PRINT("Global menus are not supported on this platform.");
}
float NativeMenu::get_minimum_width(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return 0.f;
}
int NativeMenu::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_icon_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_icon_radio_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::add_separator(const RID &p_rid, int p_index) {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::find_item_index_with_text(const RID &p_rid, const String &p_text) const {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::find_item_index_with_submenu(const RID &p_rid, const RID &p_submenu_rid) const {
if (!has_menu(p_rid) || !has_menu(p_submenu_rid)) {
return -1;
}
int count = get_item_count(p_rid);
for (int i = 0; i < count; i++) {
if (p_submenu_rid == get_item_submenu(p_rid, i)) {
return i;
}
}
return -1;
}
bool NativeMenu::is_item_checked(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
bool NativeMenu::is_item_checkable(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
bool NativeMenu::is_item_radio_checkable(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
Callable NativeMenu::get_item_callback(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Callable();
}
Callable NativeMenu::get_item_key_callback(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Callable();
}
Variant NativeMenu::get_item_tag(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Variant();
}
String NativeMenu::get_item_text(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return String();
}
RID NativeMenu::get_item_submenu(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return RID();
}
Key NativeMenu::get_item_accelerator(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Key::NONE;
}
bool NativeMenu::is_item_disabled(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
bool NativeMenu::is_item_hidden(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
String NativeMenu::get_item_tooltip(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return String();
}
int NativeMenu::get_item_state(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
int NativeMenu::get_item_max_states(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return -1;
}
Ref<Texture2D> NativeMenu::get_item_icon(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return Ref<Texture2D>();
}
int NativeMenu::get_item_indentation_level(const RID &p_rid, int p_idx) const {
WARN_PRINT("Global menus are not supported on this platform.");
return 0;
}
void NativeMenu::set_item_checked(const RID &p_rid, int p_idx, bool p_checked) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_text(const RID &p_rid, int p_idx, const String &p_text) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_state(const RID &p_rid, int p_idx, int p_state) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) {
WARN_PRINT("Global menus are not supported on this platform.");
}
int NativeMenu::get_item_count(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return 0;
}
bool NativeMenu::is_system_menu(const RID &p_rid) const {
WARN_PRINT("Global menus are not supported on this platform.");
return false;
}
void NativeMenu::remove_item(const RID &p_rid, int p_idx) {
WARN_PRINT("Global menus are not supported on this platform.");
}
void NativeMenu::clear(const RID &p_rid) {
WARN_PRINT("Global menus are not supported on this platform.");
}

View file

@ -0,0 +1,162 @@
/**************************************************************************/
/* native_menu.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NATIVE_MENU_H
#define NATIVE_MENU_H
#include "core/input/input.h"
#include "core/io/resource.h"
#include "core/os/os.h"
#include "core/variant/callable.h"
class Texture2D;
class NativeMenu : public Object {
GDCLASS(NativeMenu, Object)
static NativeMenu *singleton;
protected:
static void _bind_methods();
public:
_FORCE_INLINE_ static NativeMenu *get_singleton() {
return singleton;
}
enum Feature {
FEATURE_GLOBAL_MENU,
FEATURE_POPUP_MENU,
FEATURE_OPEN_CLOSE_CALLBACK,
FEATURE_HOVER_CALLBACK,
FEATURE_KEY_CALLBACK,
};
enum SystemMenus {
INVALID_MENU_ID,
MAIN_MENU_ID,
APPLICATION_MENU_ID,
WINDOW_MENU_ID,
HELP_MENU_ID,
DOCK_MENU_ID,
};
virtual bool has_feature(Feature p_feature) const;
virtual bool has_system_menu(SystemMenus p_menu_id) const;
virtual RID get_system_menu(SystemMenus p_menu_id) const;
virtual String get_system_menu_name(SystemMenus p_menu_id) const;
virtual RID create_menu();
virtual bool has_menu(const RID &p_rid) const;
virtual void free_menu(const RID &p_rid);
virtual Size2 get_size(const RID &p_rid) const;
virtual void popup(const RID &p_rid, const Vector2i &p_position);
virtual void set_interface_direction(const RID &p_rid, bool p_is_rtl);
virtual void set_popup_open_callback(const RID &p_rid, const Callable &p_callback);
virtual Callable get_popup_open_callback(const RID &p_rid) const;
virtual void set_popup_close_callback(const RID &p_rid, const Callable &p_callback);
virtual Callable get_popup_close_callback(const RID &p_rid) const;
virtual void set_minimum_width(const RID &p_rid, float p_width);
virtual float get_minimum_width(const RID &p_rid) const;
virtual bool is_opened(const RID &p_rid) const;
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1);
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_icon_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_icon_radio_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_separator(const RID &p_rid, int p_index = -1);
virtual int find_item_index_with_text(const RID &p_rid, const String &p_text) const;
virtual int find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const;
virtual int find_item_index_with_submenu(const RID &p_rid, const RID &p_submenu_rid) const;
virtual bool is_item_checked(const RID &p_rid, int p_idx) const;
virtual bool is_item_checkable(const RID &p_rid, int p_idx) const;
virtual bool is_item_radio_checkable(const RID &p_rid, int p_idx) const;
virtual Callable get_item_callback(const RID &p_rid, int p_idx) const;
virtual Callable get_item_key_callback(const RID &p_rid, int p_idx) const;
virtual Variant get_item_tag(const RID &p_rid, int p_idx) const;
virtual String get_item_text(const RID &p_rid, int p_idx) const;
virtual RID get_item_submenu(const RID &p_rid, int p_idx) const;
virtual Key get_item_accelerator(const RID &p_rid, int p_idx) const;
virtual bool is_item_disabled(const RID &p_rid, int p_idx) const;
virtual bool is_item_hidden(const RID &p_rid, int p_idx) const;
virtual String get_item_tooltip(const RID &p_rid, int p_idx) const;
virtual int get_item_state(const RID &p_rid, int p_idx) const;
virtual int get_item_max_states(const RID &p_rid, int p_idx) const;
virtual Ref<Texture2D> get_item_icon(const RID &p_rid, int p_idx) const;
virtual int get_item_indentation_level(const RID &p_rid, int p_idx) const;
virtual void set_item_checked(const RID &p_rid, int p_idx, bool p_checked);
virtual void set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable);
virtual void set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable);
virtual void set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback);
virtual void set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback);
virtual void set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback);
virtual void set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag);
virtual void set_item_text(const RID &p_rid, int p_idx, const String &p_text);
virtual void set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid);
virtual void set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode);
virtual void set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled);
virtual void set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden);
virtual void set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip);
virtual void set_item_state(const RID &p_rid, int p_idx, int p_state);
virtual void set_item_max_states(const RID &p_rid, int p_idx, int p_max_states);
virtual void set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon);
virtual void set_item_indentation_level(const RID &p_rid, int p_idx, int p_level);
virtual int get_item_count(const RID &p_rid) const;
virtual bool is_system_menu(const RID &p_rid) const;
virtual void remove_item(const RID &p_rid, int p_idx);
virtual void clear(const RID &p_rid);
NativeMenu() {
singleton = this;
}
~NativeMenu() {
singleton = nullptr;
}
};
VARIANT_ENUM_CAST(NativeMenu::Feature);
VARIANT_ENUM_CAST(NativeMenu::SystemMenus);
#endif // NATIVE_MENU_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,614 @@
/**************************************************************************/
/* display_server.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 DISPLAY_SERVER_H
#define DISPLAY_SERVER_H
#include "core/input/input.h"
#include "core/io/resource.h"
#include "core/os/os.h"
#include "core/variant/callable.h"
#include "display/native_menu.h"
class Texture2D;
class Image;
class DisplayServer : public Object {
GDCLASS(DisplayServer, Object)
static DisplayServer *singleton;
static bool hidpi_allowed;
#ifndef DISABLE_DEPRECATED
mutable HashMap<String, RID> menu_names;
RID _get_rid_from_name(NativeMenu *p_nmenu, const String &p_menu_root) const;
#endif
LocalVector<ObjectID> additional_outputs;
public:
_FORCE_INLINE_ static DisplayServer *get_singleton() {
return singleton;
}
enum WindowMode {
WINDOW_MODE_WINDOWED,
WINDOW_MODE_MINIMIZED,
WINDOW_MODE_MAXIMIZED,
WINDOW_MODE_FULLSCREEN,
WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
};
// Keep the VSyncMode enum values in sync with the `display/window/vsync/vsync_mode`
// project setting hint.
enum VSyncMode {
VSYNC_DISABLED,
VSYNC_ENABLED,
VSYNC_ADAPTIVE,
VSYNC_MAILBOX
};
enum HandleType {
DISPLAY_HANDLE,
WINDOW_HANDLE,
WINDOW_VIEW,
OPENGL_CONTEXT,
};
enum Context {
CONTEXT_EDITOR,
CONTEXT_PROJECTMAN,
CONTEXT_ENGINE,
};
typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, int p_screen, Context, Error &r_error);
typedef Vector<String> (*GetRenderingDriversFunction)();
private:
static void _input_set_mouse_mode(Input::MouseMode p_mode);
static Input::MouseMode _input_get_mouse_mode();
static void _input_warp(const Vector2 &p_to_pos);
static Input::CursorShape _input_get_current_cursor_shape();
static void _input_set_custom_mouse_cursor_func(const Ref<Resource> &, Input::CursorShape, const Vector2 &p_hostspot);
protected:
static void _bind_methods();
static Ref<Image> _get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot);
enum {
MAX_SERVERS = 64
};
struct DisplayServerCreate {
const char *name;
CreateFunction create_function;
GetRenderingDriversFunction get_rendering_drivers_function;
};
static DisplayServerCreate server_create_functions[MAX_SERVERS];
static int server_create_count;
friend class RendererViewport;
public:
enum Feature {
#ifndef DISABLE_DEPRECATED
FEATURE_GLOBAL_MENU,
#endif
FEATURE_SUBWINDOWS,
FEATURE_TOUCHSCREEN,
FEATURE_MOUSE,
FEATURE_MOUSE_WARP,
FEATURE_CLIPBOARD,
FEATURE_VIRTUAL_KEYBOARD,
FEATURE_CURSOR_SHAPE,
FEATURE_CUSTOM_CURSOR_SHAPE,
FEATURE_NATIVE_DIALOG,
FEATURE_IME,
FEATURE_WINDOW_TRANSPARENCY,
FEATURE_HIDPI,
FEATURE_ICON,
FEATURE_NATIVE_ICON,
FEATURE_ORIENTATION,
FEATURE_SWAP_BUFFERS,
FEATURE_KEEP_SCREEN_ON,
FEATURE_CLIPBOARD_PRIMARY,
FEATURE_TEXT_TO_SPEECH,
FEATURE_EXTEND_TO_TITLE,
FEATURE_SCREEN_CAPTURE,
FEATURE_STATUS_INDICATOR,
FEATURE_NATIVE_HELP,
FEATURE_NATIVE_DIALOG_INPUT,
FEATURE_NATIVE_DIALOG_FILE,
};
virtual bool has_feature(Feature p_feature) const = 0;
virtual String get_name() const = 0;
virtual void help_set_search_callbacks(const Callable &p_search_callback = Callable(), const Callable &p_action_callback = Callable());
#ifndef DISABLE_DEPRECATED
virtual void global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback = Callable(), const Callable &p_close_callback = Callable());
virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1);
virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int global_menu_add_separator(const String &p_menu_root, int p_index = -1);
virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const;
virtual int global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const;
virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const;
virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const;
virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const;
virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const;
virtual Callable global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const;
virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const;
virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const;
virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const;
virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const;
virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const;
virtual bool global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const;
virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const;
virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const;
virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const;
virtual Ref<Texture2D> global_menu_get_item_icon(const String &p_menu_root, int p_idx) const;
virtual int global_menu_get_item_indentation_level(const String &p_menu_root, int p_idx) const;
virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked);
virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable);
virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable);
virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback);
virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback);
virtual void global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback);
virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag);
virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text);
virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu);
virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode);
virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled);
virtual void global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden);
virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip);
virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state);
virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states);
virtual void global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon);
virtual void global_menu_set_item_indentation_level(const String &p_menu_root, int p_idx, int p_level);
virtual int global_menu_get_item_count(const String &p_menu_root) const;
virtual void global_menu_remove_item(const String &p_menu_root, int p_idx);
virtual void global_menu_clear(const String &p_menu_root);
virtual Dictionary global_menu_get_system_menu_roots() const;
#endif
struct TTSUtterance {
String text;
String voice;
int volume = 50;
float pitch = 1.f;
float rate = 1.f;
int id = 0;
};
enum TTSUtteranceEvent {
TTS_UTTERANCE_STARTED,
TTS_UTTERANCE_ENDED,
TTS_UTTERANCE_CANCELED,
TTS_UTTERANCE_BOUNDARY,
TTS_UTTERANCE_MAX,
};
private:
Callable utterance_callback[TTS_UTTERANCE_MAX];
public:
virtual bool tts_is_speaking() const;
virtual bool tts_is_paused() const;
virtual TypedArray<Dictionary> tts_get_voices() const;
virtual PackedStringArray tts_get_voices_for_language(const String &p_language) const;
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false);
virtual void tts_pause();
virtual void tts_resume();
virtual void tts_stop();
virtual void tts_set_utterance_callback(TTSUtteranceEvent p_event, const Callable &p_callable);
virtual void tts_post_utterance_event(TTSUtteranceEvent p_event, int p_id, int p_pos = 0);
virtual bool is_dark_mode_supported() const { return false; }
virtual bool is_dark_mode() const { return false; }
virtual Color get_accent_color() const { return Color(0, 0, 0, 0); }
virtual Color get_base_color() const { return Color(0, 0, 0, 0); }
virtual void set_system_theme_change_callback(const Callable &p_callable) {}
private:
static bool window_early_clear_override_enabled;
static Color window_early_clear_override_color;
protected:
static bool _get_window_early_clear_override(Color &r_color);
public:
static void set_early_window_clear_color_override(bool p_enabled, Color p_color = Color(0, 0, 0, 0));
enum MouseMode {
MOUSE_MODE_VISIBLE,
MOUSE_MODE_HIDDEN,
MOUSE_MODE_CAPTURED,
MOUSE_MODE_CONFINED,
MOUSE_MODE_CONFINED_HIDDEN,
};
virtual void mouse_set_mode(MouseMode p_mode);
virtual MouseMode mouse_get_mode() const;
virtual void warp_mouse(const Point2i &p_position);
virtual Point2i mouse_get_position() const;
virtual BitField<MouseButtonMask> mouse_get_button_state() const;
virtual void clipboard_set(const String &p_text);
virtual String clipboard_get() const;
virtual Ref<Image> clipboard_get_image() const;
virtual bool clipboard_has() const;
virtual bool clipboard_has_image() const;
virtual void clipboard_set_primary(const String &p_text);
virtual String clipboard_get_primary() const;
virtual TypedArray<Rect2> get_display_cutouts() const { return TypedArray<Rect2>(); }
virtual Rect2i get_display_safe_area() const { return screen_get_usable_rect(); }
enum {
SCREEN_WITH_MOUSE_FOCUS = -4,
SCREEN_WITH_KEYBOARD_FOCUS = -3,
SCREEN_PRIMARY = -2,
SCREEN_OF_MAIN_WINDOW = -1, // Note: for the main window, determine screen from position.
};
const float SCREEN_REFRESH_RATE_FALLBACK = -1.0; // Returned by screen_get_refresh_rate if the method fails.
int _get_screen_index(int p_screen) const {
switch (p_screen) {
case SCREEN_WITH_MOUSE_FOCUS: {
const Rect2i rect = Rect2i(mouse_get_position(), Vector2i(1, 1));
return get_screen_from_rect(rect);
} break;
case SCREEN_WITH_KEYBOARD_FOCUS: {
return get_keyboard_focus_screen();
} break;
case SCREEN_PRIMARY: {
return get_primary_screen();
} break;
case SCREEN_OF_MAIN_WINDOW: {
return window_get_current_screen(MAIN_WINDOW_ID);
} break;
default: {
return p_screen;
} break;
}
}
virtual int get_screen_count() const = 0;
virtual int get_primary_screen() const = 0;
virtual int get_keyboard_focus_screen() const { return get_primary_screen(); }
virtual int get_screen_from_rect(const Rect2 &p_rect) const;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
virtual float screen_get_max_scale() const {
float scale = 1.f;
int screen_count = get_screen_count();
for (int i = 0; i < screen_count; i++) {
scale = fmax(scale, screen_get_scale(i));
}
return scale;
}
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); }
virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return Ref<Image>(); }
virtual bool is_touchscreen_available() const;
// Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation`
// project setting hint.
enum ScreenOrientation {
SCREEN_LANDSCAPE,
SCREEN_PORTRAIT,
SCREEN_REVERSE_LANDSCAPE,
SCREEN_REVERSE_PORTRAIT,
SCREEN_SENSOR_LANDSCAPE,
SCREEN_SENSOR_PORTRAIT,
SCREEN_SENSOR,
};
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW);
virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
virtual void screen_set_keep_on(bool p_enable); //disable screensaver
virtual bool screen_is_kept_on() const;
enum {
MAIN_WINDOW_ID = 0,
INVALID_WINDOW_ID = -1,
INVALID_INDICATOR_ID = -1
};
typedef int WindowID;
typedef int IndicatorID;
virtual Vector<DisplayServer::WindowID> get_window_list() const = 0;
enum WindowFlags {
WINDOW_FLAG_RESIZE_DISABLED,
WINDOW_FLAG_BORDERLESS,
WINDOW_FLAG_ALWAYS_ON_TOP,
WINDOW_FLAG_TRANSPARENT,
WINDOW_FLAG_NO_FOCUS,
WINDOW_FLAG_POPUP,
WINDOW_FLAG_EXTEND_TO_TITLE,
WINDOW_FLAG_MOUSE_PASSTHROUGH,
WINDOW_FLAG_MAX,
};
// Separate enum otherwise we get warnings in switches not handling all values.
enum WindowFlagsBit {
WINDOW_FLAG_RESIZE_DISABLED_BIT = (1 << WINDOW_FLAG_RESIZE_DISABLED),
WINDOW_FLAG_BORDERLESS_BIT = (1 << WINDOW_FLAG_BORDERLESS),
WINDOW_FLAG_ALWAYS_ON_TOP_BIT = (1 << WINDOW_FLAG_ALWAYS_ON_TOP),
WINDOW_FLAG_TRANSPARENT_BIT = (1 << WINDOW_FLAG_TRANSPARENT),
WINDOW_FLAG_NO_FOCUS_BIT = (1 << WINDOW_FLAG_NO_FOCUS),
WINDOW_FLAG_POPUP_BIT = (1 << WINDOW_FLAG_POPUP),
WINDOW_FLAG_EXTEND_TO_TITLE_BIT = (1 << WINDOW_FLAG_EXTEND_TO_TITLE),
WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT = (1 << WINDOW_FLAG_MOUSE_PASSTHROUGH),
};
virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID);
virtual void show_window(WindowID p_id);
virtual void delete_sub_window(WindowID p_id);
virtual WindowID window_get_active_popup() const { return INVALID_WINDOW_ID; }
virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {}
virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const { return Rect2i(); }
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const = 0;
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
enum WindowEvent {
WINDOW_EVENT_MOUSE_ENTER,
WINDOW_EVENT_MOUSE_EXIT,
WINDOW_EVENT_FOCUS_IN,
WINDOW_EVENT_FOCUS_OUT,
WINDOW_EVENT_CLOSE_REQUEST,
WINDOW_EVENT_GO_BACK_REQUEST,
WINDOW_EVENT_DPI_CHANGE,
WINDOW_EVENT_TITLEBAR_CHANGE,
};
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual Size2i window_get_title_size(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) const { return Size2i(); }
virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID);
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_set_transient(WindowID p_window, WindowID p_parent) = 0;
virtual void window_set_exclusive(WindowID p_window, bool p_exclusive);
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_vsync_mode(VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID);
virtual VSyncMode window_get_vsync_mode(WindowID p_window) const;
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual WindowID get_focused_window() const;
virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) {}
virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const { return Vector3i(); }
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual bool can_any_window_draw() const = 0;
virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
virtual bool window_maximize_on_title_dbl_click() const { return false; }
virtual bool window_minimize_on_title_dbl_click() const { return false; }
// necessary for GL focus, may be able to use one of the existing functions for this, not sure yet
virtual void gl_window_make_current(DisplayServer::WindowID p_window_id);
virtual Point2i ime_get_selection() const;
virtual String ime_get_text() const;
enum VirtualKeyboardType {
KEYBOARD_TYPE_DEFAULT,
KEYBOARD_TYPE_MULTILINE,
KEYBOARD_TYPE_NUMBER,
KEYBOARD_TYPE_NUMBER_DECIMAL,
KEYBOARD_TYPE_PHONE,
KEYBOARD_TYPE_EMAIL_ADDRESS,
KEYBOARD_TYPE_PASSWORD,
KEYBOARD_TYPE_URL
};
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), VirtualKeyboardType p_type = KEYBOARD_TYPE_DEFAULT, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
virtual void virtual_keyboard_hide();
// returns height of the currently shown virtual keyboard (0 if keyboard is hidden)
virtual int virtual_keyboard_get_height() const;
enum CursorShape {
CURSOR_ARROW,
CURSOR_IBEAM,
CURSOR_POINTING_HAND,
CURSOR_CROSS,
CURSOR_WAIT,
CURSOR_BUSY,
CURSOR_DRAG,
CURSOR_CAN_DROP,
CURSOR_FORBIDDEN,
CURSOR_VSIZE,
CURSOR_HSIZE,
CURSOR_BDIAGSIZE,
CURSOR_FDIAGSIZE,
CURSOR_MOVE,
CURSOR_VSPLIT,
CURSOR_HSPLIT,
CURSOR_HELP,
CURSOR_MAX
};
virtual void cursor_set_shape(CursorShape p_shape);
virtual CursorShape cursor_get_shape() const;
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
virtual bool get_swap_cancel_ok();
virtual void enable_for_stealing_focus(OS::ProcessID pid);
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback);
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback);
enum FileDialogMode {
FILE_DIALOG_MODE_OPEN_FILE,
FILE_DIALOG_MODE_OPEN_FILES,
FILE_DIALOG_MODE_OPEN_DIR,
FILE_DIALOG_MODE_OPEN_ANY,
FILE_DIALOG_MODE_SAVE_FILE,
FILE_DIALOG_MODE_SAVE_MAX
};
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback);
virtual int keyboard_get_layout_count() const;
virtual int keyboard_get_current_layout() const;
virtual void keyboard_set_current_layout(int p_index);
virtual String keyboard_get_layout_language(int p_index) const;
virtual String keyboard_get_layout_name(int p_index) const;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const;
virtual Key keyboard_get_label_from_physical(Key p_keycode) const;
virtual int tablet_get_driver_count() const { return 1; }
virtual String tablet_get_driver_name(int p_driver) const { return "default"; }
virtual String tablet_get_current_driver() const { return "default"; }
virtual void tablet_set_current_driver(const String &p_driver) {}
virtual void process_events() = 0;
virtual void force_process_and_drop_events();
virtual void release_rendering_thread();
virtual void swap_buffers();
virtual void set_native_icon(const String &p_filename);
virtual void set_icon(const Ref<Image> &p_icon);
virtual IndicatorID create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback);
virtual void status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon);
virtual void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip);
virtual void status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid);
virtual void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback);
virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const;
virtual void delete_status_indicator(IndicatorID p_id);
virtual void set_context(Context p_context);
virtual bool is_window_transparency_available() const { return false; }
void register_additional_output(Object *p_output);
void unregister_additional_output(Object *p_output);
bool has_additional_outputs() const { return additional_outputs.size() > 0; }
static void register_create_function(const char *p_name, CreateFunction p_function, GetRenderingDriversFunction p_get_drivers);
static int get_create_function_count();
static const char *get_create_function_name(int p_index);
static Vector<String> get_create_function_rendering_drivers(int p_index);
static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
DisplayServer();
~DisplayServer();
};
VARIANT_ENUM_CAST(DisplayServer::WindowEvent)
VARIANT_ENUM_CAST(DisplayServer::Feature)
VARIANT_ENUM_CAST(DisplayServer::MouseMode)
VARIANT_ENUM_CAST(DisplayServer::ScreenOrientation)
VARIANT_ENUM_CAST(DisplayServer::WindowMode)
VARIANT_ENUM_CAST(DisplayServer::WindowFlags)
VARIANT_ENUM_CAST(DisplayServer::HandleType)
VARIANT_ENUM_CAST(DisplayServer::VirtualKeyboardType);
VARIANT_ENUM_CAST(DisplayServer::CursorShape)
VARIANT_ENUM_CAST(DisplayServer::VSyncMode)
VARIANT_ENUM_CAST(DisplayServer::TTSUtteranceEvent)
VARIANT_ENUM_CAST(DisplayServer::FileDialogMode)
#endif // DISPLAY_SERVER_H

View file

@ -0,0 +1,210 @@
/**************************************************************************/
/* display_server_headless.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 DISPLAY_SERVER_HEADLESS_H
#define DISPLAY_SERVER_HEADLESS_H
#include "servers/display_server.h"
#include "servers/rendering/dummy/rasterizer_dummy.h"
class DisplayServerHeadless : public DisplayServer {
private:
friend class DisplayServer;
static Vector<String> get_rendering_drivers_func() {
Vector<String> drivers;
drivers.push_back("dummy");
return drivers;
}
static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
r_error = OK;
RasterizerDummy::make_current();
return memnew(DisplayServerHeadless());
}
static void _dispatch_input_events(const Ref<InputEvent> &p_event) {
static_cast<DisplayServerHeadless *>(get_singleton())->_dispatch_input_event(p_event);
}
void _dispatch_input_event(const Ref<InputEvent> &p_event) {
if (input_event_callback.is_valid()) {
input_event_callback.call(p_event);
}
}
NativeMenu *native_menu = nullptr;
Callable input_event_callback;
public:
bool has_feature(Feature p_feature) const override { return false; }
String get_name() const override { return "headless"; }
// Stub implementations to prevent warnings from being printed for methods
// that don't affect the project's behavior in headless mode.
int get_screen_count() const override { return 0; }
int get_primary_screen() const override { return 0; };
Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return Point2i(); }
Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return Size2i(); }
Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return Rect2i(); }
int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return 96; /* 0 might cause issues */ }
float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return 1; }
float screen_get_max_scale() const override { return 1; }
float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return SCREEN_REFRESH_RATE_FALLBACK; }
void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override {}
void screen_set_keep_on(bool p_enable) override {}
Vector<DisplayServer::WindowID> get_window_list() const override { return Vector<DisplayServer::WindowID>(); }
WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID) override { return 0; }
void show_window(WindowID p_id) override {}
void delete_sub_window(WindowID p_id) override {}
WindowID get_window_at_screen_position(const Point2i &p_position) const override { return 0; }
void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override {}
ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override { return ObjectID(); }
void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {
input_event_callback = p_callable;
}
void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override {}
int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override { return -1; }
void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override {}
Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override { return Point2i(); }
Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override { return Point2i(); }
void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_transient(WindowID p_window, WindowID p_parent) override {}
void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override {}
Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override { return Size2i(); }
void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override {}
Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override { return Size2i(); }
void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override {}
Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override { return Size2i(); }
Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override { return Size2i(); }
void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override {}
WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override { return WINDOW_MODE_MINIMIZED; }
void window_set_vsync_mode(VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override {}
VSyncMode window_get_vsync_mode(WindowID p_window) const override { return VSyncMode::VSYNC_ENABLED; }
bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override { return false; }
void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override {}
bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override { return false; }
void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override {}
void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override {}
bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override { return true; };
bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override { return false; }
bool can_any_window_draw() const override { return false; }
void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID) override {}
int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override { return 0; }
void process_events() override {
Input::get_singleton()->flush_buffered_events();
}
void set_native_icon(const String &p_filename) override {}
void set_icon(const Ref<Image> &p_icon) override {}
void help_set_search_callbacks(const Callable &p_search_callback = Callable(), const Callable &p_action_callback = Callable()) override {}
bool tts_is_speaking() const override { return false; }
bool tts_is_paused() const override { return false; }
TypedArray<Dictionary> tts_get_voices() const override { return TypedArray<Dictionary>(); }
void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.0f, float p_rate = 1.0f, int p_utterance_id = 0, bool p_interrupt = false) override {}
void tts_pause() override {}
void tts_resume() override {}
void tts_stop() override {}
void mouse_set_mode(MouseMode p_mode) override {}
Point2i mouse_get_position() const override { return Point2i(); }
void clipboard_set(const String &p_text) override {}
void clipboard_set_primary(const String &p_text) override {}
void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), VirtualKeyboardType p_type = KEYBOARD_TYPE_DEFAULT, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override {}
void virtual_keyboard_hide() override {}
void cursor_set_shape(CursorShape p_shape) override {}
void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override {}
Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override { return ERR_UNAVAILABLE; }
Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override { return ERR_UNAVAILABLE; }
Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override { return ERR_UNAVAILABLE; }
Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override { return ERR_UNAVAILABLE; }
void release_rendering_thread() override {}
void swap_buffers() override {}
IndicatorID create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) override { return INVALID_INDICATOR_ID; }
void status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) override {}
void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) override {}
void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) override {}
void delete_status_indicator(IndicatorID p_id) override {}
DisplayServerHeadless() {
native_menu = memnew(NativeMenu);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
}
~DisplayServerHeadless() {
if (native_menu) {
memdelete(native_menu);
native_menu = nullptr;
}
}
};
#endif // DISPLAY_SERVER_HEADLESS_H

View file

@ -0,0 +1,10 @@
#!/usr/bin/env python
Import("env")
env_object = env.Clone()
env_object.add_source_files(env.servers_sources, "physics_server_2d_extension.cpp")
if not env["disable_3d"]:
env_object.add_source_files(env.servers_sources, "physics_server_3d_extension.cpp")

View file

@ -0,0 +1,351 @@
/**************************************************************************/
/* physics_server_2d_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "physics_server_2d_extension.h"
bool PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query(const RID &p_body) const {
return exclude && exclude->has(p_body);
}
thread_local const HashSet<RID> *PhysicsDirectSpaceState2DExtension::exclude = nullptr;
void PhysicsDirectSpaceState2DExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query);
GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "result");
GDVIRTUAL_BIND(_intersect_point, "position", "canvas_instance_id", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results");
GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result", "max_results");
GDVIRTUAL_BIND(_cast_motion, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "closest_safe", "closest_unsafe");
GDVIRTUAL_BIND(_collide_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results", "result_count");
GDVIRTUAL_BIND(_rest_info, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "rest_info");
}
PhysicsDirectSpaceState2DExtension::PhysicsDirectSpaceState2DExtension() {
}
void PhysicsDirectBodyState2DExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_total_gravity);
GDVIRTUAL_BIND(_get_total_linear_damp);
GDVIRTUAL_BIND(_get_total_angular_damp);
GDVIRTUAL_BIND(_get_center_of_mass);
GDVIRTUAL_BIND(_get_center_of_mass_local);
GDVIRTUAL_BIND(_get_inverse_mass);
GDVIRTUAL_BIND(_get_inverse_inertia);
GDVIRTUAL_BIND(_set_linear_velocity, "velocity");
GDVIRTUAL_BIND(_get_linear_velocity);
GDVIRTUAL_BIND(_set_angular_velocity, "velocity");
GDVIRTUAL_BIND(_get_angular_velocity);
GDVIRTUAL_BIND(_set_transform, "transform");
GDVIRTUAL_BIND(_get_transform);
GDVIRTUAL_BIND(_get_velocity_at_local_position, "local_position");
GDVIRTUAL_BIND(_apply_central_impulse, "impulse");
GDVIRTUAL_BIND(_apply_impulse, "impulse", "position");
GDVIRTUAL_BIND(_apply_torque_impulse, "impulse");
GDVIRTUAL_BIND(_apply_central_force, "force");
GDVIRTUAL_BIND(_apply_force, "force", "position");
GDVIRTUAL_BIND(_apply_torque, "torque");
GDVIRTUAL_BIND(_add_constant_central_force, "force");
GDVIRTUAL_BIND(_add_constant_force, "force", "position");
GDVIRTUAL_BIND(_add_constant_torque, "torque");
GDVIRTUAL_BIND(_set_constant_force, "force");
GDVIRTUAL_BIND(_get_constant_force);
GDVIRTUAL_BIND(_set_constant_torque, "torque");
GDVIRTUAL_BIND(_get_constant_torque);
GDVIRTUAL_BIND(_set_sleep_state, "enabled");
GDVIRTUAL_BIND(_is_sleeping);
GDVIRTUAL_BIND(_get_contact_count);
GDVIRTUAL_BIND(_get_contact_local_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_local_normal, "contact_idx");
GDVIRTUAL_BIND(_get_contact_local_shape, "contact_idx");
GDVIRTUAL_BIND(_get_contact_local_velocity_at_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_id, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_object, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_shape, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_velocity_at_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_impulse, "contact_idx");
GDVIRTUAL_BIND(_get_step);
GDVIRTUAL_BIND(_integrate_forces);
GDVIRTUAL_BIND(_get_space_state);
}
PhysicsDirectBodyState2DExtension::PhysicsDirectBodyState2DExtension() {
}
thread_local const HashSet<RID> *PhysicsServer2DExtension::exclude_bodies = nullptr;
thread_local const HashSet<ObjectID> *PhysicsServer2DExtension::exclude_objects = nullptr;
bool PhysicsServer2DExtension::body_test_motion_is_excluding_body(RID p_body) const {
return exclude_bodies && exclude_bodies->has(p_body);
}
bool PhysicsServer2DExtension::body_test_motion_is_excluding_object(ObjectID p_object) const {
return exclude_objects && exclude_objects->has(p_object);
}
void PhysicsServer2DExtension::_bind_methods() {
/* SHAPE API */
GDVIRTUAL_BIND(_world_boundary_shape_create);
GDVIRTUAL_BIND(_separation_ray_shape_create);
GDVIRTUAL_BIND(_segment_shape_create);
GDVIRTUAL_BIND(_circle_shape_create);
GDVIRTUAL_BIND(_rectangle_shape_create);
GDVIRTUAL_BIND(_capsule_shape_create);
GDVIRTUAL_BIND(_convex_polygon_shape_create);
GDVIRTUAL_BIND(_concave_polygon_shape_create);
GDVIRTUAL_BIND(_shape_set_data, "shape", "data");
GDVIRTUAL_BIND(_shape_set_custom_solver_bias, "shape", "bias");
GDVIRTUAL_BIND(_shape_get_type, "shape");
GDVIRTUAL_BIND(_shape_get_data, "shape");
GDVIRTUAL_BIND(_shape_get_custom_solver_bias, "shape");
GDVIRTUAL_BIND(_shape_collide, "shape_A", "xform_A", "motion_A", "shape_B", "xform_B", "motion_B", "results", "result_max", "result_count");
/* SPACE API */
GDVIRTUAL_BIND(_space_create);
GDVIRTUAL_BIND(_space_set_active, "space", "active");
GDVIRTUAL_BIND(_space_is_active, "space");
GDVIRTUAL_BIND(_space_set_param, "space", "param", "value");
GDVIRTUAL_BIND(_space_get_param, "space", "param");
GDVIRTUAL_BIND(_space_get_direct_state, "space");
GDVIRTUAL_BIND(_space_set_debug_contacts, "space", "max_contacts");
GDVIRTUAL_BIND(_space_get_contacts, "space");
GDVIRTUAL_BIND(_space_get_contact_count, "space");
/* AREA API */
GDVIRTUAL_BIND(_area_create);
GDVIRTUAL_BIND(_area_set_space, "area", "space");
GDVIRTUAL_BIND(_area_get_space, "area");
GDVIRTUAL_BIND(_area_add_shape, "area", "shape", "transform", "disabled");
GDVIRTUAL_BIND(_area_set_shape, "area", "shape_idx", "shape");
GDVIRTUAL_BIND(_area_set_shape_transform, "area", "shape_idx", "transform");
GDVIRTUAL_BIND(_area_set_shape_disabled, "area", "shape_idx", "disabled");
GDVIRTUAL_BIND(_area_get_shape_count, "area");
GDVIRTUAL_BIND(_area_get_shape, "area", "shape_idx");
GDVIRTUAL_BIND(_area_get_shape_transform, "area", "shape_idx");
GDVIRTUAL_BIND(_area_remove_shape, "area", "shape_idx");
GDVIRTUAL_BIND(_area_clear_shapes, "area");
GDVIRTUAL_BIND(_area_attach_object_instance_id, "area", "id");
GDVIRTUAL_BIND(_area_get_object_instance_id, "area");
GDVIRTUAL_BIND(_area_attach_canvas_instance_id, "area", "id");
GDVIRTUAL_BIND(_area_get_canvas_instance_id, "area");
GDVIRTUAL_BIND(_area_set_param, "area", "param", "value");
GDVIRTUAL_BIND(_area_set_transform, "area", "transform");
GDVIRTUAL_BIND(_area_get_param, "area", "param");
GDVIRTUAL_BIND(_area_get_transform, "area");
GDVIRTUAL_BIND(_area_set_collision_layer, "area", "layer");
GDVIRTUAL_BIND(_area_get_collision_layer, "area");
GDVIRTUAL_BIND(_area_set_collision_mask, "area", "mask");
GDVIRTUAL_BIND(_area_get_collision_mask, "area");
GDVIRTUAL_BIND(_area_set_monitorable, "area", "monitorable");
GDVIRTUAL_BIND(_area_set_pickable, "area", "pickable");
GDVIRTUAL_BIND(_area_set_monitor_callback, "area", "callback");
GDVIRTUAL_BIND(_area_set_area_monitor_callback, "area", "callback");
/* BODY API */
ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer2DExtension::body_test_motion_is_excluding_body);
ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer2DExtension::body_test_motion_is_excluding_object);
GDVIRTUAL_BIND(_body_create);
GDVIRTUAL_BIND(_body_set_space, "body", "space");
GDVIRTUAL_BIND(_body_get_space, "body");
GDVIRTUAL_BIND(_body_set_mode, "body", "mode");
GDVIRTUAL_BIND(_body_get_mode, "body");
GDVIRTUAL_BIND(_body_add_shape, "body", "shape", "transform", "disabled");
GDVIRTUAL_BIND(_body_set_shape, "body", "shape_idx", "shape");
GDVIRTUAL_BIND(_body_set_shape_transform, "body", "shape_idx", "transform");
GDVIRTUAL_BIND(_body_get_shape_count, "body");
GDVIRTUAL_BIND(_body_get_shape, "body", "shape_idx");
GDVIRTUAL_BIND(_body_get_shape_transform, "body", "shape_idx");
GDVIRTUAL_BIND(_body_set_shape_disabled, "body", "shape_idx", "disabled");
GDVIRTUAL_BIND(_body_set_shape_as_one_way_collision, "body", "shape_idx", "enable", "margin");
GDVIRTUAL_BIND(_body_remove_shape, "body", "shape_idx");
GDVIRTUAL_BIND(_body_clear_shapes, "body");
GDVIRTUAL_BIND(_body_attach_object_instance_id, "body", "id");
GDVIRTUAL_BIND(_body_get_object_instance_id, "body");
GDVIRTUAL_BIND(_body_attach_canvas_instance_id, "body", "id");
GDVIRTUAL_BIND(_body_get_canvas_instance_id, "body");
GDVIRTUAL_BIND(_body_set_continuous_collision_detection_mode, "body", "mode");
GDVIRTUAL_BIND(_body_get_continuous_collision_detection_mode, "body");
GDVIRTUAL_BIND(_body_set_collision_layer, "body", "layer");
GDVIRTUAL_BIND(_body_get_collision_layer, "body");
GDVIRTUAL_BIND(_body_set_collision_mask, "body", "mask");
GDVIRTUAL_BIND(_body_get_collision_mask, "body");
GDVIRTUAL_BIND(_body_set_collision_priority, "body", "priority");
GDVIRTUAL_BIND(_body_get_collision_priority, "body");
GDVIRTUAL_BIND(_body_set_param, "body", "param", "value");
GDVIRTUAL_BIND(_body_get_param, "body", "param");
GDVIRTUAL_BIND(_body_reset_mass_properties, "body");
GDVIRTUAL_BIND(_body_set_state, "body", "state", "value");
GDVIRTUAL_BIND(_body_get_state, "body", "state");
GDVIRTUAL_BIND(_body_apply_central_impulse, "body", "impulse");
GDVIRTUAL_BIND(_body_apply_torque_impulse, "body", "impulse");
GDVIRTUAL_BIND(_body_apply_impulse, "body", "impulse", "position");
GDVIRTUAL_BIND(_body_apply_central_force, "body", "force");
GDVIRTUAL_BIND(_body_apply_force, "body", "force", "position");
GDVIRTUAL_BIND(_body_apply_torque, "body", "torque");
GDVIRTUAL_BIND(_body_add_constant_central_force, "body", "force");
GDVIRTUAL_BIND(_body_add_constant_force, "body", "force", "position");
GDVIRTUAL_BIND(_body_add_constant_torque, "body", "torque");
GDVIRTUAL_BIND(_body_set_constant_force, "body", "force");
GDVIRTUAL_BIND(_body_get_constant_force, "body");
GDVIRTUAL_BIND(_body_set_constant_torque, "body", "torque");
GDVIRTUAL_BIND(_body_get_constant_torque, "body");
GDVIRTUAL_BIND(_body_set_axis_velocity, "body", "axis_velocity");
GDVIRTUAL_BIND(_body_add_collision_exception, "body", "excepted_body");
GDVIRTUAL_BIND(_body_remove_collision_exception, "body", "excepted_body");
GDVIRTUAL_BIND(_body_get_collision_exceptions, "body");
GDVIRTUAL_BIND(_body_set_max_contacts_reported, "body", "amount");
GDVIRTUAL_BIND(_body_get_max_contacts_reported, "body");
GDVIRTUAL_BIND(_body_set_contacts_reported_depth_threshold, "body", "threshold");
GDVIRTUAL_BIND(_body_get_contacts_reported_depth_threshold, "body");
GDVIRTUAL_BIND(_body_set_omit_force_integration, "body", "enable");
GDVIRTUAL_BIND(_body_is_omitting_force_integration, "body");
GDVIRTUAL_BIND(_body_set_state_sync_callback, "body", "callable");
GDVIRTUAL_BIND(_body_set_force_integration_callback, "body", "callable", "userdata");
GDVIRTUAL_BIND(_body_collide_shape, "body", "body_shape", "shape", "shape_xform", "motion", "results", "result_max", "result_count");
GDVIRTUAL_BIND(_body_set_pickable, "body", "pickable");
GDVIRTUAL_BIND(_body_get_direct_state, "body");
GDVIRTUAL_BIND(_body_test_motion, "body", "from", "motion", "margin", "collide_separation_ray", "recovery_as_collision", "result");
/* JOINT API */
GDVIRTUAL_BIND(_joint_create);
GDVIRTUAL_BIND(_joint_clear, "joint");
GDVIRTUAL_BIND(_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_joint_disable_collisions_between_bodies, "joint", "disable");
GDVIRTUAL_BIND(_joint_is_disabled_collisions_between_bodies, "joint");
GDVIRTUAL_BIND(_joint_make_pin, "joint", "anchor", "body_a", "body_b");
GDVIRTUAL_BIND(_joint_make_groove, "joint", "a_groove1", "a_groove2", "b_anchor", "body_a", "body_b");
GDVIRTUAL_BIND(_joint_make_damped_spring, "joint", "anchor_a", "anchor_b", "body_a", "body_b");
GDVIRTUAL_BIND(_pin_joint_set_flag, "joint", "flag", "enabled");
GDVIRTUAL_BIND(_pin_joint_get_flag, "joint", "flag");
GDVIRTUAL_BIND(_pin_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_pin_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_damped_spring_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_damped_spring_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_joint_get_type, "joint");
/* MISC */
GDVIRTUAL_BIND(_free_rid, "rid");
GDVIRTUAL_BIND(_set_active, "active");
GDVIRTUAL_BIND(_init);
GDVIRTUAL_BIND(_step, "step");
GDVIRTUAL_BIND(_sync);
GDVIRTUAL_BIND(_flush_queries);
GDVIRTUAL_BIND(_end_sync);
GDVIRTUAL_BIND(_finish);
GDVIRTUAL_BIND(_is_flushing_queries);
GDVIRTUAL_BIND(_get_process_info, "process_info");
}
PhysicsServer2DExtension::PhysicsServer2DExtension() {
}
PhysicsServer2DExtension::~PhysicsServer2DExtension() {
}

View file

@ -0,0 +1,456 @@
/**************************************************************************/
/* physics_server_2d_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 PHYSICS_SERVER_2D_EXTENSION_H
#define PHYSICS_SERVER_2D_EXTENSION_H
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
#include "core/variant/type_info.h"
#include "core/variant/typed_array.h"
#include "servers/physics_server_2d.h"
class PhysicsDirectBodyState2DExtension : public PhysicsDirectBodyState2D {
GDCLASS(PhysicsDirectBodyState2DExtension, PhysicsDirectBodyState2D);
protected:
static void _bind_methods();
public:
// The warning is valid, but unavoidable. If the function is not overridden it will error anyway.
EXBIND0RC(Vector2, get_total_gravity)
EXBIND0RC(real_t, get_total_angular_damp)
EXBIND0RC(real_t, get_total_linear_damp)
EXBIND0RC(Vector2, get_center_of_mass)
EXBIND0RC(Vector2, get_center_of_mass_local)
EXBIND0RC(real_t, get_inverse_mass)
EXBIND0RC(real_t, get_inverse_inertia)
EXBIND1(set_linear_velocity, const Vector2 &)
EXBIND0RC(Vector2, get_linear_velocity)
EXBIND1(set_angular_velocity, real_t)
EXBIND0RC(real_t, get_angular_velocity)
EXBIND1(set_transform, const Transform2D &)
EXBIND0RC(Transform2D, get_transform)
EXBIND1RC(Vector2, get_velocity_at_local_position, const Vector2 &)
EXBIND1(apply_central_impulse, const Vector2 &)
EXBIND1(apply_torque_impulse, real_t)
EXBIND2(apply_impulse, const Vector2 &, const Vector2 &)
EXBIND1(apply_central_force, const Vector2 &)
EXBIND2(apply_force, const Vector2 &, const Vector2 &)
EXBIND1(apply_torque, real_t)
EXBIND1(add_constant_central_force, const Vector2 &)
EXBIND2(add_constant_force, const Vector2 &, const Vector2 &)
EXBIND1(add_constant_torque, real_t)
EXBIND1(set_constant_force, const Vector2 &)
EXBIND0RC(Vector2, get_constant_force)
EXBIND1(set_constant_torque, real_t)
EXBIND0RC(real_t, get_constant_torque)
EXBIND1(set_sleep_state, bool)
EXBIND0RC(bool, is_sleeping)
EXBIND0RC(int, get_contact_count)
EXBIND1RC(Vector2, get_contact_local_position, int)
EXBIND1RC(Vector2, get_contact_local_normal, int)
EXBIND1RC(Vector2, get_contact_local_velocity_at_position, int)
EXBIND1RC(int, get_contact_local_shape, int)
EXBIND1RC(RID, get_contact_collider, int)
EXBIND1RC(Vector2, get_contact_collider_position, int)
EXBIND1RC(ObjectID, get_contact_collider_id, int)
EXBIND1RC(Object *, get_contact_collider_object, int)
EXBIND1RC(int, get_contact_collider_shape, int)
EXBIND1RC(Vector2, get_contact_collider_velocity_at_position, int)
EXBIND1RC(Vector2, get_contact_impulse, int)
EXBIND0RC(real_t, get_step)
EXBIND0(integrate_forces)
EXBIND0R(PhysicsDirectSpaceState2D *, get_space_state)
PhysicsDirectBodyState2DExtension();
};
typedef PhysicsDirectSpaceState2D::RayResult PhysicsServer2DExtensionRayResult;
typedef PhysicsDirectSpaceState2D::ShapeResult PhysicsServer2DExtensionShapeResult;
typedef PhysicsDirectSpaceState2D::ShapeRestInfo PhysicsServer2DExtensionShapeRestInfo;
GDVIRTUAL_NATIVE_PTR(PhysicsServer2DExtensionRayResult)
GDVIRTUAL_NATIVE_PTR(PhysicsServer2DExtensionShapeResult)
GDVIRTUAL_NATIVE_PTR(PhysicsServer2DExtensionShapeRestInfo)
class PhysicsDirectSpaceState2DExtension : public PhysicsDirectSpaceState2D {
GDCLASS(PhysicsDirectSpaceState2DExtension, PhysicsDirectSpaceState2D);
thread_local static const HashSet<RID> *exclude;
protected:
static void _bind_methods();
bool is_body_excluded_from_query(const RID &p_body) const;
GDVIRTUAL7R(bool, _intersect_ray, const Vector2 &, const Vector2 &, uint32_t, bool, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionRayResult>)
GDVIRTUAL7R(int, _intersect_point, const Vector2 &, ObjectID, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
GDVIRTUAL9R(int, _intersect_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
GDVIRTUAL9R(bool, _cast_motion, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>)
GDVIRTUAL10R(bool, _collide_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
GDVIRTUAL8R(bool, _rest_info, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeRestInfo>)
public:
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_intersect_ray, p_parameters.from, p_parameters.to, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.hit_from_inside, &r_result, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = false;
GDVIRTUAL_REQUIRED_CALL(_intersect_point, p_parameters.position, p_parameters.canvas_instance_id, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_intersect_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_cast_motion, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, &p_closest_safe, &p_closest_unsafe, ret);
exclude = nullptr;
return ret;
}
virtual bool collide_shape(const ShapeParameters &p_parameters, Vector2 *r_results, int p_result_max, int &r_result_count) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_collide_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, &r_result_count, ret);
exclude = nullptr;
return ret;
}
virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_rest_info, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_info, ret);
exclude = nullptr;
return ret;
}
PhysicsDirectSpaceState2DExtension();
};
typedef PhysicsServer2D::MotionResult PhysicsServer2DExtensionMotionResult;
GDVIRTUAL_NATIVE_PTR(PhysicsServer2DExtensionMotionResult)
class PhysicsServer2DExtension : public PhysicsServer2D {
GDCLASS(PhysicsServer2DExtension, PhysicsServer2D);
protected:
static void _bind_methods();
GDVIRTUAL9R(bool, _shape_collide, RID, const Transform2D &, const Vector2 &, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
GDVIRTUAL8R(bool, _body_collide_shape, RID, int, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
public:
// The warning is valid, but unavoidable. If the function is not overridden it will error anyway.
/* SHAPE API */
EXBIND0R(RID, world_boundary_shape_create)
EXBIND0R(RID, separation_ray_shape_create)
EXBIND0R(RID, segment_shape_create)
EXBIND0R(RID, circle_shape_create)
EXBIND0R(RID, rectangle_shape_create)
EXBIND0R(RID, capsule_shape_create)
EXBIND0R(RID, convex_polygon_shape_create)
EXBIND0R(RID, concave_polygon_shape_create)
EXBIND2(shape_set_data, RID, const Variant &)
EXBIND2(shape_set_custom_solver_bias, RID, real_t)
EXBIND1RC(ShapeType, shape_get_type, RID)
EXBIND1RC(Variant, shape_get_data, RID)
EXBIND1RC(real_t, shape_get_custom_solver_bias, RID)
virtual bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override {
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_shape_collide, p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, &r_result_count, ret);
return ret;
}
/* SPACE API */
EXBIND0R(RID, space_create)
EXBIND2(space_set_active, RID, bool)
EXBIND1RC(bool, space_is_active, RID)
EXBIND3(space_set_param, RID, SpaceParameter, real_t)
EXBIND2RC(real_t, space_get_param, RID, SpaceParameter)
EXBIND1R(PhysicsDirectSpaceState2D *, space_get_direct_state, RID)
EXBIND2(space_set_debug_contacts, RID, int)
EXBIND1RC(Vector<Vector2>, space_get_contacts, RID)
EXBIND1RC(int, space_get_contact_count, RID)
/* AREA API */
//EXBIND0RID(area);
EXBIND0R(RID, area_create)
EXBIND2(area_set_space, RID, RID)
EXBIND1RC(RID, area_get_space, RID)
EXBIND4(area_add_shape, RID, RID, const Transform2D &, bool)
EXBIND3(area_set_shape, RID, int, RID)
EXBIND3(area_set_shape_transform, RID, int, const Transform2D &)
EXBIND3(area_set_shape_disabled, RID, int, bool)
EXBIND1RC(int, area_get_shape_count, RID)
EXBIND2RC(RID, area_get_shape, RID, int)
EXBIND2RC(Transform2D, area_get_shape_transform, RID, int)
EXBIND2(area_remove_shape, RID, int)
EXBIND1(area_clear_shapes, RID)
EXBIND2(area_attach_object_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, area_get_object_instance_id, RID)
EXBIND2(area_attach_canvas_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, area_get_canvas_instance_id, RID)
EXBIND3(area_set_param, RID, AreaParameter, const Variant &)
EXBIND2(area_set_transform, RID, const Transform2D &)
EXBIND2RC(Variant, area_get_param, RID, AreaParameter)
EXBIND1RC(Transform2D, area_get_transform, RID)
EXBIND2(area_set_collision_layer, RID, uint32_t)
EXBIND1RC(uint32_t, area_get_collision_layer, RID)
EXBIND2(area_set_collision_mask, RID, uint32_t)
EXBIND1RC(uint32_t, area_get_collision_mask, RID)
EXBIND2(area_set_monitorable, RID, bool)
EXBIND2(area_set_pickable, RID, bool)
EXBIND2(area_set_monitor_callback, RID, const Callable &)
EXBIND2(area_set_area_monitor_callback, RID, const Callable &)
/* BODY API */
//EXBIND2RID(body,BodyMode,bool);
EXBIND0R(RID, body_create)
EXBIND2(body_set_space, RID, RID)
EXBIND1RC(RID, body_get_space, RID)
EXBIND2(body_set_mode, RID, BodyMode)
EXBIND1RC(BodyMode, body_get_mode, RID)
EXBIND4(body_add_shape, RID, RID, const Transform2D &, bool)
EXBIND3(body_set_shape, RID, int, RID)
EXBIND3(body_set_shape_transform, RID, int, const Transform2D &)
EXBIND1RC(int, body_get_shape_count, RID)
EXBIND2RC(RID, body_get_shape, RID, int)
EXBIND2RC(Transform2D, body_get_shape_transform, RID, int)
EXBIND3(body_set_shape_disabled, RID, int, bool)
EXBIND4(body_set_shape_as_one_way_collision, RID, int, bool, real_t)
EXBIND2(body_remove_shape, RID, int)
EXBIND1(body_clear_shapes, RID)
EXBIND2(body_attach_object_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, body_get_object_instance_id, RID)
EXBIND2(body_attach_canvas_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, body_get_canvas_instance_id, RID)
EXBIND2(body_set_continuous_collision_detection_mode, RID, CCDMode)
EXBIND1RC(CCDMode, body_get_continuous_collision_detection_mode, RID)
EXBIND2(body_set_collision_layer, RID, uint32_t)
EXBIND1RC(uint32_t, body_get_collision_layer, RID)
EXBIND2(body_set_collision_mask, RID, uint32_t)
EXBIND1RC(uint32_t, body_get_collision_mask, RID)
EXBIND2(body_set_collision_priority, RID, real_t)
EXBIND1RC(real_t, body_get_collision_priority, RID)
EXBIND3(body_set_param, RID, BodyParameter, const Variant &)
EXBIND2RC(Variant, body_get_param, RID, BodyParameter)
EXBIND1(body_reset_mass_properties, RID)
EXBIND3(body_set_state, RID, BodyState, const Variant &)
EXBIND2RC(Variant, body_get_state, RID, BodyState)
EXBIND2(body_apply_central_impulse, RID, const Vector2 &)
EXBIND2(body_apply_torque_impulse, RID, real_t)
EXBIND3(body_apply_impulse, RID, const Vector2 &, const Vector2 &)
EXBIND2(body_apply_central_force, RID, const Vector2 &)
EXBIND3(body_apply_force, RID, const Vector2 &, const Vector2 &)
EXBIND2(body_apply_torque, RID, real_t)
EXBIND2(body_add_constant_central_force, RID, const Vector2 &)
EXBIND3(body_add_constant_force, RID, const Vector2 &, const Vector2 &)
EXBIND2(body_add_constant_torque, RID, real_t)
EXBIND2(body_set_constant_force, RID, const Vector2 &)
EXBIND1RC(Vector2, body_get_constant_force, RID)
EXBIND2(body_set_constant_torque, RID, real_t)
EXBIND1RC(real_t, body_get_constant_torque, RID)
EXBIND2(body_set_axis_velocity, RID, const Vector2 &)
EXBIND2(body_add_collision_exception, RID, RID)
EXBIND2(body_remove_collision_exception, RID, RID)
GDVIRTUAL1RC(TypedArray<RID>, _body_get_collision_exceptions, RID)
void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
GDVIRTUAL_REQUIRED_CALL(_body_get_collision_exceptions, p_body, ret);
for (int i = 0; i < ret.size(); i++) {
p_exceptions->push_back(ret[i]);
}
}
EXBIND2(body_set_max_contacts_reported, RID, int)
EXBIND1RC(int, body_get_max_contacts_reported, RID)
EXBIND2(body_set_contacts_reported_depth_threshold, RID, real_t)
EXBIND1RC(real_t, body_get_contacts_reported_depth_threshold, RID)
EXBIND2(body_set_omit_force_integration, RID, bool)
EXBIND1RC(bool, body_is_omitting_force_integration, RID)
EXBIND2(body_set_state_sync_callback, RID, const Callable &)
EXBIND3(body_set_force_integration_callback, RID, const Callable &, const Variant &)
virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override {
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_body_collide_shape, p_body, p_body_shape, p_shape, p_shape_xform, p_motion, r_results, p_result_max, &r_result_count, ret);
return ret;
}
EXBIND2(body_set_pickable, RID, bool)
EXBIND1R(PhysicsDirectBodyState2D *, body_get_direct_state, RID)
GDVIRTUAL7RC(bool, _body_test_motion, RID, const Transform2D &, const Vector2 &, real_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionMotionResult>)
thread_local static const HashSet<RID> *exclude_bodies;
thread_local static const HashSet<ObjectID> *exclude_objects;
bool body_test_motion_is_excluding_body(RID p_body) const;
bool body_test_motion_is_excluding_object(ObjectID p_object) const;
bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
bool ret = false;
exclude_bodies = &p_parameters.exclude_bodies;
exclude_objects = &p_parameters.exclude_objects;
GDVIRTUAL_REQUIRED_CALL(_body_test_motion, p_body, p_parameters.from, p_parameters.motion, p_parameters.margin, p_parameters.collide_separation_ray, p_parameters.recovery_as_collision, r_result, ret);
exclude_bodies = nullptr;
exclude_objects = nullptr;
return ret;
}
/* JOINT API */
EXBIND0R(RID, joint_create)
EXBIND1(joint_clear, RID)
EXBIND3(joint_set_param, RID, JointParam, real_t)
EXBIND2RC(real_t, joint_get_param, RID, JointParam)
EXBIND2(joint_disable_collisions_between_bodies, RID, bool)
EXBIND1RC(bool, joint_is_disabled_collisions_between_bodies, RID)
EXBIND4(joint_make_pin, RID, const Vector2 &, RID, RID)
EXBIND6(joint_make_groove, RID, const Vector2 &, const Vector2 &, const Vector2 &, RID, RID)
EXBIND5(joint_make_damped_spring, RID, const Vector2 &, const Vector2 &, RID, RID)
EXBIND3(pin_joint_set_flag, RID, PinJointFlag, bool)
EXBIND2RC(bool, pin_joint_get_flag, RID, PinJointFlag)
EXBIND3(pin_joint_set_param, RID, PinJointParam, real_t)
EXBIND2RC(real_t, pin_joint_get_param, RID, PinJointParam)
EXBIND3(damped_spring_joint_set_param, RID, DampedSpringParam, real_t)
EXBIND2RC(real_t, damped_spring_joint_get_param, RID, DampedSpringParam)
EXBIND1RC(JointType, joint_get_type, RID)
/* MISC */
GDVIRTUAL1(_free_rid, RID)
virtual void free(RID p_rid) override {
GDVIRTUAL_REQUIRED_CALL(_free_rid, p_rid);
}
EXBIND1(set_active, bool)
EXBIND0(init)
EXBIND1(step, real_t)
EXBIND0(sync)
EXBIND0(flush_queries)
EXBIND0(end_sync)
EXBIND0(finish)
EXBIND0RC(bool, is_flushing_queries)
EXBIND1R(int, get_process_info, ProcessInfo)
PhysicsServer2DExtension();
~PhysicsServer2DExtension();
};
#endif // PHYSICS_SERVER_2D_EXTENSION_H

View file

@ -0,0 +1,431 @@
/**************************************************************************/
/* physics_server_3d_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "physics_server_3d_extension.h"
bool PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query(const RID &p_body) const {
return exclude && exclude->has(p_body);
}
thread_local const HashSet<RID> *PhysicsDirectSpaceState3DExtension::exclude = nullptr;
void PhysicsDirectSpaceState3DExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query);
GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "hit_back_faces", "pick_ray", "result");
GDVIRTUAL_BIND(_intersect_point, "position", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results");
GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result_count", "max_results");
GDVIRTUAL_BIND(_cast_motion, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "closest_safe", "closest_unsafe", "info");
GDVIRTUAL_BIND(_collide_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results", "result_count");
GDVIRTUAL_BIND(_rest_info, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "rest_info");
GDVIRTUAL_BIND(_get_closest_point_to_object_volume, "object", "point");
}
PhysicsDirectSpaceState3DExtension::PhysicsDirectSpaceState3DExtension() {
}
void PhysicsDirectBodyState3DExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_total_gravity);
GDVIRTUAL_BIND(_get_total_linear_damp);
GDVIRTUAL_BIND(_get_total_angular_damp);
GDVIRTUAL_BIND(_get_center_of_mass);
GDVIRTUAL_BIND(_get_center_of_mass_local);
GDVIRTUAL_BIND(_get_principal_inertia_axes);
GDVIRTUAL_BIND(_get_inverse_mass);
GDVIRTUAL_BIND(_get_inverse_inertia);
GDVIRTUAL_BIND(_get_inverse_inertia_tensor);
GDVIRTUAL_BIND(_set_linear_velocity, "velocity");
GDVIRTUAL_BIND(_get_linear_velocity);
GDVIRTUAL_BIND(_set_angular_velocity, "velocity");
GDVIRTUAL_BIND(_get_angular_velocity);
GDVIRTUAL_BIND(_set_transform, "transform");
GDVIRTUAL_BIND(_get_transform);
GDVIRTUAL_BIND(_get_velocity_at_local_position, "local_position");
GDVIRTUAL_BIND(_apply_central_impulse, "impulse");
GDVIRTUAL_BIND(_apply_impulse, "impulse", "position");
GDVIRTUAL_BIND(_apply_torque_impulse, "impulse");
GDVIRTUAL_BIND(_apply_central_force, "force");
GDVIRTUAL_BIND(_apply_force, "force", "position");
GDVIRTUAL_BIND(_apply_torque, "torque");
GDVIRTUAL_BIND(_add_constant_central_force, "force");
GDVIRTUAL_BIND(_add_constant_force, "force", "position");
GDVIRTUAL_BIND(_add_constant_torque, "torque");
GDVIRTUAL_BIND(_set_constant_force, "force");
GDVIRTUAL_BIND(_get_constant_force);
GDVIRTUAL_BIND(_set_constant_torque, "torque");
GDVIRTUAL_BIND(_get_constant_torque);
GDVIRTUAL_BIND(_set_sleep_state, "enabled");
GDVIRTUAL_BIND(_is_sleeping);
GDVIRTUAL_BIND(_get_contact_count);
GDVIRTUAL_BIND(_get_contact_local_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_local_normal, "contact_idx");
GDVIRTUAL_BIND(_get_contact_impulse, "contact_idx");
GDVIRTUAL_BIND(_get_contact_local_shape, "contact_idx");
GDVIRTUAL_BIND(_get_contact_local_velocity_at_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_position, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_id, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_object, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_shape, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_velocity_at_position, "contact_idx");
GDVIRTUAL_BIND(_get_step);
GDVIRTUAL_BIND(_integrate_forces);
GDVIRTUAL_BIND(_get_space_state);
}
PhysicsDirectBodyState3DExtension::PhysicsDirectBodyState3DExtension() {
}
thread_local const HashSet<RID> *PhysicsServer3DExtension::exclude_bodies = nullptr;
thread_local const HashSet<ObjectID> *PhysicsServer3DExtension::exclude_objects = nullptr;
bool PhysicsServer3DExtension::body_test_motion_is_excluding_body(RID p_body) const {
return exclude_bodies && exclude_bodies->has(p_body);
}
bool PhysicsServer3DExtension::body_test_motion_is_excluding_object(ObjectID p_object) const {
return exclude_objects && exclude_objects->has(p_object);
}
void PhysicsServer3DExtension::_bind_methods() {
/* SHAPE API */
GDVIRTUAL_BIND(_world_boundary_shape_create);
GDVIRTUAL_BIND(_separation_ray_shape_create);
GDVIRTUAL_BIND(_sphere_shape_create);
GDVIRTUAL_BIND(_box_shape_create);
GDVIRTUAL_BIND(_capsule_shape_create);
GDVIRTUAL_BIND(_cylinder_shape_create);
GDVIRTUAL_BIND(_convex_polygon_shape_create);
GDVIRTUAL_BIND(_concave_polygon_shape_create);
GDVIRTUAL_BIND(_heightmap_shape_create);
GDVIRTUAL_BIND(_custom_shape_create);
GDVIRTUAL_BIND(_shape_set_data, "shape", "data");
GDVIRTUAL_BIND(_shape_set_custom_solver_bias, "shape", "bias");
GDVIRTUAL_BIND(_shape_set_margin, "shape", "margin");
GDVIRTUAL_BIND(_shape_get_margin, "shape");
GDVIRTUAL_BIND(_shape_get_type, "shape");
GDVIRTUAL_BIND(_shape_get_data, "shape");
GDVIRTUAL_BIND(_shape_get_custom_solver_bias, "shape");
/* SPACE API */
GDVIRTUAL_BIND(_space_create);
GDVIRTUAL_BIND(_space_set_active, "space", "active");
GDVIRTUAL_BIND(_space_is_active, "space");
GDVIRTUAL_BIND(_space_set_param, "space", "param", "value");
GDVIRTUAL_BIND(_space_get_param, "space", "param");
GDVIRTUAL_BIND(_space_get_direct_state, "space");
GDVIRTUAL_BIND(_space_set_debug_contacts, "space", "max_contacts");
GDVIRTUAL_BIND(_space_get_contacts, "space");
GDVIRTUAL_BIND(_space_get_contact_count, "space");
/* AREA API */
GDVIRTUAL_BIND(_area_create);
GDVIRTUAL_BIND(_area_set_space, "area", "space");
GDVIRTUAL_BIND(_area_get_space, "area");
GDVIRTUAL_BIND(_area_add_shape, "area", "shape", "transform", "disabled");
GDVIRTUAL_BIND(_area_set_shape, "area", "shape_idx", "shape");
GDVIRTUAL_BIND(_area_set_shape_transform, "area", "shape_idx", "transform");
GDVIRTUAL_BIND(_area_set_shape_disabled, "area", "shape_idx", "disabled");
GDVIRTUAL_BIND(_area_get_shape_count, "area");
GDVIRTUAL_BIND(_area_get_shape, "area", "shape_idx");
GDVIRTUAL_BIND(_area_get_shape_transform, "area", "shape_idx");
GDVIRTUAL_BIND(_area_remove_shape, "area", "shape_idx");
GDVIRTUAL_BIND(_area_clear_shapes, "area");
GDVIRTUAL_BIND(_area_attach_object_instance_id, "area", "id");
GDVIRTUAL_BIND(_area_get_object_instance_id, "area");
GDVIRTUAL_BIND(_area_set_param, "area", "param", "value");
GDVIRTUAL_BIND(_area_set_transform, "area", "transform");
GDVIRTUAL_BIND(_area_get_param, "area", "param");
GDVIRTUAL_BIND(_area_get_transform, "area");
GDVIRTUAL_BIND(_area_set_collision_layer, "area", "layer");
GDVIRTUAL_BIND(_area_get_collision_layer, "area");
GDVIRTUAL_BIND(_area_set_collision_mask, "area", "mask");
GDVIRTUAL_BIND(_area_get_collision_mask, "area");
GDVIRTUAL_BIND(_area_set_monitorable, "area", "monitorable");
GDVIRTUAL_BIND(_area_set_ray_pickable, "area", "enable");
GDVIRTUAL_BIND(_area_set_monitor_callback, "area", "callback");
GDVIRTUAL_BIND(_area_set_area_monitor_callback, "area", "callback");
/* BODY API */
ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer3DExtension::body_test_motion_is_excluding_body);
ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer3DExtension::body_test_motion_is_excluding_object);
GDVIRTUAL_BIND(_body_create);
GDVIRTUAL_BIND(_body_set_space, "body", "space");
GDVIRTUAL_BIND(_body_get_space, "body");
GDVIRTUAL_BIND(_body_set_mode, "body", "mode");
GDVIRTUAL_BIND(_body_get_mode, "body");
GDVIRTUAL_BIND(_body_add_shape, "body", "shape", "transform", "disabled");
GDVIRTUAL_BIND(_body_set_shape, "body", "shape_idx", "shape");
GDVIRTUAL_BIND(_body_set_shape_transform, "body", "shape_idx", "transform");
GDVIRTUAL_BIND(_body_set_shape_disabled, "body", "shape_idx", "disabled");
GDVIRTUAL_BIND(_body_get_shape_count, "body");
GDVIRTUAL_BIND(_body_get_shape, "body", "shape_idx");
GDVIRTUAL_BIND(_body_get_shape_transform, "body", "shape_idx");
GDVIRTUAL_BIND(_body_remove_shape, "body", "shape_idx");
GDVIRTUAL_BIND(_body_clear_shapes, "body");
GDVIRTUAL_BIND(_body_attach_object_instance_id, "body", "id");
GDVIRTUAL_BIND(_body_get_object_instance_id, "body");
GDVIRTUAL_BIND(_body_set_enable_continuous_collision_detection, "body", "enable");
GDVIRTUAL_BIND(_body_is_continuous_collision_detection_enabled, "body");
GDVIRTUAL_BIND(_body_set_collision_layer, "body", "layer");
GDVIRTUAL_BIND(_body_get_collision_layer, "body");
GDVIRTUAL_BIND(_body_set_collision_mask, "body", "mask");
GDVIRTUAL_BIND(_body_get_collision_mask, "body");
GDVIRTUAL_BIND(_body_set_collision_priority, "body", "priority");
GDVIRTUAL_BIND(_body_get_collision_priority, "body");
GDVIRTUAL_BIND(_body_set_user_flags, "body", "flags");
GDVIRTUAL_BIND(_body_get_user_flags, "body");
GDVIRTUAL_BIND(_body_set_param, "body", "param", "value");
GDVIRTUAL_BIND(_body_get_param, "body", "param");
GDVIRTUAL_BIND(_body_reset_mass_properties, "body");
GDVIRTUAL_BIND(_body_set_state, "body", "state", "value");
GDVIRTUAL_BIND(_body_get_state, "body", "state");
GDVIRTUAL_BIND(_body_apply_central_impulse, "body", "impulse");
GDVIRTUAL_BIND(_body_apply_impulse, "body", "impulse", "position");
GDVIRTUAL_BIND(_body_apply_torque_impulse, "body", "impulse");
GDVIRTUAL_BIND(_body_apply_central_force, "body", "force");
GDVIRTUAL_BIND(_body_apply_force, "body", "force", "position");
GDVIRTUAL_BIND(_body_apply_torque, "body", "torque");
GDVIRTUAL_BIND(_body_add_constant_central_force, "body", "force");
GDVIRTUAL_BIND(_body_add_constant_force, "body", "force", "position");
GDVIRTUAL_BIND(_body_add_constant_torque, "body", "torque");
GDVIRTUAL_BIND(_body_set_constant_force, "body", "force");
GDVIRTUAL_BIND(_body_get_constant_force, "body");
GDVIRTUAL_BIND(_body_set_constant_torque, "body", "torque");
GDVIRTUAL_BIND(_body_get_constant_torque, "body");
GDVIRTUAL_BIND(_body_set_axis_velocity, "body", "axis_velocity");
GDVIRTUAL_BIND(_body_set_axis_lock, "body", "axis", "lock");
GDVIRTUAL_BIND(_body_is_axis_locked, "body", "axis");
GDVIRTUAL_BIND(_body_add_collision_exception, "body", "excepted_body");
GDVIRTUAL_BIND(_body_remove_collision_exception, "body", "excepted_body");
GDVIRTUAL_BIND(_body_get_collision_exceptions, "body");
GDVIRTUAL_BIND(_body_set_max_contacts_reported, "body", "amount");
GDVIRTUAL_BIND(_body_get_max_contacts_reported, "body");
GDVIRTUAL_BIND(_body_set_contacts_reported_depth_threshold, "body", "threshold");
GDVIRTUAL_BIND(_body_get_contacts_reported_depth_threshold, "body");
GDVIRTUAL_BIND(_body_set_omit_force_integration, "body", "enable");
GDVIRTUAL_BIND(_body_is_omitting_force_integration, "body");
GDVIRTUAL_BIND(_body_set_state_sync_callback, "body", "callable");
GDVIRTUAL_BIND(_body_set_force_integration_callback, "body", "callable", "userdata");
GDVIRTUAL_BIND(_body_set_ray_pickable, "body", "enable");
GDVIRTUAL_BIND(_body_test_motion, "body", "from", "motion", "margin", "max_collisions", "collide_separation_ray", "recovery_as_collision", "result");
GDVIRTUAL_BIND(_body_get_direct_state, "body");
/* SOFT BODY API */
GDVIRTUAL_BIND(_soft_body_create);
GDVIRTUAL_BIND(_soft_body_update_rendering_server, "body", "rendering_server_handler");
GDVIRTUAL_BIND(_soft_body_set_space, "body", "space");
GDVIRTUAL_BIND(_soft_body_get_space, "body");
GDVIRTUAL_BIND(_soft_body_set_ray_pickable, "body", "enable");
GDVIRTUAL_BIND(_soft_body_set_collision_layer, "body", "layer");
GDVIRTUAL_BIND(_soft_body_get_collision_layer, "body");
GDVIRTUAL_BIND(_soft_body_set_collision_mask, "body", "mask");
GDVIRTUAL_BIND(_soft_body_get_collision_mask, "body");
GDVIRTUAL_BIND(_soft_body_add_collision_exception, "body", "body_b");
GDVIRTUAL_BIND(_soft_body_remove_collision_exception, "body", "body_b");
GDVIRTUAL_BIND(_soft_body_get_collision_exceptions, "body");
GDVIRTUAL_BIND(_soft_body_set_state, "body", "state", "variant");
GDVIRTUAL_BIND(_soft_body_get_state, "body", "state");
GDVIRTUAL_BIND(_soft_body_set_transform, "body", "transform");
GDVIRTUAL_BIND(_soft_body_set_simulation_precision, "body", "simulation_precision");
GDVIRTUAL_BIND(_soft_body_get_simulation_precision, "body");
GDVIRTUAL_BIND(_soft_body_set_total_mass, "body", "total_mass");
GDVIRTUAL_BIND(_soft_body_get_total_mass, "body");
GDVIRTUAL_BIND(_soft_body_set_linear_stiffness, "body", "linear_stiffness");
GDVIRTUAL_BIND(_soft_body_get_linear_stiffness, "body");
GDVIRTUAL_BIND(_soft_body_set_pressure_coefficient, "body", "pressure_coefficient");
GDVIRTUAL_BIND(_soft_body_get_pressure_coefficient, "body");
GDVIRTUAL_BIND(_soft_body_set_damping_coefficient, "body", "damping_coefficient");
GDVIRTUAL_BIND(_soft_body_get_damping_coefficient, "body");
GDVIRTUAL_BIND(_soft_body_set_drag_coefficient, "body", "drag_coefficient");
GDVIRTUAL_BIND(_soft_body_get_drag_coefficient, "body");
GDVIRTUAL_BIND(_soft_body_set_mesh, "body", "mesh");
GDVIRTUAL_BIND(_soft_body_get_bounds, "body");
GDVIRTUAL_BIND(_soft_body_move_point, "body", "point_index", "global_position");
GDVIRTUAL_BIND(_soft_body_get_point_global_position, "body", "point_index");
GDVIRTUAL_BIND(_soft_body_remove_all_pinned_points, "body");
GDVIRTUAL_BIND(_soft_body_pin_point, "body", "point_index", "pin");
GDVIRTUAL_BIND(_soft_body_is_point_pinned, "body", "point_index");
/* JOINT API */
GDVIRTUAL_BIND(_joint_create);
GDVIRTUAL_BIND(_joint_clear, "joint");
GDVIRTUAL_BIND(_joint_make_pin, "joint", "body_A", "local_A", "body_B", "local_B");
GDVIRTUAL_BIND(_pin_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_pin_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_pin_joint_set_local_a, "joint", "local_A");
GDVIRTUAL_BIND(_pin_joint_get_local_a, "joint");
GDVIRTUAL_BIND(_pin_joint_set_local_b, "joint", "local_B");
GDVIRTUAL_BIND(_pin_joint_get_local_b, "joint");
GDVIRTUAL_BIND(_joint_make_hinge, "joint", "body_A", "hinge_A", "body_B", "hinge_B");
GDVIRTUAL_BIND(_joint_make_hinge_simple, "joint", "body_A", "pivot_A", "axis_A", "body_B", "pivot_B", "axis_B");
GDVIRTUAL_BIND(_hinge_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_hinge_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_hinge_joint_set_flag, "joint", "flag", "enabled");
GDVIRTUAL_BIND(_hinge_joint_get_flag, "joint", "flag");
GDVIRTUAL_BIND(_joint_make_slider, "joint", "body_A", "local_ref_A", "body_B", "local_ref_B");
GDVIRTUAL_BIND(_slider_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_slider_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_joint_make_cone_twist, "joint", "body_A", "local_ref_A", "body_B", "local_ref_B");
GDVIRTUAL_BIND(_cone_twist_joint_set_param, "joint", "param", "value");
GDVIRTUAL_BIND(_cone_twist_joint_get_param, "joint", "param");
GDVIRTUAL_BIND(_joint_make_generic_6dof, "joint", "body_A", "local_ref_A", "body_B", "local_ref_B");
GDVIRTUAL_BIND(_generic_6dof_joint_set_param, "joint", "axis", "param", "value");
GDVIRTUAL_BIND(_generic_6dof_joint_get_param, "joint", "axis", "param");
GDVIRTUAL_BIND(_generic_6dof_joint_set_flag, "joint", "axis", "flag", "enable");
GDVIRTUAL_BIND(_generic_6dof_joint_get_flag, "joint", "axis", "flag");
GDVIRTUAL_BIND(_joint_get_type, "joint");
GDVIRTUAL_BIND(_joint_set_solver_priority, "joint", "priority");
GDVIRTUAL_BIND(_joint_get_solver_priority, "joint");
GDVIRTUAL_BIND(_joint_disable_collisions_between_bodies, "joint", "disable");
GDVIRTUAL_BIND(_joint_is_disabled_collisions_between_bodies, "joint");
GDVIRTUAL_BIND(_free_rid, "rid");
GDVIRTUAL_BIND(_set_active, "active");
GDVIRTUAL_BIND(_init);
GDVIRTUAL_BIND(_step, "step");
GDVIRTUAL_BIND(_sync);
GDVIRTUAL_BIND(_flush_queries);
GDVIRTUAL_BIND(_end_sync);
GDVIRTUAL_BIND(_finish);
GDVIRTUAL_BIND(_is_flushing_queries);
GDVIRTUAL_BIND(_get_process_info, "process_info");
}
PhysicsServer3DExtension::PhysicsServer3DExtension() {
}
PhysicsServer3DExtension::~PhysicsServer3DExtension() {
}

View file

@ -0,0 +1,544 @@
/**************************************************************************/
/* physics_server_3d_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 PHYSICS_SERVER_3D_EXTENSION_H
#define PHYSICS_SERVER_3D_EXTENSION_H
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/script_language.h"
#include "core/variant/native_ptr.h"
#include "core/variant/type_info.h"
#include "core/variant/typed_array.h"
#include "servers/physics_server_3d.h"
class PhysicsDirectBodyState3DExtension : public PhysicsDirectBodyState3D {
GDCLASS(PhysicsDirectBodyState3DExtension, PhysicsDirectBodyState3D);
protected:
static void _bind_methods();
public:
// The warning is valid, but unavoidable. If the function is not overridden it will error anyway.
EXBIND0RC(Vector3, get_total_gravity)
EXBIND0RC(real_t, get_total_angular_damp)
EXBIND0RC(real_t, get_total_linear_damp)
EXBIND0RC(Vector3, get_center_of_mass)
EXBIND0RC(Vector3, get_center_of_mass_local)
EXBIND0RC(Basis, get_principal_inertia_axes)
EXBIND0RC(real_t, get_inverse_mass)
EXBIND0RC(Vector3, get_inverse_inertia)
EXBIND0RC(Basis, get_inverse_inertia_tensor)
EXBIND1(set_linear_velocity, const Vector3 &)
EXBIND0RC(Vector3, get_linear_velocity)
EXBIND1(set_angular_velocity, const Vector3 &)
EXBIND0RC(Vector3, get_angular_velocity)
EXBIND1(set_transform, const Transform3D &)
EXBIND0RC(Transform3D, get_transform)
EXBIND1RC(Vector3, get_velocity_at_local_position, const Vector3 &)
EXBIND1(apply_central_impulse, const Vector3 &)
EXBIND2(apply_impulse, const Vector3 &, const Vector3 &)
EXBIND1(apply_torque_impulse, const Vector3 &)
EXBIND1(apply_central_force, const Vector3 &)
EXBIND2(apply_force, const Vector3 &, const Vector3 &)
EXBIND1(apply_torque, const Vector3 &)
EXBIND1(add_constant_central_force, const Vector3 &)
EXBIND2(add_constant_force, const Vector3 &, const Vector3 &)
EXBIND1(add_constant_torque, const Vector3 &)
EXBIND1(set_constant_force, const Vector3 &)
EXBIND0RC(Vector3, get_constant_force)
EXBIND1(set_constant_torque, const Vector3 &)
EXBIND0RC(Vector3, get_constant_torque)
EXBIND1(set_sleep_state, bool)
EXBIND0RC(bool, is_sleeping)
EXBIND0RC(int, get_contact_count)
EXBIND1RC(Vector3, get_contact_local_position, int)
EXBIND1RC(Vector3, get_contact_local_normal, int)
EXBIND1RC(Vector3, get_contact_impulse, int)
EXBIND1RC(int, get_contact_local_shape, int)
EXBIND1RC(Vector3, get_contact_local_velocity_at_position, int)
EXBIND1RC(RID, get_contact_collider, int)
EXBIND1RC(Vector3, get_contact_collider_position, int)
EXBIND1RC(ObjectID, get_contact_collider_id, int)
EXBIND1RC(Object *, get_contact_collider_object, int)
EXBIND1RC(int, get_contact_collider_shape, int)
EXBIND1RC(Vector3, get_contact_collider_velocity_at_position, int)
EXBIND0RC(real_t, get_step)
EXBIND0(integrate_forces)
EXBIND0R(PhysicsDirectSpaceState3D *, get_space_state)
PhysicsDirectBodyState3DExtension();
};
typedef PhysicsDirectSpaceState3D::RayResult PhysicsServer3DExtensionRayResult;
typedef PhysicsDirectSpaceState3D::ShapeResult PhysicsServer3DExtensionShapeResult;
typedef PhysicsDirectSpaceState3D::ShapeRestInfo PhysicsServer3DExtensionShapeRestInfo;
GDVIRTUAL_NATIVE_PTR(PhysicsServer3DExtensionRayResult)
GDVIRTUAL_NATIVE_PTR(PhysicsServer3DExtensionShapeResult)
GDVIRTUAL_NATIVE_PTR(PhysicsServer3DExtensionShapeRestInfo)
class PhysicsDirectSpaceState3DExtension : public PhysicsDirectSpaceState3D {
GDCLASS(PhysicsDirectSpaceState3DExtension, PhysicsDirectSpaceState3D);
thread_local static const HashSet<RID> *exclude;
protected:
static void _bind_methods();
bool is_body_excluded_from_query(const RID &p_body) const;
GDVIRTUAL9R(bool, _intersect_ray, const Vector3 &, const Vector3 &, uint32_t, bool, bool, bool, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionRayResult>)
GDVIRTUAL6R(int, _intersect_point, const Vector3 &, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
GDVIRTUAL9R(int, _intersect_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
GDVIRTUAL10R(bool, _cast_motion, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
GDVIRTUAL10R(bool, _collide_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector3>, int, GDExtensionPtr<int>)
GDVIRTUAL8R(bool, _rest_info, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
GDVIRTUAL2RC(Vector3, _get_closest_point_to_object_volume, RID, const Vector3 &)
public:
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_intersect_ray, p_parameters.from, p_parameters.to, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.hit_from_inside, p_parameters.hit_back_faces, p_parameters.pick_ray, &r_result, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = false;
GDVIRTUAL_REQUIRED_CALL(_intersect_point, p_parameters.position, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_intersect_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe, ShapeRestInfo *r_info = nullptr) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_cast_motion, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, &p_closest_safe, &p_closest_unsafe, r_info, ret);
exclude = nullptr;
return ret;
}
virtual bool collide_shape(const ShapeParameters &p_parameters, Vector3 *r_results, int p_result_max, int &r_result_count) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_collide_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, &r_result_count, ret);
exclude = nullptr;
return ret;
}
virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override {
exclude = &p_parameters.exclude;
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_rest_info, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_info, ret);
exclude = nullptr;
return ret;
}
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override {
Vector3 ret;
GDVIRTUAL_REQUIRED_CALL(_get_closest_point_to_object_volume, p_object, p_point, ret);
return ret;
}
PhysicsDirectSpaceState3DExtension();
};
typedef PhysicsServer3D::MotionCollision PhysicsServer3DExtensionMotionCollision;
typedef PhysicsServer3D::MotionResult PhysicsServer3DExtensionMotionResult;
GDVIRTUAL_NATIVE_PTR(PhysicsServer3DExtensionMotionCollision)
GDVIRTUAL_NATIVE_PTR(PhysicsServer3DExtensionMotionResult)
class PhysicsServer3DExtension : public PhysicsServer3D {
GDCLASS(PhysicsServer3DExtension, PhysicsServer3D);
protected:
static void _bind_methods();
public:
// The warning is valid, but unavoidable. If the function is not overridden it will error anyway.
/* SHAPE API */
EXBIND0R(RID, world_boundary_shape_create)
EXBIND0R(RID, separation_ray_shape_create)
EXBIND0R(RID, sphere_shape_create)
EXBIND0R(RID, box_shape_create)
EXBIND0R(RID, capsule_shape_create)
EXBIND0R(RID, cylinder_shape_create)
EXBIND0R(RID, convex_polygon_shape_create)
EXBIND0R(RID, concave_polygon_shape_create)
EXBIND0R(RID, heightmap_shape_create)
EXBIND0R(RID, custom_shape_create)
EXBIND2(shape_set_data, RID, const Variant &)
EXBIND2(shape_set_custom_solver_bias, RID, real_t)
EXBIND2(shape_set_margin, RID, real_t)
EXBIND1RC(real_t, shape_get_margin, RID)
EXBIND1RC(ShapeType, shape_get_type, RID)
EXBIND1RC(Variant, shape_get_data, RID)
EXBIND1RC(real_t, shape_get_custom_solver_bias, RID)
/* SPACE API */
EXBIND0R(RID, space_create)
EXBIND2(space_set_active, RID, bool)
EXBIND1RC(bool, space_is_active, RID)
EXBIND3(space_set_param, RID, SpaceParameter, real_t)
EXBIND2RC(real_t, space_get_param, RID, SpaceParameter)
EXBIND1R(PhysicsDirectSpaceState3D *, space_get_direct_state, RID)
EXBIND2(space_set_debug_contacts, RID, int)
EXBIND1RC(Vector<Vector3>, space_get_contacts, RID)
EXBIND1RC(int, space_get_contact_count, RID)
/* AREA API */
//EXBIND0RID(area);
EXBIND0R(RID, area_create)
EXBIND2(area_set_space, RID, RID)
EXBIND1RC(RID, area_get_space, RID)
EXBIND4(area_add_shape, RID, RID, const Transform3D &, bool)
EXBIND3(area_set_shape, RID, int, RID)
EXBIND3(area_set_shape_transform, RID, int, const Transform3D &)
EXBIND3(area_set_shape_disabled, RID, int, bool)
EXBIND1RC(int, area_get_shape_count, RID)
EXBIND2RC(RID, area_get_shape, RID, int)
EXBIND2RC(Transform3D, area_get_shape_transform, RID, int)
EXBIND2(area_remove_shape, RID, int)
EXBIND1(area_clear_shapes, RID)
EXBIND2(area_attach_object_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, area_get_object_instance_id, RID)
EXBIND3(area_set_param, RID, AreaParameter, const Variant &)
EXBIND2(area_set_transform, RID, const Transform3D &)
EXBIND2RC(Variant, area_get_param, RID, AreaParameter)
EXBIND1RC(Transform3D, area_get_transform, RID)
EXBIND2(area_set_collision_layer, RID, uint32_t)
EXBIND1RC(uint32_t, area_get_collision_layer, RID)
EXBIND2(area_set_collision_mask, RID, uint32_t)
EXBIND1RC(uint32_t, area_get_collision_mask, RID)
EXBIND2(area_set_monitorable, RID, bool)
EXBIND2(area_set_ray_pickable, RID, bool)
EXBIND2(area_set_monitor_callback, RID, const Callable &)
EXBIND2(area_set_area_monitor_callback, RID, const Callable &)
/* BODY API */
//EXBIND2RID(body,BodyMode,bool);
EXBIND0R(RID, body_create)
EXBIND2(body_set_space, RID, RID)
EXBIND1RC(RID, body_get_space, RID)
EXBIND2(body_set_mode, RID, BodyMode)
EXBIND1RC(BodyMode, body_get_mode, RID)
EXBIND4(body_add_shape, RID, RID, const Transform3D &, bool)
EXBIND3(body_set_shape, RID, int, RID)
EXBIND3(body_set_shape_transform, RID, int, const Transform3D &)
EXBIND3(body_set_shape_disabled, RID, int, bool)
EXBIND1RC(int, body_get_shape_count, RID)
EXBIND2RC(RID, body_get_shape, RID, int)
EXBIND2RC(Transform3D, body_get_shape_transform, RID, int)
EXBIND2(body_remove_shape, RID, int)
EXBIND1(body_clear_shapes, RID)
EXBIND2(body_attach_object_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, body_get_object_instance_id, RID)
EXBIND2(body_set_enable_continuous_collision_detection, RID, bool)
EXBIND1RC(bool, body_is_continuous_collision_detection_enabled, RID)
EXBIND2(body_set_collision_layer, RID, uint32_t)
EXBIND1RC(uint32_t, body_get_collision_layer, RID)
EXBIND2(body_set_collision_mask, RID, uint32_t)
EXBIND1RC(uint32_t, body_get_collision_mask, RID)
EXBIND2(body_set_collision_priority, RID, real_t)
EXBIND1RC(real_t, body_get_collision_priority, RID)
EXBIND2(body_set_user_flags, RID, uint32_t)
EXBIND1RC(uint32_t, body_get_user_flags, RID)
EXBIND3(body_set_param, RID, BodyParameter, const Variant &)
EXBIND2RC(Variant, body_get_param, RID, BodyParameter)
EXBIND1(body_reset_mass_properties, RID)
EXBIND3(body_set_state, RID, BodyState, const Variant &)
EXBIND2RC(Variant, body_get_state, RID, BodyState)
EXBIND2(body_apply_central_impulse, RID, const Vector3 &)
EXBIND3(body_apply_impulse, RID, const Vector3 &, const Vector3 &)
EXBIND2(body_apply_torque_impulse, RID, const Vector3 &)
EXBIND2(body_apply_central_force, RID, const Vector3 &)
EXBIND3(body_apply_force, RID, const Vector3 &, const Vector3 &)
EXBIND2(body_apply_torque, RID, const Vector3 &)
EXBIND2(body_add_constant_central_force, RID, const Vector3 &)
EXBIND3(body_add_constant_force, RID, const Vector3 &, const Vector3 &)
EXBIND2(body_add_constant_torque, RID, const Vector3 &)
EXBIND2(body_set_constant_force, RID, const Vector3 &)
EXBIND1RC(Vector3, body_get_constant_force, RID)
EXBIND2(body_set_constant_torque, RID, const Vector3 &)
EXBIND1RC(Vector3, body_get_constant_torque, RID)
EXBIND2(body_set_axis_velocity, RID, const Vector3 &)
EXBIND3(body_set_axis_lock, RID, BodyAxis, bool)
EXBIND2RC(bool, body_is_axis_locked, RID, BodyAxis)
EXBIND2(body_add_collision_exception, RID, RID)
EXBIND2(body_remove_collision_exception, RID, RID)
GDVIRTUAL1RC(TypedArray<RID>, _body_get_collision_exceptions, RID)
void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
GDVIRTUAL_REQUIRED_CALL(_body_get_collision_exceptions, p_body, ret);
for (int i = 0; i < ret.size(); i++) {
p_exceptions->push_back(ret[i]);
}
}
EXBIND2(body_set_max_contacts_reported, RID, int)
EXBIND1RC(int, body_get_max_contacts_reported, RID)
EXBIND2(body_set_contacts_reported_depth_threshold, RID, real_t)
EXBIND1RC(real_t, body_get_contacts_reported_depth_threshold, RID)
EXBIND2(body_set_omit_force_integration, RID, bool)
EXBIND1RC(bool, body_is_omitting_force_integration, RID)
EXBIND2(body_set_state_sync_callback, RID, const Callable &)
EXBIND3(body_set_force_integration_callback, RID, const Callable &, const Variant &)
EXBIND2(body_set_ray_pickable, RID, bool)
GDVIRTUAL8RC(bool, _body_test_motion, RID, const Transform3D &, const Vector3 &, real_t, int, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionMotionResult>)
thread_local static const HashSet<RID> *exclude_bodies;
thread_local static const HashSet<ObjectID> *exclude_objects;
bool body_test_motion_is_excluding_body(RID p_body) const;
bool body_test_motion_is_excluding_object(ObjectID p_object) const;
bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
bool ret = false;
exclude_bodies = &p_parameters.exclude_bodies;
exclude_objects = &p_parameters.exclude_objects;
GDVIRTUAL_REQUIRED_CALL(_body_test_motion, p_body, p_parameters.from, p_parameters.motion, p_parameters.margin, p_parameters.max_collisions, p_parameters.collide_separation_ray, p_parameters.recovery_as_collision, r_result, ret);
exclude_bodies = nullptr;
exclude_objects = nullptr;
return ret;
}
EXBIND1R(PhysicsDirectBodyState3D *, body_get_direct_state, RID)
/* SOFT BODY API */
EXBIND0R(RID, soft_body_create)
EXBIND2(soft_body_update_rendering_server, RID, PhysicsServer3DRenderingServerHandler *)
EXBIND2(soft_body_set_space, RID, RID)
EXBIND1RC(RID, soft_body_get_space, RID)
EXBIND2(soft_body_set_ray_pickable, RID, bool)
EXBIND2(soft_body_set_collision_layer, RID, uint32_t)
EXBIND1RC(uint32_t, soft_body_get_collision_layer, RID)
EXBIND2(soft_body_set_collision_mask, RID, uint32_t)
EXBIND1RC(uint32_t, soft_body_get_collision_mask, RID)
EXBIND2(soft_body_add_collision_exception, RID, RID)
EXBIND2(soft_body_remove_collision_exception, RID, RID)
GDVIRTUAL1RC(TypedArray<RID>, _soft_body_get_collision_exceptions, RID)
void soft_body_get_collision_exceptions(RID p_soft_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
GDVIRTUAL_REQUIRED_CALL(_soft_body_get_collision_exceptions, p_soft_body, ret);
for (int i = 0; i < ret.size(); i++) {
p_exceptions->push_back(ret[i]);
}
}
EXBIND3(soft_body_set_state, RID, BodyState, const Variant &)
EXBIND2RC(Variant, soft_body_get_state, RID, BodyState)
EXBIND2(soft_body_set_transform, RID, const Transform3D &)
EXBIND2(soft_body_set_simulation_precision, RID, int)
EXBIND1RC(int, soft_body_get_simulation_precision, RID)
EXBIND2(soft_body_set_total_mass, RID, real_t)
EXBIND1RC(real_t, soft_body_get_total_mass, RID)
EXBIND2(soft_body_set_linear_stiffness, RID, real_t)
EXBIND1RC(real_t, soft_body_get_linear_stiffness, RID)
EXBIND2(soft_body_set_pressure_coefficient, RID, real_t)
EXBIND1RC(real_t, soft_body_get_pressure_coefficient, RID)
EXBIND2(soft_body_set_damping_coefficient, RID, real_t)
EXBIND1RC(real_t, soft_body_get_damping_coefficient, RID)
EXBIND2(soft_body_set_drag_coefficient, RID, real_t)
EXBIND1RC(real_t, soft_body_get_drag_coefficient, RID)
EXBIND2(soft_body_set_mesh, RID, RID)
EXBIND1RC(AABB, soft_body_get_bounds, RID)
EXBIND3(soft_body_move_point, RID, int, const Vector3 &)
EXBIND2RC(Vector3, soft_body_get_point_global_position, RID, int)
EXBIND1(soft_body_remove_all_pinned_points, RID)
EXBIND3(soft_body_pin_point, RID, int, bool)
EXBIND2RC(bool, soft_body_is_point_pinned, RID, int)
/* JOINT API */
EXBIND0R(RID, joint_create)
EXBIND1(joint_clear, RID)
EXBIND5(joint_make_pin, RID, RID, const Vector3 &, RID, const Vector3 &)
EXBIND3(pin_joint_set_param, RID, PinJointParam, real_t)
EXBIND2RC(real_t, pin_joint_get_param, RID, PinJointParam)
EXBIND2(pin_joint_set_local_a, RID, const Vector3 &)
EXBIND1RC(Vector3, pin_joint_get_local_a, RID)
EXBIND2(pin_joint_set_local_b, RID, const Vector3 &)
EXBIND1RC(Vector3, pin_joint_get_local_b, RID)
EXBIND5(joint_make_hinge, RID, RID, const Transform3D &, RID, const Transform3D &)
EXBIND7(joint_make_hinge_simple, RID, RID, const Vector3 &, const Vector3 &, RID, const Vector3 &, const Vector3 &)
EXBIND3(hinge_joint_set_param, RID, HingeJointParam, real_t)
EXBIND2RC(real_t, hinge_joint_get_param, RID, HingeJointParam)
EXBIND3(hinge_joint_set_flag, RID, HingeJointFlag, bool)
EXBIND2RC(bool, hinge_joint_get_flag, RID, HingeJointFlag)
EXBIND5(joint_make_slider, RID, RID, const Transform3D &, RID, const Transform3D &)
EXBIND3(slider_joint_set_param, RID, SliderJointParam, real_t)
EXBIND2RC(real_t, slider_joint_get_param, RID, SliderJointParam)
EXBIND5(joint_make_cone_twist, RID, RID, const Transform3D &, RID, const Transform3D &)
EXBIND3(cone_twist_joint_set_param, RID, ConeTwistJointParam, real_t)
EXBIND2RC(real_t, cone_twist_joint_get_param, RID, ConeTwistJointParam)
EXBIND5(joint_make_generic_6dof, RID, RID, const Transform3D &, RID, const Transform3D &)
EXBIND4(generic_6dof_joint_set_param, RID, Vector3::Axis, G6DOFJointAxisParam, real_t)
EXBIND3RC(real_t, generic_6dof_joint_get_param, RID, Vector3::Axis, G6DOFJointAxisParam)
EXBIND4(generic_6dof_joint_set_flag, RID, Vector3::Axis, G6DOFJointAxisFlag, bool)
EXBIND3RC(bool, generic_6dof_joint_get_flag, RID, Vector3::Axis, G6DOFJointAxisFlag)
EXBIND1RC(JointType, joint_get_type, RID)
EXBIND2(joint_set_solver_priority, RID, int)
EXBIND1RC(int, joint_get_solver_priority, RID)
EXBIND2(joint_disable_collisions_between_bodies, RID, bool)
EXBIND1RC(bool, joint_is_disabled_collisions_between_bodies, RID)
/* MISC */
GDVIRTUAL1(_free_rid, RID)
virtual void free(RID p_rid) override {
GDVIRTUAL_REQUIRED_CALL(_free_rid, p_rid);
}
EXBIND1(set_active, bool)
EXBIND0(init)
EXBIND1(step, real_t)
EXBIND0(sync)
EXBIND0(flush_queries)
EXBIND0(end_sync)
EXBIND0(finish)
EXBIND0RC(bool, is_flushing_queries)
EXBIND1R(int, get_process_info, ProcessInfo)
PhysicsServer3DExtension();
~PhysicsServer3DExtension();
};
#endif // PHYSICS_SERVER_3D_EXTENSION_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,227 @@
/**************************************************************************/
/* movie_writer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "movie_writer.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/os/time.h"
#include "servers/display_server.h"
#include "servers/rendering_server.h"
MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
uint32_t MovieWriter::writer_count = 0;
void MovieWriter::add_writer(MovieWriter *p_writer) {
ERR_FAIL_COND(writer_count == MAX_WRITERS);
writers[writer_count++] = p_writer;
}
MovieWriter *MovieWriter::find_writer_for_file(const String &p_file) {
for (int32_t i = writer_count - 1; i >= 0; i--) { // More recent last, to have override ability.
if (writers[i]->handles_file(p_file)) {
return writers[i];
}
}
return nullptr;
}
uint32_t MovieWriter::get_audio_mix_rate() const {
uint32_t ret = 48000;
GDVIRTUAL_REQUIRED_CALL(_get_audio_mix_rate, ret);
return ret;
}
AudioServer::SpeakerMode MovieWriter::get_audio_speaker_mode() const {
AudioServer::SpeakerMode ret = AudioServer::SPEAKER_MODE_STEREO;
GDVIRTUAL_REQUIRED_CALL(_get_audio_speaker_mode, ret);
return ret;
}
Error MovieWriter::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
Error ret = ERR_UNCONFIGURED;
GDVIRTUAL_REQUIRED_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret);
return ret;
}
Error MovieWriter::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
Error ret = ERR_UNCONFIGURED;
GDVIRTUAL_REQUIRED_CALL(_write_frame, p_image, p_audio_data, ret);
return ret;
}
void MovieWriter::write_end() {
GDVIRTUAL_REQUIRED_CALL(_write_end);
}
bool MovieWriter::handles_file(const String &p_path) const {
bool ret = false;
GDVIRTUAL_REQUIRED_CALL(_handles_file, p_path, ret);
return ret;
}
void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
Vector<String> exts;
GDVIRTUAL_REQUIRED_CALL(_get_supported_extensions, exts);
for (int i = 0; i < exts.size(); i++) {
r_extensions->push_back(exts[i]);
}
}
void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
project_name = GLOBAL_GET("application/config/name");
print_line(vformat("Movie Maker mode enabled, recording movie at %d FPS...", p_fps));
// Check for available disk space and warn the user if needed.
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String path = p_base_path.get_basename();
if (path.is_relative_path()) {
path = "res://" + path;
}
dir->open(path);
if (dir->get_space_left() < 10 * Math::pow(1024.0, 3.0)) {
// Less than 10 GiB available.
WARN_PRINT(vformat("Current available space on disk is low (%s). MovieWriter will fail during movie recording if the disk runs out of available space.", String::humanize_size(dir->get_space_left())));
}
cpu_time = 0.0f;
gpu_time = 0.0f;
mix_rate = get_audio_mix_rate();
AudioDriverDummy::get_dummy_singleton()->set_mix_rate(mix_rate);
AudioDriverDummy::get_dummy_singleton()->set_speaker_mode(AudioDriver::SpeakerMode(get_audio_speaker_mode()));
fps = p_fps;
if ((mix_rate % fps) != 0) {
WARN_PRINT("MovieWriter's audio mix rate (" + itos(mix_rate) + ") can not be divided by the recording FPS (" + itos(fps) + "). Audio may go out of sync over time.");
}
audio_channels = AudioDriverDummy::get_dummy_singleton()->get_channels();
audio_mix_buffer.resize(mix_rate * audio_channels / fps);
write_begin(p_movie_size, p_fps, p_base_path);
}
void MovieWriter::_bind_methods() {
ClassDB::bind_static_method("MovieWriter", D_METHOD("add_writer", "writer"), &MovieWriter::add_writer);
GDVIRTUAL_BIND(_get_audio_mix_rate)
GDVIRTUAL_BIND(_get_audio_speaker_mode)
GDVIRTUAL_BIND(_handles_file, "path")
GDVIRTUAL_BIND(_write_begin, "movie_size", "fps", "base_path")
GDVIRTUAL_BIND(_write_frame, "frame_image", "audio_frame_block")
GDVIRTUAL_BIND(_write_end)
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/movie_writer/mix_rate", PROPERTY_HINT_RANGE, "8000,192000,1,suffix:Hz"), 48000);
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/movie_writer/speaker_mode", PROPERTY_HINT_ENUM, "Stereo,3.1,5.1,7.1"), 0);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "editor/movie_writer/mjpeg_quality", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"), 0.75);
// Used by the editor.
GLOBAL_DEF_BASIC("editor/movie_writer/movie_file", "");
GLOBAL_DEF_BASIC("editor/movie_writer/disable_vsync", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "editor/movie_writer/fps", PROPERTY_HINT_RANGE, "1,300,1,suffix:FPS"), 60);
}
void MovieWriter::set_extensions_hint() {
RBSet<String> found;
for (uint32_t i = 0; i < writer_count; i++) {
List<String> extensions;
writers[i]->get_supported_extensions(&extensions);
for (const String &ext : extensions) {
found.insert(ext);
}
}
String ext_hint;
for (const String &S : found) {
if (ext_hint != "") {
ext_hint += ",";
}
ext_hint += "*." + S;
}
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "editor/movie_writer/movie_file", PROPERTY_HINT_GLOBAL_SAVE_FILE, ext_hint));
}
void MovieWriter::add_frame() {
const int movie_time_seconds = Engine::get_singleton()->get_frames_drawn() / fps;
const String movie_time = vformat("%s:%s:%s",
String::num(movie_time_seconds / 3600).pad_zeros(2),
String::num((movie_time_seconds % 3600) / 60).pad_zeros(2),
String::num(movie_time_seconds % 60).pad_zeros(2));
#ifdef DEBUG_ENABLED
DisplayServer::get_singleton()->window_set_title(vformat("MovieWriter: Frame %d (time: %s) - %s (DEBUG)", Engine::get_singleton()->get_frames_drawn(), movie_time, project_name));
#else
DisplayServer::get_singleton()->window_set_title(vformat("MovieWriter: Frame %d (time: %s) - %s", Engine::get_singleton()->get_frames_drawn(), movie_time, project_name));
#endif
RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
RenderingServer::get_singleton()->viewport_set_measure_render_time(main_vp_rid, true);
cpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_cpu(main_vp_rid);
cpu_time += RenderingServer::get_singleton()->get_frame_setup_time_cpu();
gpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_gpu(main_vp_rid);
AudioDriverDummy::get_dummy_singleton()->mix_audio(mix_rate / fps, audio_mix_buffer.ptr());
write_frame(vp_tex, audio_mix_buffer.ptr());
}
void MovieWriter::end() {
write_end();
// Print a report with various statistics.
print_line("----------------");
String movie_path = Engine::get_singleton()->get_write_movie_path();
if (movie_path.is_relative_path()) {
// Print absolute path to make finding the file easier,
// and to make it clickable in terminal emulators that support this.
movie_path = ProjectSettings::get_singleton()->globalize_path("res://").path_join(movie_path);
}
print_line(vformat("Done recording movie at path: %s", movie_path));
const int movie_time_seconds = Engine::get_singleton()->get_frames_drawn() / fps;
const String movie_time = vformat("%s:%s:%s",
String::num(movie_time_seconds / 3600).pad_zeros(2),
String::num((movie_time_seconds % 3600) / 60).pad_zeros(2),
String::num(movie_time_seconds % 60).pad_zeros(2));
const int real_time_seconds = Time::get_singleton()->get_ticks_msec() / 1000;
const String real_time = vformat("%s:%s:%s",
String::num(real_time_seconds / 3600).pad_zeros(2),
String::num((real_time_seconds % 3600) / 60).pad_zeros(2),
String::num(real_time_seconds % 60).pad_zeros(2));
print_line(vformat("%d frames at %d FPS (movie length: %s), recorded in %s (%d%% of real-time speed).", Engine::get_singleton()->get_frames_drawn(), fps, movie_time, real_time, (float(movie_time_seconds) / real_time_seconds) * 100));
print_line(vformat("CPU time: %.2f seconds (average: %.2f ms/frame)", cpu_time / 1000, cpu_time / Engine::get_singleton()->get_frames_drawn()));
print_line(vformat("GPU time: %.2f seconds (average: %.2f ms/frame)", gpu_time / 1000, gpu_time / Engine::get_singleton()->get_frames_drawn()));
print_line("----------------");
}

View file

@ -0,0 +1,93 @@
/**************************************************************************/
/* movie_writer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MOVIE_WRITER_H
#define MOVIE_WRITER_H
#include "core/templates/local_vector.h"
#include "servers/audio/audio_driver_dummy.h"
#include "servers/audio_server.h"
class MovieWriter : public Object {
GDCLASS(MovieWriter, Object);
uint64_t fps = 0;
uint64_t mix_rate = 0;
uint32_t audio_channels = 0;
float cpu_time = 0.0f;
float gpu_time = 0.0f;
String project_name;
LocalVector<int32_t> audio_mix_buffer;
enum {
MAX_WRITERS = 8
};
static MovieWriter *writers[];
static uint32_t writer_count;
protected:
virtual uint32_t get_audio_mix_rate() const;
virtual AudioServer::SpeakerMode get_audio_speaker_mode() const;
virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data);
virtual void write_end();
GDVIRTUAL0RC(uint32_t, _get_audio_mix_rate)
GDVIRTUAL0RC(AudioServer::SpeakerMode, _get_audio_speaker_mode)
GDVIRTUAL1RC(bool, _handles_file, const String &)
GDVIRTUAL0RC(Vector<String>, _get_supported_extensions)
GDVIRTUAL3R(Error, _write_begin, const Size2i &, uint32_t, const String &)
GDVIRTUAL2R(Error, _write_frame, const Ref<Image> &, GDExtensionConstPtr<int32_t>)
GDVIRTUAL0(_write_end)
static void _bind_methods();
public:
virtual bool handles_file(const String &p_path) const;
virtual void get_supported_extensions(List<String> *r_extensions) const;
static void add_writer(MovieWriter *p_writer);
static MovieWriter *find_writer_for_file(const String &p_file);
void begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
void add_frame();
static void set_extensions_hint();
void end();
};
#endif // MOVIE_WRITER_H

View file

@ -0,0 +1,263 @@
/**************************************************************************/
/* movie_writer_mjpeg.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "movie_writer_mjpeg.h"
#include "core/config/project_settings.h"
uint32_t MovieWriterMJPEG::get_audio_mix_rate() const {
return mix_rate;
}
AudioServer::SpeakerMode MovieWriterMJPEG::get_audio_speaker_mode() const {
return speaker_mode;
}
bool MovieWriterMJPEG::handles_file(const String &p_path) const {
return p_path.get_extension().to_lower() == "avi";
}
void MovieWriterMJPEG::get_supported_extensions(List<String> *r_extensions) const {
r_extensions->push_back("avi");
}
Error MovieWriterMJPEG::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
// Quick & Dirty MJPEG Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
base_path = p_base_path.get_basename();
if (base_path.is_relative_path()) {
base_path = "res://" + base_path;
}
base_path += ".avi";
f = FileAccess::open(base_path, FileAccess::WRITE_READ);
fps = p_fps;
ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);
f->store_buffer((const uint8_t *)"RIFF", 4);
f->store_32(0); // Total length (update later)
f->store_buffer((const uint8_t *)"AVI ", 4);
f->store_buffer((const uint8_t *)"LIST", 4);
f->store_32(300); // 4 + 4 + 4 + 56 + 4 + 4 + 132 + 4 + 4 + 84
f->store_buffer((const uint8_t *)"hdrl", 4);
f->store_buffer((const uint8_t *)"avih", 4);
f->store_32(56);
f->store_32(1000000 / p_fps); // Microsecs per frame.
f->store_32(7000); // Max bytes per second
f->store_32(0); // Padding Granularity
f->store_32(16);
total_frames_ofs = f->get_position();
f->store_32(0); // Total frames (update later)
f->store_32(0); // Initial frames
f->store_32(1); // Streams
f->store_32(0); // Suggested buffer size
f->store_32(p_movie_size.width); // Movie Width
f->store_32(p_movie_size.height); // Movie Height
for (uint32_t i = 0; i < 4; i++) {
f->store_32(0); // Reserved.
}
f->store_buffer((const uint8_t *)"LIST", 4);
f->store_32(132); // 4 + 4 + 4 + 48 + 4 + 4 + 40 + 4 + 4 + 16
f->store_buffer((const uint8_t *)"strl", 4);
f->store_buffer((const uint8_t *)"strh", 4);
f->store_32(48);
f->store_buffer((const uint8_t *)"vids", 4);
f->store_buffer((const uint8_t *)"MJPG", 4);
f->store_32(0); // Flags
f->store_16(0); // Priority
f->store_16(0); // Language
f->store_32(0); // Initial Frames
f->store_32(1); // Scale
f->store_32(p_fps); // FPS
f->store_32(0); // Start
total_frames_ofs2 = f->get_position();
f->store_32(0); // Number of frames (to be updated later)
f->store_32(0); // Suggested Buffer Size
f->store_32(0); // Quality
f->store_32(0); // Sample Size
f->store_buffer((const uint8_t *)"strf", 4);
f->store_32(40); // Size.
f->store_32(40); // Size.
f->store_32(p_movie_size.width); // Width
f->store_32(p_movie_size.height); // Width
f->store_16(1); // Planes
f->store_16(24); // Bitcount
f->store_buffer((const uint8_t *)"MJPG", 4); // Compression
f->store_32(((p_movie_size.width * 24 / 8 + 3) & 0xFFFFFFFC) * p_movie_size.height); // SizeImage
f->store_32(0); // XPelsXMeter
f->store_32(0); // YPelsXMeter
f->store_32(0); // ClrUsed
f->store_32(0); // ClrImportant
f->store_buffer((const uint8_t *)"LIST", 4);
f->store_32(16);
f->store_buffer((const uint8_t *)"odml", 4);
f->store_buffer((const uint8_t *)"dmlh", 4);
f->store_32(4); // sizes
total_frames_ofs3 = f->get_position();
f->store_32(0); // Number of frames (to be updated later)
// Audio //
const uint32_t bit_depth = 32;
uint32_t channels = 2;
switch (speaker_mode) {
case AudioServer::SPEAKER_MODE_STEREO:
channels = 2;
break;
case AudioServer::SPEAKER_SURROUND_31:
channels = 4;
break;
case AudioServer::SPEAKER_SURROUND_51:
channels = 6;
break;
case AudioServer::SPEAKER_SURROUND_71:
channels = 8;
break;
}
uint32_t blockalign = bit_depth / 8 * channels;
f->store_buffer((const uint8_t *)"LIST", 4);
f->store_32(84); // 4 + 4 + 4 + 48 + 4 + 4 + 16
f->store_buffer((const uint8_t *)"strl", 4);
f->store_buffer((const uint8_t *)"strh", 4);
f->store_32(48);
f->store_buffer((const uint8_t *)"auds", 4);
f->store_32(0); // Handler
f->store_32(0); // Flags
f->store_16(0); // Priority
f->store_16(0); // Language
f->store_32(0); // Initial Frames
f->store_32(blockalign); // Scale
f->store_32(mix_rate * blockalign); // mix rate
f->store_32(0); // Start
total_audio_frames_ofs4 = f->get_position();
f->store_32(0); // Number of frames (to be updated later)
f->store_32(12288); // Suggested Buffer Size
f->store_32(0xFFFFFFFF); // Quality
f->store_32(blockalign); // Block Align to 32 bits
audio_block_size = (mix_rate / fps) * blockalign;
f->store_buffer((const uint8_t *)"strf", 4);
f->store_32(16); // Standard format, no extra fields
f->store_16(1); // Compression code, standard PCM
f->store_16(channels);
f->store_32(mix_rate); // Samples (frames) / Sec
f->store_32(mix_rate * blockalign); // Bytes / sec
f->store_16(blockalign); // Bytes / sec
f->store_16(bit_depth); // Bytes / sec
f->store_buffer((const uint8_t *)"LIST", 4);
movi_data_ofs = f->get_position();
f->store_32(0); // Number of frames (to be updated later)
f->store_buffer((const uint8_t *)"movi", 4);
return OK;
}
Error MovieWriterMJPEG::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
ERR_FAIL_COND_V(!f.is_valid(), ERR_UNCONFIGURED);
Vector<uint8_t> jpg_buffer = p_image->save_jpg_to_buffer(quality);
uint32_t s = jpg_buffer.size();
f->store_buffer((const uint8_t *)"00db", 4); // Stream 0, Video
f->store_32(jpg_buffer.size()); // sizes
f->store_buffer(jpg_buffer.ptr(), jpg_buffer.size());
if (jpg_buffer.size() & 1) {
f->store_8(0);
s++;
}
jpg_frame_sizes.push_back(s);
f->store_buffer((const uint8_t *)"01wb", 4); // Stream 1, Audio.
f->store_32(audio_block_size);
f->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
frame_count++;
return OK;
}
void MovieWriterMJPEG::write_end() {
if (f.is_valid()) {
// Finalize the file (frame indices)
f->store_buffer((const uint8_t *)"idx1", 4);
f->store_32(8 * 4 * frame_count);
uint32_t ofs = 4;
uint32_t all_data_size = 0;
for (uint32_t i = 0; i < frame_count; i++) {
f->store_buffer((const uint8_t *)"00db", 4);
f->store_32(16); // AVI_KEYFRAME
f->store_32(ofs);
f->store_32(jpg_frame_sizes[i]);
ofs += jpg_frame_sizes[i] + 8;
f->store_buffer((const uint8_t *)"01wb", 4);
f->store_32(16); // AVI_KEYFRAME
f->store_32(ofs);
f->store_32(audio_block_size);
ofs += audio_block_size + 8;
all_data_size += jpg_frame_sizes[i] + audio_block_size;
}
uint32_t file_size = f->get_position();
f->seek(4);
f->store_32(file_size - 78);
f->seek(total_frames_ofs);
f->store_32(frame_count);
f->seek(total_frames_ofs2);
f->store_32(frame_count);
f->seek(total_frames_ofs3);
f->store_32(frame_count);
f->seek(total_audio_frames_ofs4);
f->store_32(frame_count * mix_rate / fps);
f->seek(movi_data_ofs);
f->store_32(all_data_size + 4 + 16 * frame_count);
f.unref();
}
}
MovieWriterMJPEG::MovieWriterMJPEG() {
mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate");
speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
quality = GLOBAL_GET("editor/movie_writer/mjpeg_quality");
}

View file

@ -0,0 +1,73 @@
/**************************************************************************/
/* movie_writer_mjpeg.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MOVIE_WRITER_MJPEG_H
#define MOVIE_WRITER_MJPEG_H
#include "servers/movie_writer/movie_writer.h"
class MovieWriterMJPEG : public MovieWriter {
GDCLASS(MovieWriterMJPEG, MovieWriter)
uint32_t mix_rate = 48000;
AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO;
String base_path;
uint32_t frame_count = 0;
uint32_t fps = 0;
float quality = 0.75;
uint32_t audio_block_size = 0;
Vector<uint32_t> jpg_frame_sizes;
uint64_t total_frames_ofs = 0;
uint64_t total_frames_ofs2 = 0;
uint64_t total_frames_ofs3 = 0;
uint64_t total_audio_frames_ofs4 = 0;
uint64_t movi_data_ofs = 0;
Ref<FileAccess> f;
protected:
virtual uint32_t get_audio_mix_rate() const override;
virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override;
virtual void get_supported_extensions(List<String> *r_extensions) const override;
virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override;
virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override;
virtual void write_end() override;
virtual bool handles_file(const String &p_path) const override;
public:
MovieWriterMJPEG();
};
#endif // MOVIE_WRITER_MJPEG_H

View file

@ -0,0 +1,170 @@
/**************************************************************************/
/* movie_writer_pngwav.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "movie_writer_pngwav.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
uint32_t MovieWriterPNGWAV::get_audio_mix_rate() const {
return mix_rate;
}
AudioServer::SpeakerMode MovieWriterPNGWAV::get_audio_speaker_mode() const {
return speaker_mode;
}
void MovieWriterPNGWAV::get_supported_extensions(List<String> *r_extensions) const {
r_extensions->push_back("png");
}
bool MovieWriterPNGWAV::handles_file(const String &p_path) const {
return p_path.get_extension().to_lower() == "png";
}
String MovieWriterPNGWAV::zeros_str(uint32_t p_index) {
char zeros[MAX_TRAILING_ZEROS + 1];
for (uint32_t i = 0; i < MAX_TRAILING_ZEROS; i++) {
uint32_t idx = MAX_TRAILING_ZEROS - i - 1;
uint32_t digit = (p_index / uint32_t(Math::pow(double(10), double(idx)))) % 10;
zeros[i] = '0' + digit;
}
zeros[MAX_TRAILING_ZEROS] = 0;
return zeros;
}
Error MovieWriterPNGWAV::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
// Quick & Dirty PNGWAV Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
base_path = p_base_path.get_basename();
if (base_path.is_relative_path()) {
base_path = "res://" + base_path;
}
{
//Remove existing files before writing anew
uint32_t idx = 0;
Ref<DirAccess> d = DirAccess::open(base_path.get_base_dir());
ERR_FAIL_COND_V(d.is_null(), FAILED);
String file = base_path.get_file();
while (true) {
String path = file + zeros_str(idx) + ".png";
if (d->remove(path) != OK) {
break;
}
}
}
f_wav = FileAccess::open(base_path + ".wav", FileAccess::WRITE_READ);
ERR_FAIL_COND_V(f_wav.is_null(), ERR_CANT_OPEN);
fps = p_fps;
f_wav->store_buffer((const uint8_t *)"RIFF", 4);
int total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */;
f_wav->store_32(total_size); //will store final later
f_wav->store_buffer((const uint8_t *)"WAVE", 4);
/* FORMAT CHUNK */
f_wav->store_buffer((const uint8_t *)"fmt ", 4);
uint32_t channels = 2;
switch (speaker_mode) {
case AudioServer::SPEAKER_MODE_STEREO:
channels = 2;
break;
case AudioServer::SPEAKER_SURROUND_31:
channels = 4;
break;
case AudioServer::SPEAKER_SURROUND_51:
channels = 6;
break;
case AudioServer::SPEAKER_SURROUND_71:
channels = 8;
break;
}
f_wav->store_32(16); //standard format, no extra fields
f_wav->store_16(1); // compression code, standard PCM
f_wav->store_16(channels); //CHANNELS: 2
f_wav->store_32(mix_rate);
/* useless stuff the format asks for */
int bits_per_sample = 32;
int blockalign = bits_per_sample / 8 * channels;
int bytes_per_sec = mix_rate * blockalign;
audio_block_size = (mix_rate / fps) * blockalign;
f_wav->store_32(bytes_per_sec);
f_wav->store_16(blockalign); // block align (unused)
f_wav->store_16(bits_per_sample);
/* DATA CHUNK */
f_wav->store_buffer((const uint8_t *)"data", 4);
f_wav->store_32(0); //data size... wooh
wav_data_size_pos = f_wav->get_position();
return OK;
}
Error MovieWriterPNGWAV::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
ERR_FAIL_COND_V(!f_wav.is_valid(), ERR_UNCONFIGURED);
Vector<uint8_t> png_buffer = p_image->save_png_to_buffer();
Ref<FileAccess> fi = FileAccess::open(base_path + zeros_str(frame_count) + ".png", FileAccess::WRITE);
fi->store_buffer(png_buffer.ptr(), png_buffer.size());
f_wav->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
frame_count++;
return OK;
}
void MovieWriterPNGWAV::write_end() {
if (f_wav.is_valid()) {
uint32_t total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */;
uint32_t datasize = f_wav->get_position() - wav_data_size_pos;
f_wav->seek(4);
f_wav->store_32(total_size + datasize);
f_wav->seek(0x28);
f_wav->store_32(datasize);
}
}
MovieWriterPNGWAV::MovieWriterPNGWAV() {
mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate");
speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
}

View file

@ -0,0 +1,71 @@
/**************************************************************************/
/* movie_writer_pngwav.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MOVIE_WRITER_PNGWAV_H
#define MOVIE_WRITER_PNGWAV_H
#include "servers/movie_writer/movie_writer.h"
class MovieWriterPNGWAV : public MovieWriter {
GDCLASS(MovieWriterPNGWAV, MovieWriter)
enum {
MAX_TRAILING_ZEROS = 8 // more than 10 days at 60fps, no hard drive can put up with this anyway :)
};
uint32_t mix_rate = 48000;
AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO;
String base_path;
uint32_t frame_count = 0;
uint32_t fps = 0;
uint32_t audio_block_size = 0;
Ref<FileAccess> f_wav;
uint32_t wav_data_size_pos = 0;
String zeros_str(uint32_t p_index);
protected:
virtual uint32_t get_audio_mix_rate() const override;
virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override;
virtual void get_supported_extensions(List<String> *r_extensions) const override;
virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override;
virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override;
virtual void write_end() override;
virtual bool handles_file(const String &p_path) const override;
public:
MovieWriterPNGWAV();
};
#endif // MOVIE_WRITER_PNGWAV_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,186 @@
/**************************************************************************/
/* navigation_path_query_parameters_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "navigation_path_query_parameters_2d.h"
void NavigationPathQueryParameters2D::set_pathfinding_algorithm(const NavigationPathQueryParameters2D::PathfindingAlgorithm p_pathfinding_algorithm) {
switch (p_pathfinding_algorithm) {
case PATHFINDING_ALGORITHM_ASTAR: {
parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
} break;
default: {
WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default");
parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
} break;
}
}
NavigationPathQueryParameters2D::PathfindingAlgorithm NavigationPathQueryParameters2D::get_pathfinding_algorithm() const {
switch (parameters.pathfinding_algorithm) {
case NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR:
return PATHFINDING_ALGORITHM_ASTAR;
default:
WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default");
return PATHFINDING_ALGORITHM_ASTAR;
}
}
void NavigationPathQueryParameters2D::set_path_postprocessing(const NavigationPathQueryParameters2D::PathPostProcessing p_path_postprocessing) {
switch (p_path_postprocessing) {
case PATH_POSTPROCESSING_CORRIDORFUNNEL: {
parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
} break;
case PATH_POSTPROCESSING_EDGECENTERED: {
parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED;
} break;
default: {
WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default");
parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
} break;
}
}
NavigationPathQueryParameters2D::PathPostProcessing NavigationPathQueryParameters2D::get_path_postprocessing() const {
switch (parameters.path_postprocessing) {
case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL:
return PATH_POSTPROCESSING_CORRIDORFUNNEL;
case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED:
return PATH_POSTPROCESSING_EDGECENTERED;
default:
WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default");
return PATH_POSTPROCESSING_CORRIDORFUNNEL;
}
}
void NavigationPathQueryParameters2D::set_map(const RID &p_map) {
parameters.map = p_map;
}
const RID &NavigationPathQueryParameters2D::get_map() const {
return parameters.map;
}
void NavigationPathQueryParameters2D::set_start_position(const Vector2 p_start_position) {
parameters.start_position = Vector3(p_start_position.x, 0.0, p_start_position.y);
}
Vector2 NavigationPathQueryParameters2D::get_start_position() const {
return Vector2(parameters.start_position.x, parameters.start_position.z);
}
void NavigationPathQueryParameters2D::set_target_position(const Vector2 p_target_position) {
parameters.target_position = Vector3(p_target_position.x, 0.0, p_target_position.y);
}
Vector2 NavigationPathQueryParameters2D::get_target_position() const {
return Vector2(parameters.target_position.x, parameters.target_position.z);
}
void NavigationPathQueryParameters2D::set_navigation_layers(uint32_t p_navigation_layers) {
parameters.navigation_layers = p_navigation_layers;
}
uint32_t NavigationPathQueryParameters2D::get_navigation_layers() const {
return parameters.navigation_layers;
}
void NavigationPathQueryParameters2D::set_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags) {
parameters.metadata_flags = (int64_t)p_flags;
}
BitField<NavigationPathQueryParameters2D::PathMetadataFlags> NavigationPathQueryParameters2D::get_metadata_flags() const {
return (int64_t)parameters.metadata_flags;
}
void NavigationPathQueryParameters2D::set_simplify_path(bool p_enabled) {
parameters.simplify_path = p_enabled;
}
bool NavigationPathQueryParameters2D::get_simplify_path() const {
return parameters.simplify_path;
}
void NavigationPathQueryParameters2D::set_simplify_epsilon(real_t p_epsilon) {
parameters.simplify_epsilon = MAX(0.0, p_epsilon);
}
real_t NavigationPathQueryParameters2D::get_simplify_epsilon() const {
return parameters.simplify_epsilon;
}
void NavigationPathQueryParameters2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pathfinding_algorithm", "pathfinding_algorithm"), &NavigationPathQueryParameters2D::set_pathfinding_algorithm);
ClassDB::bind_method(D_METHOD("get_pathfinding_algorithm"), &NavigationPathQueryParameters2D::get_pathfinding_algorithm);
ClassDB::bind_method(D_METHOD("set_path_postprocessing", "path_postprocessing"), &NavigationPathQueryParameters2D::set_path_postprocessing);
ClassDB::bind_method(D_METHOD("get_path_postprocessing"), &NavigationPathQueryParameters2D::get_path_postprocessing);
ClassDB::bind_method(D_METHOD("set_map", "map"), &NavigationPathQueryParameters2D::set_map);
ClassDB::bind_method(D_METHOD("get_map"), &NavigationPathQueryParameters2D::get_map);
ClassDB::bind_method(D_METHOD("set_start_position", "start_position"), &NavigationPathQueryParameters2D::set_start_position);
ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationPathQueryParameters2D::get_start_position);
ClassDB::bind_method(D_METHOD("set_target_position", "target_position"), &NavigationPathQueryParameters2D::set_target_position);
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationPathQueryParameters2D::get_target_position);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPathQueryParameters2D::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPathQueryParameters2D::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_metadata_flags", "flags"), &NavigationPathQueryParameters2D::set_metadata_flags);
ClassDB::bind_method(D_METHOD("get_metadata_flags"), &NavigationPathQueryParameters2D::get_metadata_flags);
ClassDB::bind_method(D_METHOD("set_simplify_path", "enabled"), &NavigationPathQueryParameters2D::set_simplify_path);
ClassDB::bind_method(D_METHOD("get_simplify_path"), &NavigationPathQueryParameters2D::get_simplify_path);
ClassDB::bind_method(D_METHOD("set_simplify_epsilon", "epsilon"), &NavigationPathQueryParameters2D::set_simplify_epsilon);
ClassDB::bind_method(D_METHOD("get_simplify_epsilon"), &NavigationPathQueryParameters2D::get_simplify_epsilon);
ADD_PROPERTY(PropertyInfo(Variant::RID, "map"), "set_map", "get_map");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_position"), "set_start_position", "get_start_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon"), "set_simplify_epsilon", "get_simplify_epsilon");
BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_NONE);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_TYPES);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_RIDS);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_OWNERS);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_ALL);
}

View file

@ -0,0 +1,97 @@
/**************************************************************************/
/* navigation_path_query_parameters_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_PATH_QUERY_PARAMETERS_2D_H
#define NAVIGATION_PATH_QUERY_PARAMETERS_2D_H
#include "core/object/ref_counted.h"
#include "servers/navigation/navigation_utilities.h"
class NavigationPathQueryParameters2D : public RefCounted {
GDCLASS(NavigationPathQueryParameters2D, RefCounted);
NavigationUtilities::PathQueryParameters parameters;
protected:
static void _bind_methods();
public:
enum PathfindingAlgorithm {
PATHFINDING_ALGORITHM_ASTAR = 0,
};
enum PathPostProcessing {
PATH_POSTPROCESSING_CORRIDORFUNNEL = 0,
PATH_POSTPROCESSING_EDGECENTERED,
};
enum PathMetadataFlags {
PATH_METADATA_INCLUDE_NONE = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_NONE,
PATH_METADATA_INCLUDE_TYPES = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_TYPES,
PATH_METADATA_INCLUDE_RIDS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_RIDS,
PATH_METADATA_INCLUDE_OWNERS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_OWNERS,
PATH_METADATA_INCLUDE_ALL = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_ALL
};
const NavigationUtilities::PathQueryParameters &get_parameters() const { return parameters; }
void set_pathfinding_algorithm(const PathfindingAlgorithm p_pathfinding_algorithm);
PathfindingAlgorithm get_pathfinding_algorithm() const;
void set_path_postprocessing(const PathPostProcessing p_path_postprocessing);
PathPostProcessing get_path_postprocessing() const;
void set_map(const RID &p_map);
const RID &get_map() const;
void set_start_position(const Vector2 p_start_position);
Vector2 get_start_position() const;
void set_target_position(const Vector2 p_target_position);
Vector2 get_target_position() const;
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags);
BitField<NavigationPathQueryParameters2D::PathMetadataFlags> get_metadata_flags() const;
void set_simplify_path(bool p_enabled);
bool get_simplify_path() const;
void set_simplify_epsilon(real_t p_epsilon);
real_t get_simplify_epsilon() const;
};
VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathfindingAlgorithm);
VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathPostProcessing);
VARIANT_BITFIELD_CAST(NavigationPathQueryParameters2D::PathMetadataFlags);
#endif // NAVIGATION_PATH_QUERY_PARAMETERS_2D_H

View file

@ -0,0 +1,186 @@
/**************************************************************************/
/* navigation_path_query_parameters_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "navigation_path_query_parameters_3d.h"
void NavigationPathQueryParameters3D::set_pathfinding_algorithm(const NavigationPathQueryParameters3D::PathfindingAlgorithm p_pathfinding_algorithm) {
switch (p_pathfinding_algorithm) {
case PATHFINDING_ALGORITHM_ASTAR: {
parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
} break;
default: {
WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default");
parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
} break;
}
}
NavigationPathQueryParameters3D::PathfindingAlgorithm NavigationPathQueryParameters3D::get_pathfinding_algorithm() const {
switch (parameters.pathfinding_algorithm) {
case NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR:
return PATHFINDING_ALGORITHM_ASTAR;
default:
WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default");
return PATHFINDING_ALGORITHM_ASTAR;
}
}
void NavigationPathQueryParameters3D::set_path_postprocessing(const NavigationPathQueryParameters3D::PathPostProcessing p_path_postprocessing) {
switch (p_path_postprocessing) {
case PATH_POSTPROCESSING_CORRIDORFUNNEL: {
parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
} break;
case PATH_POSTPROCESSING_EDGECENTERED: {
parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED;
} break;
default: {
WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default");
parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
} break;
}
}
NavigationPathQueryParameters3D::PathPostProcessing NavigationPathQueryParameters3D::get_path_postprocessing() const {
switch (parameters.path_postprocessing) {
case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL:
return PATH_POSTPROCESSING_CORRIDORFUNNEL;
case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED:
return PATH_POSTPROCESSING_EDGECENTERED;
default:
WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default");
return PATH_POSTPROCESSING_CORRIDORFUNNEL;
}
}
void NavigationPathQueryParameters3D::set_map(const RID &p_map) {
parameters.map = p_map;
}
const RID &NavigationPathQueryParameters3D::get_map() const {
return parameters.map;
}
void NavigationPathQueryParameters3D::set_start_position(const Vector3 &p_start_position) {
parameters.start_position = p_start_position;
}
const Vector3 &NavigationPathQueryParameters3D::get_start_position() const {
return parameters.start_position;
}
void NavigationPathQueryParameters3D::set_target_position(const Vector3 &p_target_position) {
parameters.target_position = p_target_position;
}
const Vector3 &NavigationPathQueryParameters3D::get_target_position() const {
return parameters.target_position;
}
void NavigationPathQueryParameters3D::set_navigation_layers(uint32_t p_navigation_layers) {
parameters.navigation_layers = p_navigation_layers;
}
uint32_t NavigationPathQueryParameters3D::get_navigation_layers() const {
return parameters.navigation_layers;
}
void NavigationPathQueryParameters3D::set_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags) {
parameters.metadata_flags = (int64_t)p_flags;
}
BitField<NavigationPathQueryParameters3D::PathMetadataFlags> NavigationPathQueryParameters3D::get_metadata_flags() const {
return (int64_t)parameters.metadata_flags;
}
void NavigationPathQueryParameters3D::set_simplify_path(bool p_enabled) {
parameters.simplify_path = p_enabled;
}
bool NavigationPathQueryParameters3D::get_simplify_path() const {
return parameters.simplify_path;
}
void NavigationPathQueryParameters3D::set_simplify_epsilon(real_t p_epsilon) {
parameters.simplify_epsilon = MAX(0.0, p_epsilon);
}
real_t NavigationPathQueryParameters3D::get_simplify_epsilon() const {
return parameters.simplify_epsilon;
}
void NavigationPathQueryParameters3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pathfinding_algorithm", "pathfinding_algorithm"), &NavigationPathQueryParameters3D::set_pathfinding_algorithm);
ClassDB::bind_method(D_METHOD("get_pathfinding_algorithm"), &NavigationPathQueryParameters3D::get_pathfinding_algorithm);
ClassDB::bind_method(D_METHOD("set_path_postprocessing", "path_postprocessing"), &NavigationPathQueryParameters3D::set_path_postprocessing);
ClassDB::bind_method(D_METHOD("get_path_postprocessing"), &NavigationPathQueryParameters3D::get_path_postprocessing);
ClassDB::bind_method(D_METHOD("set_map", "map"), &NavigationPathQueryParameters3D::set_map);
ClassDB::bind_method(D_METHOD("get_map"), &NavigationPathQueryParameters3D::get_map);
ClassDB::bind_method(D_METHOD("set_start_position", "start_position"), &NavigationPathQueryParameters3D::set_start_position);
ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationPathQueryParameters3D::get_start_position);
ClassDB::bind_method(D_METHOD("set_target_position", "target_position"), &NavigationPathQueryParameters3D::set_target_position);
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationPathQueryParameters3D::get_target_position);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPathQueryParameters3D::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPathQueryParameters3D::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_metadata_flags", "flags"), &NavigationPathQueryParameters3D::set_metadata_flags);
ClassDB::bind_method(D_METHOD("get_metadata_flags"), &NavigationPathQueryParameters3D::get_metadata_flags);
ClassDB::bind_method(D_METHOD("set_simplify_path", "enabled"), &NavigationPathQueryParameters3D::set_simplify_path);
ClassDB::bind_method(D_METHOD("get_simplify_path"), &NavigationPathQueryParameters3D::get_simplify_path);
ClassDB::bind_method(D_METHOD("set_simplify_epsilon", "epsilon"), &NavigationPathQueryParameters3D::set_simplify_epsilon);
ClassDB::bind_method(D_METHOD("get_simplify_epsilon"), &NavigationPathQueryParameters3D::get_simplify_epsilon);
ADD_PROPERTY(PropertyInfo(Variant::RID, "map"), "set_map", "get_map");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_position"), "set_start_position", "get_start_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon"), "set_simplify_epsilon", "get_simplify_epsilon");
BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_NONE);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_TYPES);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_RIDS);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_OWNERS);
BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_ALL);
}

View file

@ -0,0 +1,97 @@
/**************************************************************************/
/* navigation_path_query_parameters_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_PATH_QUERY_PARAMETERS_3D_H
#define NAVIGATION_PATH_QUERY_PARAMETERS_3D_H
#include "core/object/ref_counted.h"
#include "servers/navigation/navigation_utilities.h"
class NavigationPathQueryParameters3D : public RefCounted {
GDCLASS(NavigationPathQueryParameters3D, RefCounted);
NavigationUtilities::PathQueryParameters parameters;
protected:
static void _bind_methods();
public:
enum PathfindingAlgorithm {
PATHFINDING_ALGORITHM_ASTAR = 0,
};
enum PathPostProcessing {
PATH_POSTPROCESSING_CORRIDORFUNNEL = 0,
PATH_POSTPROCESSING_EDGECENTERED,
};
enum PathMetadataFlags {
PATH_METADATA_INCLUDE_NONE = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_NONE,
PATH_METADATA_INCLUDE_TYPES = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_TYPES,
PATH_METADATA_INCLUDE_RIDS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_RIDS,
PATH_METADATA_INCLUDE_OWNERS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_OWNERS,
PATH_METADATA_INCLUDE_ALL = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_ALL
};
const NavigationUtilities::PathQueryParameters &get_parameters() const { return parameters; }
void set_pathfinding_algorithm(const PathfindingAlgorithm p_pathfinding_algorithm);
PathfindingAlgorithm get_pathfinding_algorithm() const;
void set_path_postprocessing(const PathPostProcessing p_path_postprocessing);
PathPostProcessing get_path_postprocessing() const;
void set_map(const RID &p_map);
const RID &get_map() const;
void set_start_position(const Vector3 &p_start_position);
const Vector3 &get_start_position() const;
void set_target_position(const Vector3 &p_target_position);
const Vector3 &get_target_position() const;
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags);
BitField<NavigationPathQueryParameters3D::PathMetadataFlags> get_metadata_flags() const;
void set_simplify_path(bool p_enabled);
bool get_simplify_path() const;
void set_simplify_epsilon(real_t p_epsilon);
real_t get_simplify_epsilon() const;
};
VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathfindingAlgorithm);
VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathPostProcessing);
VARIANT_BITFIELD_CAST(NavigationPathQueryParameters3D::PathMetadataFlags);
#endif // NAVIGATION_PATH_QUERY_PARAMETERS_3D_H

View file

@ -0,0 +1,94 @@
/**************************************************************************/
/* navigation_path_query_result_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "navigation_path_query_result_2d.h"
void NavigationPathQueryResult2D::set_path(const Vector<Vector2> &p_path) {
path = p_path;
}
const Vector<Vector2> &NavigationPathQueryResult2D::get_path() const {
return path;
}
void NavigationPathQueryResult2D::set_path_types(const Vector<int32_t> &p_path_types) {
path_types = p_path_types;
}
const Vector<int32_t> &NavigationPathQueryResult2D::get_path_types() const {
return path_types;
}
void NavigationPathQueryResult2D::set_path_rids(const TypedArray<RID> &p_path_rids) {
path_rids = p_path_rids;
}
TypedArray<RID> NavigationPathQueryResult2D::get_path_rids() const {
return path_rids;
}
void NavigationPathQueryResult2D::set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids) {
path_owner_ids = p_path_owner_ids;
}
const Vector<int64_t> &NavigationPathQueryResult2D::get_path_owner_ids() const {
return path_owner_ids;
}
void NavigationPathQueryResult2D::reset() {
path.clear();
path_types.clear();
path_rids.clear();
path_owner_ids.clear();
}
void NavigationPathQueryResult2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path", "path"), &NavigationPathQueryResult2D::set_path);
ClassDB::bind_method(D_METHOD("get_path"), &NavigationPathQueryResult2D::get_path);
ClassDB::bind_method(D_METHOD("set_path_types", "path_types"), &NavigationPathQueryResult2D::set_path_types);
ClassDB::bind_method(D_METHOD("get_path_types"), &NavigationPathQueryResult2D::get_path_types);
ClassDB::bind_method(D_METHOD("set_path_rids", "path_rids"), &NavigationPathQueryResult2D::set_path_rids);
ClassDB::bind_method(D_METHOD("get_path_rids"), &NavigationPathQueryResult2D::get_path_rids);
ClassDB::bind_method(D_METHOD("set_path_owner_ids", "path_owner_ids"), &NavigationPathQueryResult2D::set_path_owner_ids);
ClassDB::bind_method(D_METHOD("get_path_owner_ids"), &NavigationPathQueryResult2D::get_path_owner_ids);
ClassDB::bind_method(D_METHOD("reset"), &NavigationPathQueryResult2D::reset);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "path"), "set_path", "get_path");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "path_types"), "set_path_types", "get_path_types");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "path_rids", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_path_rids", "get_path_rids");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT64_ARRAY, "path_owner_ids"), "set_path_owner_ids", "get_path_owner_ids");
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_REGION);
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_LINK);
}

View file

@ -0,0 +1,71 @@
/**************************************************************************/
/* navigation_path_query_result_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_PATH_QUERY_RESULT_2D_H
#define NAVIGATION_PATH_QUERY_RESULT_2D_H
#include "core/object/ref_counted.h"
#include "servers/navigation/navigation_utilities.h"
class NavigationPathQueryResult2D : public RefCounted {
GDCLASS(NavigationPathQueryResult2D, RefCounted);
Vector<Vector2> path;
Vector<int32_t> path_types;
TypedArray<RID> path_rids;
Vector<int64_t> path_owner_ids;
protected:
static void _bind_methods();
public:
enum PathSegmentType {
PATH_SEGMENT_TYPE_REGION = 0,
PATH_SEGMENT_TYPE_LINK = 1,
};
void set_path(const Vector<Vector2> &p_path);
const Vector<Vector2> &get_path() const;
void set_path_types(const Vector<int32_t> &p_path_types);
const Vector<int32_t> &get_path_types() const;
void set_path_rids(const TypedArray<RID> &p_path_rids);
TypedArray<RID> get_path_rids() const;
void set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids);
const Vector<int64_t> &get_path_owner_ids() const;
void reset();
};
VARIANT_ENUM_CAST(NavigationPathQueryResult2D::PathSegmentType);
#endif // NAVIGATION_PATH_QUERY_RESULT_2D_H

View file

@ -0,0 +1,94 @@
/**************************************************************************/
/* navigation_path_query_result_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "navigation_path_query_result_3d.h"
void NavigationPathQueryResult3D::set_path(const Vector<Vector3> &p_path) {
path = p_path;
}
const Vector<Vector3> &NavigationPathQueryResult3D::get_path() const {
return path;
}
void NavigationPathQueryResult3D::set_path_types(const Vector<int32_t> &p_path_types) {
path_types = p_path_types;
}
const Vector<int32_t> &NavigationPathQueryResult3D::get_path_types() const {
return path_types;
}
void NavigationPathQueryResult3D::set_path_rids(const TypedArray<RID> &p_path_rids) {
path_rids = p_path_rids;
}
TypedArray<RID> NavigationPathQueryResult3D::get_path_rids() const {
return path_rids;
}
void NavigationPathQueryResult3D::set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids) {
path_owner_ids = p_path_owner_ids;
}
const Vector<int64_t> &NavigationPathQueryResult3D::get_path_owner_ids() const {
return path_owner_ids;
}
void NavigationPathQueryResult3D::reset() {
path.clear();
path_types.clear();
path_rids.clear();
path_owner_ids.clear();
}
void NavigationPathQueryResult3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path", "path"), &NavigationPathQueryResult3D::set_path);
ClassDB::bind_method(D_METHOD("get_path"), &NavigationPathQueryResult3D::get_path);
ClassDB::bind_method(D_METHOD("set_path_types", "path_types"), &NavigationPathQueryResult3D::set_path_types);
ClassDB::bind_method(D_METHOD("get_path_types"), &NavigationPathQueryResult3D::get_path_types);
ClassDB::bind_method(D_METHOD("set_path_rids", "path_rids"), &NavigationPathQueryResult3D::set_path_rids);
ClassDB::bind_method(D_METHOD("get_path_rids"), &NavigationPathQueryResult3D::get_path_rids);
ClassDB::bind_method(D_METHOD("set_path_owner_ids", "path_owner_ids"), &NavigationPathQueryResult3D::set_path_owner_ids);
ClassDB::bind_method(D_METHOD("get_path_owner_ids"), &NavigationPathQueryResult3D::get_path_owner_ids);
ClassDB::bind_method(D_METHOD("reset"), &NavigationPathQueryResult3D::reset);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "path"), "set_path", "get_path");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "path_types"), "set_path_types", "get_path_types");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "path_rids", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_path_rids", "get_path_rids");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT64_ARRAY, "path_owner_ids"), "set_path_owner_ids", "get_path_owner_ids");
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_REGION);
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_LINK);
}

View file

@ -0,0 +1,72 @@
/**************************************************************************/
/* navigation_path_query_result_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_PATH_QUERY_RESULT_3D_H
#define NAVIGATION_PATH_QUERY_RESULT_3D_H
#include "core/object/ref_counted.h"
#include "core/variant/typed_array.h"
#include "servers/navigation/navigation_utilities.h"
class NavigationPathQueryResult3D : public RefCounted {
GDCLASS(NavigationPathQueryResult3D, RefCounted);
Vector<Vector3> path;
Vector<int32_t> path_types;
TypedArray<RID> path_rids;
Vector<int64_t> path_owner_ids;
protected:
static void _bind_methods();
public:
enum PathSegmentType {
PATH_SEGMENT_TYPE_REGION = 0,
PATH_SEGMENT_TYPE_LINK = 1,
};
void set_path(const Vector<Vector3> &p_path);
const Vector<Vector3> &get_path() const;
void set_path_types(const Vector<int32_t> &p_path_types);
const Vector<int32_t> &get_path_types() const;
void set_path_rids(const TypedArray<RID> &p_path_rids);
TypedArray<RID> get_path_rids() const;
void set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids);
const Vector<int64_t> &get_path_owner_ids() const;
void reset();
};
VARIANT_ENUM_CAST(NavigationPathQueryResult3D::PathSegmentType);
#endif // NAVIGATION_PATH_QUERY_RESULT_3D_H

View file

@ -0,0 +1,82 @@
/**************************************************************************/
/* navigation_utilities.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_UTILITIES_H
#define NAVIGATION_UTILITIES_H
#include "core/math/vector3.h"
#include "core/variant/typed_array.h"
namespace NavigationUtilities {
enum PathfindingAlgorithm {
PATHFINDING_ALGORITHM_ASTAR = 0,
};
enum PathPostProcessing {
PATH_POSTPROCESSING_CORRIDORFUNNEL = 0,
PATH_POSTPROCESSING_EDGECENTERED,
};
enum PathSegmentType {
PATH_SEGMENT_TYPE_REGION = 0,
PATH_SEGMENT_TYPE_LINK
};
enum PathMetadataFlags {
PATH_INCLUDE_NONE = 0,
PATH_INCLUDE_TYPES = 1,
PATH_INCLUDE_RIDS = 2,
PATH_INCLUDE_OWNERS = 4,
PATH_INCLUDE_ALL = PATH_INCLUDE_TYPES | PATH_INCLUDE_RIDS | PATH_INCLUDE_OWNERS
};
struct PathQueryParameters {
PathfindingAlgorithm pathfinding_algorithm = PATHFINDING_ALGORITHM_ASTAR;
PathPostProcessing path_postprocessing = PATH_POSTPROCESSING_CORRIDORFUNNEL;
RID map;
Vector3 start_position;
Vector3 target_position;
uint32_t navigation_layers = 1;
BitField<PathMetadataFlags> metadata_flags = PATH_INCLUDE_ALL;
bool simplify_path = false;
real_t simplify_epsilon = 0.0;
};
struct PathQueryResult {
PackedVector3Array path;
PackedInt32Array path_types;
TypedArray<RID> path_rids;
PackedInt64Array path_owner_ids;
};
} //namespace NavigationUtilities
#endif // NAVIGATION_UTILITIES_H

View file

@ -0,0 +1,428 @@
/**************************************************************************/
/* navigation_server_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "navigation_server_2d.h"
#include "servers/navigation_server_3d.h"
NavigationServer2D *NavigationServer2D::singleton = nullptr;
void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_maps"), &NavigationServer2D::get_maps);
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer2D::map_create);
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer2D::map_set_active);
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &NavigationServer2D::map_is_active);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer2D::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer2D::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_use_edge_connections", "map", "enabled"), &NavigationServer2D::map_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_get_use_edge_connections", "map"), &NavigationServer2D::map_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer2D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer2D::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer2D::map_set_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_link_connection_radius", "map"), &NavigationServer2D::map_get_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer2D::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer2D::map_get_closest_point_owner);
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer2D::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer2D::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer2D::map_get_agents);
ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer2D::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer2D::map_force_update);
ClassDB::bind_method(D_METHOD("map_get_iteration_id", "map"), &NavigationServer2D::map_get_iteration_id);
ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer2D::map_get_random_point);
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled);
ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer2D::region_get_enabled);
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer2D::region_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_get_use_edge_connections", "region"), &NavigationServer2D::region_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer2D::region_set_enter_cost);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer2D::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer2D::region_set_travel_cost);
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer2D::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_owner_id", "region", "owner_id"), &NavigationServer2D::region_set_owner_id);
ClassDB::bind_method(D_METHOD("region_get_owner_id", "region"), &NavigationServer2D::region_get_owner_id);
ClassDB::bind_method(D_METHOD("region_owns_point", "region", "point"), &NavigationServer2D::region_owns_point);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer2D::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer2D::region_get_map);
ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers);
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_get_transform", "region"), &NavigationServer2D::region_get_transform);
ClassDB::bind_method(D_METHOD("region_set_navigation_polygon", "region", "navigation_polygon"), &NavigationServer2D::region_set_navigation_polygon);
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map);
ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer2D::link_get_map);
ClassDB::bind_method(D_METHOD("link_set_enabled", "link", "enabled"), &NavigationServer2D::link_set_enabled);
ClassDB::bind_method(D_METHOD("link_get_enabled", "link"), &NavigationServer2D::link_get_enabled);
ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer2D::link_set_bidirectional);
ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional);
ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers);
ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer2D::link_get_navigation_layers);
ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer2D::link_set_start_position);
ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer2D::link_get_start_position);
ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer2D::link_set_end_position);
ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer2D::link_get_end_position);
ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer2D::link_set_enter_cost);
ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer2D::link_get_enter_cost);
ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer2D::link_set_travel_cost);
ClassDB::bind_method(D_METHOD("link_get_travel_cost", "link"), &NavigationServer2D::link_get_travel_cost);
ClassDB::bind_method(D_METHOD("link_set_owner_id", "link", "owner_id"), &NavigationServer2D::link_set_owner_id);
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer2D::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer2D::agent_create);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer2D::agent_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer2D::agent_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer2D::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer2D::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer2D::agent_set_paused);
ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer2D::agent_get_paused);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer2D::agent_set_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_get_neighbor_distance", "agent"), &NavigationServer2D::agent_get_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer2D::agent_set_max_neighbors);
ClassDB::bind_method(D_METHOD("agent_get_max_neighbors", "agent"), &NavigationServer2D::agent_get_max_neighbors);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_agents);
ClassDB::bind_method(D_METHOD("agent_get_time_horizon_agents", "agent"), &NavigationServer2D::agent_get_time_horizon_agents);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_get_time_horizon_obstacles", "agent"), &NavigationServer2D::agent_get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer2D::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_get_radius", "agent"), &NavigationServer2D::agent_get_radius);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer2D::agent_set_max_speed);
ClassDB::bind_method(D_METHOD("agent_get_max_speed", "agent"), &NavigationServer2D::agent_get_max_speed);
ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer2D::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer2D::agent_set_velocity);
ClassDB::bind_method(D_METHOD("agent_get_velocity", "agent"), &NavigationServer2D::agent_get_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_get_position", "agent"), &NavigationServer2D::agent_get_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer2D::agent_set_avoidance_callback);
ClassDB::bind_method(D_METHOD("agent_has_avoidance_callback", "agent"), &NavigationServer2D::agent_has_avoidance_callback);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer2D::agent_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_layers", "agent"), &NavigationServer2D::agent_get_avoidance_layers);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer2D::agent_set_avoidance_mask);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_mask", "agent"), &NavigationServer2D::agent_get_avoidance_mask);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer2D::agent_set_avoidance_priority);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_priority", "agent"), &NavigationServer2D::agent_get_avoidance_priority);
ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer2D::obstacle_create);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer2D::obstacle_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_enabled", "obstacle"), &NavigationServer2D::obstacle_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer2D::obstacle_set_map);
ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer2D::obstacle_get_map);
ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer2D::obstacle_set_paused);
ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer2D::obstacle_get_paused);
ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer2D::obstacle_set_radius);
ClassDB::bind_method(D_METHOD("obstacle_get_radius", "obstacle"), &NavigationServer2D::obstacle_get_radius);
ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer2D::obstacle_set_velocity);
ClassDB::bind_method(D_METHOD("obstacle_get_velocity", "obstacle"), &NavigationServer2D::obstacle_get_velocity);
ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer2D::obstacle_set_position);
ClassDB::bind_method(D_METHOD("obstacle_get_position", "obstacle"), &NavigationServer2D::obstacle_get_position);
ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer2D::obstacle_set_vertices);
ClassDB::bind_method(D_METHOD("obstacle_get_vertices", "obstacle"), &NavigationServer2D::obstacle_get_vertices);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer2D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer2D::obstacle_get_avoidance_layers);
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_polygon", "source_geometry_data", "root_node", "callback"), &NavigationServer2D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("is_baking_navigation_polygon", "navigation_polygon"), &NavigationServer2D::is_baking_navigation_polygon);
ClassDB::bind_method(D_METHOD("source_geometry_parser_create"), &NavigationServer2D::source_geometry_parser_create);
ClassDB::bind_method(D_METHOD("source_geometry_parser_set_callback", "parser", "callback"), &NavigationServer2D::source_geometry_parser_set_callback);
ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer2D::simplify_path);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free);
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationServer2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationServer2D::get_debug_enabled);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
}
NavigationServer2D *NavigationServer2D::get_singleton() {
return singleton;
}
NavigationServer2D::NavigationServer2D() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
ERR_FAIL_NULL_MSG(NavigationServer3D::get_singleton(), "The Navigation3D singleton should be initialized before the 2D one.");
NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationServer2D::_emit_map_changed));
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationServer2D::_emit_navigation_debug_changed_signal));
#endif // DEBUG_ENABLED
}
#ifdef DEBUG_ENABLED
void NavigationServer2D::_emit_navigation_debug_changed_signal() {
emit_signal(SNAME("navigation_debug_changed"));
}
#endif // DEBUG_ENABLED
NavigationServer2D::~NavigationServer2D() {
singleton = nullptr;
}
void NavigationServer2D::_emit_map_changed(RID p_map) {
emit_signal(SNAME("map_changed"), p_map);
}
void NavigationServer2D::set_debug_enabled(bool p_enabled) {
NavigationServer3D::get_singleton()->set_debug_enabled(p_enabled);
}
bool NavigationServer2D::get_debug_enabled() const {
return NavigationServer3D::get_singleton()->get_debug_enabled();
}
#ifdef DEBUG_ENABLED
void NavigationServer2D::set_debug_navigation_enabled(bool p_enabled) {
NavigationServer3D::get_singleton()->set_debug_navigation_enabled(p_enabled);
}
bool NavigationServer2D::get_debug_navigation_enabled() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_enabled();
}
void NavigationServer2D::set_debug_avoidance_enabled(bool p_enabled) {
NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(p_enabled);
}
bool NavigationServer2D::get_debug_avoidance_enabled() const {
return NavigationServer3D::get_singleton()->get_debug_avoidance_enabled();
}
void NavigationServer2D::set_debug_navigation_edge_connection_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_edge_connection_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_edge_connection_color();
}
void NavigationServer2D::set_debug_navigation_geometry_face_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_geometry_face_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
}
void NavigationServer2D::set_debug_navigation_geometry_face_disabled_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_disabled_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_geometry_face_disabled_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_color();
}
void NavigationServer2D::set_debug_navigation_link_connection_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_link_connection_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_link_connection_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_link_connection_color();
}
void NavigationServer2D::set_debug_navigation_link_connection_disabled_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_link_connection_disabled_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_link_connection_disabled_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
}
void NavigationServer2D::set_debug_navigation_geometry_edge_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_geometry_edge_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_color();
}
void NavigationServer2D::set_debug_navigation_geometry_edge_disabled_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_disabled_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_geometry_edge_disabled_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_disabled_color();
}
void NavigationServer2D::set_debug_navigation_enable_edge_connections(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_connections(p_value);
}
bool NavigationServer2D::get_debug_navigation_enable_edge_connections() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections();
}
void NavigationServer2D::set_debug_navigation_enable_geometry_face_random_color(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_enable_geometry_face_random_color(p_value);
}
bool NavigationServer2D::get_debug_navigation_enable_geometry_face_random_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
}
void NavigationServer2D::set_debug_navigation_enable_edge_lines(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_lines(p_value);
}
bool NavigationServer2D::get_debug_navigation_enable_edge_lines() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines();
}
void NavigationServer2D::set_debug_navigation_agent_path_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_agent_path_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_agent_path_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_color();
}
void NavigationServer2D::set_debug_navigation_enable_agent_paths(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_enable_agent_paths(p_value);
}
bool NavigationServer2D::get_debug_navigation_enable_agent_paths() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_enable_agent_paths();
}
void NavigationServer2D::set_debug_navigation_agent_path_point_size(real_t p_point_size) {
NavigationServer3D::get_singleton()->set_debug_navigation_agent_path_point_size(p_point_size);
}
real_t NavigationServer2D::get_debug_navigation_agent_path_point_size() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_point_size();
}
void NavigationServer2D::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_enable_agents_radius(p_value);
}
bool NavigationServer2D::get_debug_navigation_avoidance_enable_agents_radius() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_agents_radius();
}
void NavigationServer2D::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_enable_obstacles_radius(p_value);
}
bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_radius() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius();
}
void NavigationServer2D::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_agents_radius_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_avoidance_agents_radius_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_agents_radius_color();
}
void NavigationServer2D::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_obstacles_radius_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_avoidance_obstacles_radius_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushin_face_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushout_face_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
}
void NavigationServer2D::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_enable_obstacles_static(p_value);
}
bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_static() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static();
}
#endif // DEBUG_ENABLED
///////////////////////////////////////////////////////
NavigationServer2DCallback NavigationServer2DManager::create_callback = nullptr;
void NavigationServer2DManager::set_default_server(NavigationServer2DCallback p_callback) {
create_callback = p_callback;
}
NavigationServer2D *NavigationServer2DManager::new_default_server() {
if (create_callback == nullptr) {
return nullptr;
}
return create_callback();
}

View file

@ -0,0 +1,411 @@
/**************************************************************************/
/* navigation_server_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_SERVER_2D_H
#define NAVIGATION_SERVER_2D_H
#include "core/object/class_db.h"
#include "core/templates/rid.h"
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/2d/navigation_polygon.h"
#include "servers/navigation/navigation_path_query_parameters_2d.h"
#include "servers/navigation/navigation_path_query_result_2d.h"
#ifdef CLIPPER2_ENABLED
class NavMeshGenerator2D;
#endif // CLIPPER2_ENABLED
// This server exposes the `NavigationServer3D` features in the 2D world.
class NavigationServer2D : public Object {
GDCLASS(NavigationServer2D, Object);
static NavigationServer2D *singleton;
void _emit_map_changed(RID p_map);
protected:
static void _bind_methods();
public:
/// Thread safe, can be used across many threads.
static NavigationServer2D *get_singleton();
virtual TypedArray<RID> get_maps() const = 0;
/// Create a new map.
virtual RID map_create() = 0;
/// Set map active.
virtual void map_set_active(RID p_map, bool p_active) = 0;
/// Returns true if the map is active.
virtual bool map_is_active(RID p_map) const = 0;
/// Set the map cell size used to weld the navigation mesh polygons.
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) = 0;
/// Returns the map cell size.
virtual real_t map_get_cell_size(RID p_map) const = 0;
virtual void map_set_use_edge_connections(RID p_map, bool p_enabled) = 0;
virtual bool map_get_use_edge_connections(RID p_map) const = 0;
/// Set the map edge connection margin used to weld the compatible region edges.
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
/// Returns the edge connection margin of this map.
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
/// Set the map link connection radius used to attach links to the nav mesh.
virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) = 0;
/// Returns the link connection radius of this map.
virtual real_t map_get_link_connection_radius(RID p_map) const = 0;
/// Returns the navigation path to reach the destination from the origin.
virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0;
virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const = 0;
virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const = 0;
virtual TypedArray<RID> map_get_links(RID p_map) const = 0;
virtual TypedArray<RID> map_get_regions(RID p_map) const = 0;
virtual TypedArray<RID> map_get_agents(RID p_map) const = 0;
virtual TypedArray<RID> map_get_obstacles(RID p_map) const = 0;
virtual void map_force_update(RID p_map) = 0;
virtual uint32_t map_get_iteration_id(RID p_map) const = 0;
virtual Vector2 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
/// Creates a new region.
virtual RID region_create() = 0;
virtual void region_set_enabled(RID p_region, bool p_enabled) = 0;
virtual bool region_get_enabled(RID p_region) const = 0;
virtual void region_set_use_edge_connections(RID p_region, bool p_enabled) = 0;
virtual bool region_get_use_edge_connections(RID p_region) const = 0;
/// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) = 0;
virtual real_t region_get_enter_cost(RID p_region) const = 0;
/// Set the travel_cost of a region
virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) = 0;
virtual real_t region_get_travel_cost(RID p_region) const = 0;
/// Set the node which manages this region.
virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) = 0;
virtual ObjectID region_get_owner_id(RID p_region) const = 0;
virtual bool region_owns_point(RID p_region, const Vector2 &p_point) const = 0;
/// Set the map of this region.
virtual void region_set_map(RID p_region, RID p_map) = 0;
virtual RID region_get_map(RID p_region) const = 0;
/// Set the region's layers
virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) = 0;
virtual uint32_t region_get_navigation_layers(RID p_region) const = 0;
/// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform2D p_transform) = 0;
virtual Transform2D region_get_transform(RID p_region) const = 0;
/// Set the navigation poly of this region.
virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) = 0;
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const = 0;
virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0;
virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0;
virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
/// Creates a new link between positions in the nav map.
virtual RID link_create() = 0;
/// Set the map of this link.
virtual void link_set_map(RID p_link, RID p_map) = 0;
virtual RID link_get_map(RID p_link) const = 0;
virtual void link_set_enabled(RID p_link, bool p_enabled) = 0;
virtual bool link_get_enabled(RID p_link) const = 0;
/// Set whether this link travels in both directions.
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const = 0;
/// Set the link's layers.
virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) = 0;
virtual uint32_t link_get_navigation_layers(RID p_link) const = 0;
/// Set the start position of the link.
virtual void link_set_start_position(RID p_link, Vector2 p_position) = 0;
virtual Vector2 link_get_start_position(RID p_link) const = 0;
/// Set the end position of the link.
virtual void link_set_end_position(RID p_link, Vector2 p_position) = 0;
virtual Vector2 link_get_end_position(RID p_link) const = 0;
/// Set the enter cost of the link.
virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) = 0;
virtual real_t link_get_enter_cost(RID p_link) const = 0;
/// Set the travel cost of the link.
virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) = 0;
virtual real_t link_get_travel_cost(RID p_link) const = 0;
/// Set the node which manages this link.
virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) = 0;
virtual ObjectID link_get_owner_id(RID p_link) const = 0;
/// Creates the agent.
virtual RID agent_create() = 0;
/// Put the agent in the map.
virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const = 0;
virtual void agent_set_paused(RID p_agent, bool p_paused) = 0;
virtual bool agent_get_paused(RID p_agent) const = 0;
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) = 0;
virtual bool agent_get_avoidance_enabled(RID p_agent) const = 0;
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
/// larger this number, the longer the running
/// time of the simulation. If the number is too
/// low, the simulation will not be safe.
/// Must be non-negative.
virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0;
virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0;
/// The maximum number of other agents this
/// agent takes into account in the navigation.
/// The larger this number, the longer the
/// running time of the simulation. If the
/// number is too low, the simulation will not
/// be safe.
virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
virtual int agent_get_max_neighbors(RID p_agent) const = 0;
/// The minimal amount of time for which this
/// agent's velocities that are computed by the
/// simulation are safe with respect to other
/// agents. The larger this number, the sooner
/// this agent will respond to the presence of
/// other agents, but the less freedom this
/// agent has in choosing its velocities.
/// Must be positive.
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0;
virtual real_t agent_get_time_horizon_agents(RID p_agent) const = 0;
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0;
virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const = 0;
/// The radius of this agent.
/// Must be non-negative.
virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0;
virtual real_t agent_get_radius(RID p_agent) const = 0;
/// The maximum speed of this agent.
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0;
virtual real_t agent_get_max_speed(RID p_agent) const = 0;
/// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) = 0;
/// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
/// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0;
virtual Vector2 agent_get_velocity(RID p_agent) const = 0;
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position) = 0;
virtual Vector2 agent_get_position(RID p_agent) const = 0;
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const = 0;
/// Callback called at the end of the RVO process
virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0;
virtual bool agent_has_avoidance_callback(RID p_agent) const = 0;
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0;
virtual uint32_t agent_get_avoidance_layers(RID p_agent) const = 0;
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0;
virtual uint32_t agent_get_avoidance_mask(RID p_agent) const = 0;
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0;
virtual real_t agent_get_avoidance_priority(RID p_agent) const = 0;
/// Creates the obstacle.
virtual RID obstacle_create() = 0;
virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) = 0;
virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const = 0;
virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0;
virtual RID obstacle_get_map(RID p_obstacle) const = 0;
virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) = 0;
virtual bool obstacle_get_paused(RID p_obstacle) const = 0;
virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0;
virtual real_t obstacle_get_radius(RID p_obstacle) const = 0;
virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) = 0;
virtual Vector2 obstacle_get_velocity(RID p_obstacle) const = 0;
virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) = 0;
virtual Vector2 obstacle_get_position(RID p_obstacle) const = 0;
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) = 0;
virtual Vector<Vector2> obstacle_get_vertices(RID p_obstacle) const = 0;
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0;
virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0;
/// Returns a customized navigation path using a query parameters object
virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const = 0;
virtual void init() = 0;
virtual void sync() = 0;
virtual void finish() = 0;
/// Destroy the `RID`
virtual void free(RID p_object) = 0;
virtual void parse_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual bool is_baking_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) const = 0;
virtual RID source_geometry_parser_create() = 0;
virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) = 0;
virtual Vector<Vector2> simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) = 0;
NavigationServer2D();
~NavigationServer2D() override;
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
#ifdef DEBUG_ENABLED
void set_debug_navigation_enabled(bool p_enabled);
bool get_debug_navigation_enabled() const;
void set_debug_avoidance_enabled(bool p_enabled);
bool get_debug_avoidance_enabled() const;
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
void set_debug_navigation_geometry_face_color(const Color &p_color);
Color get_debug_navigation_geometry_face_color() const;
void set_debug_navigation_geometry_face_disabled_color(const Color &p_color);
Color get_debug_navigation_geometry_face_disabled_color() const;
void set_debug_navigation_geometry_edge_color(const Color &p_color);
Color get_debug_navigation_geometry_edge_color() const;
void set_debug_navigation_geometry_edge_disabled_color(const Color &p_color);
Color get_debug_navigation_geometry_edge_disabled_color() const;
void set_debug_navigation_link_connection_color(const Color &p_color);
Color get_debug_navigation_link_connection_color() const;
void set_debug_navigation_link_connection_disabled_color(const Color &p_color);
Color get_debug_navigation_link_connection_disabled_color() const;
void set_debug_navigation_enable_edge_connections(const bool p_value);
bool get_debug_navigation_enable_edge_connections() const;
void set_debug_navigation_enable_geometry_face_random_color(const bool p_value);
bool get_debug_navigation_enable_geometry_face_random_color() const;
void set_debug_navigation_enable_edge_lines(const bool p_value);
bool get_debug_navigation_enable_edge_lines() const;
void set_debug_navigation_agent_path_color(const Color &p_color);
Color get_debug_navigation_agent_path_color() const;
void set_debug_navigation_enable_agent_paths(const bool p_value);
bool get_debug_navigation_enable_agent_paths() const;
void set_debug_navigation_agent_path_point_size(real_t p_point_size);
real_t get_debug_navigation_agent_path_point_size() const;
void set_debug_navigation_avoidance_enable_agents_radius(const bool p_value);
bool get_debug_navigation_avoidance_enable_agents_radius() const;
void set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value);
bool get_debug_navigation_avoidance_enable_obstacles_radius() const;
void set_debug_navigation_avoidance_agents_radius_color(const Color &p_color);
Color get_debug_navigation_avoidance_agents_radius_color() const;
void set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color);
Color get_debug_navigation_avoidance_obstacles_radius_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const;
void set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value);
bool get_debug_navigation_avoidance_enable_obstacles_static() const;
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
private:
void _emit_navigation_debug_changed_signal();
#endif // DEBUG_ENABLED
};
typedef NavigationServer2D *(*NavigationServer2DCallback)();
/// Manager used for the server singleton registration
class NavigationServer2DManager {
static NavigationServer2DCallback create_callback;
public:
static void set_default_server(NavigationServer2DCallback p_callback);
static NavigationServer2D *new_default_server();
};
#endif // NAVIGATION_SERVER_2D_H

View file

@ -0,0 +1,182 @@
/**************************************************************************/
/* navigation_server_2d_dummy.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_SERVER_2D_DUMMY_H
#define NAVIGATION_SERVER_2D_DUMMY_H
#include "servers/navigation_server_2d.h"
class NavigationServer2DDummy : public NavigationServer2D {
GDCLASS(NavigationServer2DDummy, NavigationServer2D);
public:
TypedArray<RID> get_maps() const override { return TypedArray<RID>(); }
RID map_create() override { return RID(); }
void map_set_active(RID p_map, bool p_active) override {}
bool map_is_active(RID p_map) const override { return false; }
void map_set_cell_size(RID p_map, real_t p_cell_size) override {}
real_t map_get_cell_size(RID p_map) const override { return 0; }
void map_set_use_edge_connections(RID p_map, bool p_enabled) override {}
bool map_get_use_edge_connections(RID p_map) const override { return false; }
void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) override {}
real_t map_get_edge_connection_margin(RID p_map) const override { return 0; }
void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override {}
real_t map_get_link_connection_radius(RID p_map) const override { return 0; }
Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override { return Vector<Vector2>(); }
Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const override { return Vector2(); }
RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const override { return RID(); }
TypedArray<RID> map_get_links(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_regions(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_obstacles(RID p_map) const override { return TypedArray<RID>(); }
void map_force_update(RID p_map) override {}
Vector2 map_get_random_point(RID p_map, uint32_t p_naviation_layers, bool p_uniformly) const override { return Vector2(); };
uint32_t map_get_iteration_id(RID p_map) const override { return 0; }
RID region_create() override { return RID(); }
void region_set_enabled(RID p_region, bool p_enabled) override {}
bool region_get_enabled(RID p_region) const override { return false; }
void region_set_use_edge_connections(RID p_region, bool p_enabled) override {}
bool region_get_use_edge_connections(RID p_region) const override { return false; }
void region_set_enter_cost(RID p_region, real_t p_enter_cost) override {}
real_t region_get_enter_cost(RID p_region) const override { return 0; }
void region_set_travel_cost(RID p_region, real_t p_travel_cost) override {}
real_t region_get_travel_cost(RID p_region) const override { return 0; }
void region_set_owner_id(RID p_region, ObjectID p_owner_id) override {}
ObjectID region_get_owner_id(RID p_region) const override { return ObjectID(); }
bool region_owns_point(RID p_region, const Vector2 &p_point) const override { return false; }
void region_set_map(RID p_region, RID p_map) override {}
RID region_get_map(RID p_region) const override { return RID(); }
void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {}
uint32_t region_get_navigation_layers(RID p_region) const override { return 0; }
void region_set_transform(RID p_region, Transform2D p_transform) override {}
Transform2D region_get_transform(RID p_region) const override { return Transform2D(); }
void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) override {}
int region_get_connections_count(RID p_region) const override { return 0; }
Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); }
Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); }
Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); };
RID link_create() override { return RID(); }
void link_set_map(RID p_link, RID p_map) override {}
RID link_get_map(RID p_link) const override { return RID(); }
void link_set_enabled(RID p_link, bool p_enabled) override {}
bool link_get_enabled(RID p_link) const override { return false; }
void link_set_bidirectional(RID p_link, bool p_bidirectional) override {}
bool link_is_bidirectional(RID p_link) const override { return false; }
void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) override {}
uint32_t link_get_navigation_layers(RID p_link) const override { return 0; }
void link_set_start_position(RID p_link, Vector2 p_position) override {}
Vector2 link_get_start_position(RID p_link) const override { return Vector2(); }
void link_set_end_position(RID p_link, Vector2 p_position) override {}
Vector2 link_get_end_position(RID p_link) const override { return Vector2(); }
void link_set_enter_cost(RID p_link, real_t p_enter_cost) override {}
real_t link_get_enter_cost(RID p_link) const override { return 0; }
void link_set_travel_cost(RID p_link, real_t p_travel_cost) override {}
real_t link_get_travel_cost(RID p_link) const override { return 0; }
void link_set_owner_id(RID p_link, ObjectID p_owner_id) override {}
ObjectID link_get_owner_id(RID p_link) const override { return ObjectID(); }
RID agent_create() override { return RID(); }
void agent_set_map(RID p_agent, RID p_map) override {}
RID agent_get_map(RID p_agent) const override { return RID(); }
void agent_set_paused(RID p_agent, bool p_paused) override {}
bool agent_get_paused(RID p_agent) const override { return false; }
void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override {}
bool agent_get_avoidance_enabled(RID p_agent) const override { return false; }
void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {}
real_t agent_get_neighbor_distance(RID p_agent) const override { return 0; }
void agent_set_max_neighbors(RID p_agent, int p_count) override {}
int agent_get_max_neighbors(RID p_agent) const override { return 0; }
void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {}
real_t agent_get_time_horizon_agents(RID p_agent) const override { return 0; }
void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {}
real_t agent_get_time_horizon_obstacles(RID p_agent) const override { return 0; }
void agent_set_radius(RID p_agent, real_t p_radius) override {}
real_t agent_get_radius(RID p_agent) const override { return 0; }
void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {}
real_t agent_get_max_speed(RID p_agent) const override { return 0; }
void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) override {}
void agent_set_velocity(RID p_agent, Vector2 p_velocity) override {}
Vector2 agent_get_velocity(RID p_agent) const override { return Vector2(); }
void agent_set_position(RID p_agent, Vector2 p_position) override {}
Vector2 agent_get_position(RID p_agent) const override { return Vector2(); }
bool agent_is_map_changed(RID p_agent) const override { return false; }
void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {}
bool agent_has_avoidance_callback(RID p_agent) const override { return false; }
void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {}
uint32_t agent_get_avoidance_layers(RID p_agent) const override { return 0; }
void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {}
uint32_t agent_get_avoidance_mask(RID p_agent) const override { return 0; }
void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {}
real_t agent_get_avoidance_priority(RID p_agent) const override { return 0; }
RID obstacle_create() override { return RID(); }
void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override {}
bool obstacle_get_avoidance_enabled(RID p_obstacle) const override { return false; }
void obstacle_set_map(RID p_obstacle, RID p_map) override {}
RID obstacle_get_map(RID p_obstacle) const override { return RID(); }
void obstacle_set_paused(RID p_obstacle, bool p_paused) override {}
bool obstacle_get_paused(RID p_obstacle) const override { return false; }
void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {}
real_t obstacle_get_radius(RID p_agent) const override { return 0; }
void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) override {}
Vector2 obstacle_get_velocity(RID p_agent) const override { return Vector2(); }
void obstacle_set_position(RID p_obstacle, Vector2 p_position) override {}
Vector2 obstacle_get_position(RID p_agent) const override { return Vector2(); }
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) override {}
Vector<Vector2> obstacle_get_vertices(RID p_agent) const override { return Vector<Vector2>(); }
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
uint32_t obstacle_get_avoidance_layers(RID p_agent) const override { return 0; }
void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const override {}
void init() override {}
void sync() override {}
void finish() override {}
void free(RID p_object) override {}
void parse_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
bool is_baking_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) const override { return false; }
RID source_geometry_parser_create() override { return RID(); }
void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override {}
Vector<Vector2> simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) override { return Vector<Vector2>(); }
void set_debug_enabled(bool p_enabled) {}
bool get_debug_enabled() const { return false; }
};
#endif // NAVIGATION_SERVER_2D_DUMMY_H

View file

@ -0,0 +1,959 @@
/**************************************************************************/
/* navigation_server_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "navigation_server_3d.h"
#include "core/config/project_settings.h"
#include "scene/main/node.h"
NavigationServer3D *NavigationServer3D::singleton = nullptr;
void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_maps"), &NavigationServer3D::get_maps);
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer3D::map_create);
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer3D::map_set_active);
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &NavigationServer3D::map_is_active);
ClassDB::bind_method(D_METHOD("map_set_up", "map", "up"), &NavigationServer3D::map_set_up);
ClassDB::bind_method(D_METHOD("map_get_up", "map"), &NavigationServer3D::map_get_up);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer3D::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer3D::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_cell_height", "map", "cell_height"), &NavigationServer3D::map_set_cell_height);
ClassDB::bind_method(D_METHOD("map_get_cell_height", "map"), &NavigationServer3D::map_get_cell_height);
ClassDB::bind_method(D_METHOD("map_set_merge_rasterizer_cell_scale", "map", "scale"), &NavigationServer3D::map_set_merge_rasterizer_cell_scale);
ClassDB::bind_method(D_METHOD("map_get_merge_rasterizer_cell_scale", "map"), &NavigationServer3D::map_get_merge_rasterizer_cell_scale);
ClassDB::bind_method(D_METHOD("map_set_use_edge_connections", "map", "enabled"), &NavigationServer3D::map_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_get_use_edge_connections", "map"), &NavigationServer3D::map_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer3D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer3D::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer3D::map_set_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_link_connection_radius", "map"), &NavigationServer3D::map_get_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer3D::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point_to_segment", "map", "start", "end", "use_collision"), &NavigationServer3D::map_get_closest_point_to_segment, DEFVAL(false));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer3D::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_normal", "map", "to_point"), &NavigationServer3D::map_get_closest_point_normal);
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer3D::map_get_closest_point_owner);
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer3D::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer3D::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer3D::map_get_agents);
ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer3D::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer3D::map_force_update);
ClassDB::bind_method(D_METHOD("map_get_iteration_id", "map"), &NavigationServer3D::map_get_iteration_id);
ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer3D::map_get_random_point);
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::query_path);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create);
ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer3D::region_set_enabled);
ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer3D::region_get_enabled);
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer3D::region_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_get_use_edge_connections", "region"), &NavigationServer3D::region_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer3D::region_set_enter_cost);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer3D::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer3D::region_set_travel_cost);
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer3D::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_owner_id", "region", "owner_id"), &NavigationServer3D::region_set_owner_id);
ClassDB::bind_method(D_METHOD("region_get_owner_id", "region"), &NavigationServer3D::region_get_owner_id);
ClassDB::bind_method(D_METHOD("region_owns_point", "region", "point"), &NavigationServer3D::region_owns_point);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer3D::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer3D::region_get_map);
ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer3D::region_set_navigation_layers);
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer3D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer3D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_get_transform", "region"), &NavigationServer3D::region_get_transform);
ClassDB::bind_method(D_METHOD("region_set_navigation_mesh", "region", "navigation_mesh"), &NavigationServer3D::region_set_navigation_mesh);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("region_bake_navigation_mesh", "navigation_mesh", "root_node"), &NavigationServer3D::region_bake_navigation_mesh);
#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map);
ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer3D::link_get_map);
ClassDB::bind_method(D_METHOD("link_set_enabled", "link", "enabled"), &NavigationServer3D::link_set_enabled);
ClassDB::bind_method(D_METHOD("link_get_enabled", "link"), &NavigationServer3D::link_get_enabled);
ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer3D::link_set_bidirectional);
ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer3D::link_is_bidirectional);
ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer3D::link_set_navigation_layers);
ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer3D::link_get_navigation_layers);
ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer3D::link_set_start_position);
ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer3D::link_get_start_position);
ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer3D::link_set_end_position);
ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer3D::link_get_end_position);
ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer3D::link_set_enter_cost);
ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer3D::link_get_enter_cost);
ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer3D::link_set_travel_cost);
ClassDB::bind_method(D_METHOD("link_get_travel_cost", "link"), &NavigationServer3D::link_get_travel_cost);
ClassDB::bind_method(D_METHOD("link_set_owner_id", "link", "owner_id"), &NavigationServer3D::link_set_owner_id);
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer3D::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer3D::agent_create);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer3D::agent_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer3D::agent_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_set_use_3d_avoidance", "agent", "enabled"), &NavigationServer3D::agent_set_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("agent_get_use_3d_avoidance", "agent"), &NavigationServer3D::agent_get_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer3D::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer3D::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer3D::agent_set_paused);
ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer3D::agent_get_paused);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer3D::agent_set_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_get_neighbor_distance", "agent"), &NavigationServer3D::agent_get_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer3D::agent_set_max_neighbors);
ClassDB::bind_method(D_METHOD("agent_get_max_neighbors", "agent"), &NavigationServer3D::agent_get_max_neighbors);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_agents);
ClassDB::bind_method(D_METHOD("agent_get_time_horizon_agents", "agent"), &NavigationServer3D::agent_get_time_horizon_agents);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_get_time_horizon_obstacles", "agent"), &NavigationServer3D::agent_get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer3D::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_get_radius", "agent"), &NavigationServer3D::agent_get_radius);
ClassDB::bind_method(D_METHOD("agent_set_height", "agent", "height"), &NavigationServer3D::agent_set_height);
ClassDB::bind_method(D_METHOD("agent_get_height", "agent"), &NavigationServer3D::agent_get_height);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer3D::agent_set_max_speed);
ClassDB::bind_method(D_METHOD("agent_get_max_speed", "agent"), &NavigationServer3D::agent_get_max_speed);
ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer3D::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer3D::agent_set_velocity);
ClassDB::bind_method(D_METHOD("agent_get_velocity", "agent"), &NavigationServer3D::agent_get_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer3D::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_get_position", "agent"), &NavigationServer3D::agent_get_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer3D::agent_is_map_changed);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer3D::agent_set_avoidance_callback);
ClassDB::bind_method(D_METHOD("agent_has_avoidance_callback", "agent"), &NavigationServer3D::agent_has_avoidance_callback);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer3D::agent_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_layers", "agent"), &NavigationServer3D::agent_get_avoidance_layers);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer3D::agent_set_avoidance_mask);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_mask", "agent"), &NavigationServer3D::agent_get_avoidance_mask);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer3D::agent_set_avoidance_priority);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_priority", "agent"), &NavigationServer3D::agent_get_avoidance_priority);
ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer3D::obstacle_create);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer3D::obstacle_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_enabled", "obstacle"), &NavigationServer3D::obstacle_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_set_use_3d_avoidance", "obstacle", "enabled"), &NavigationServer3D::obstacle_set_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("obstacle_get_use_3d_avoidance", "obstacle"), &NavigationServer3D::obstacle_get_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer3D::obstacle_set_map);
ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer3D::obstacle_get_map);
ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer3D::obstacle_set_paused);
ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer3D::obstacle_get_paused);
ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer3D::obstacle_set_radius);
ClassDB::bind_method(D_METHOD("obstacle_get_radius", "obstacle"), &NavigationServer3D::obstacle_get_radius);
ClassDB::bind_method(D_METHOD("obstacle_set_height", "obstacle", "height"), &NavigationServer3D::obstacle_set_height);
ClassDB::bind_method(D_METHOD("obstacle_get_height", "obstacle"), &NavigationServer3D::obstacle_get_height);
ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer3D::obstacle_set_velocity);
ClassDB::bind_method(D_METHOD("obstacle_get_velocity", "obstacle"), &NavigationServer3D::obstacle_get_velocity);
ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer3D::obstacle_set_position);
ClassDB::bind_method(D_METHOD("obstacle_get_position", "obstacle"), &NavigationServer3D::obstacle_get_position);
ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer3D::obstacle_set_vertices);
ClassDB::bind_method(D_METHOD("obstacle_get_vertices", "obstacle"), &NavigationServer3D::obstacle_get_vertices);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer3D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer3D::obstacle_get_avoidance_layers);
#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("is_baking_navigation_mesh", "navigation_mesh"), &NavigationServer3D::is_baking_navigation_mesh);
#endif // _3D_DISABLED
ClassDB::bind_method(D_METHOD("source_geometry_parser_create"), &NavigationServer3D::source_geometry_parser_create);
ClassDB::bind_method(D_METHOD("source_geometry_parser_set_callback", "parser", "callback"), &NavigationServer3D::source_geometry_parser_set_callback);
ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer3D::simplify_path);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
ClassDB::bind_method(D_METHOD("set_active", "active"), &NavigationServer3D::set_active);
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationServer3D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationServer3D::get_debug_enabled);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
ADD_SIGNAL(MethodInfo("avoidance_debug_changed"));
ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &NavigationServer3D::get_process_info);
BIND_ENUM_CONSTANT(INFO_ACTIVE_MAPS);
BIND_ENUM_CONSTANT(INFO_REGION_COUNT);
BIND_ENUM_CONSTANT(INFO_AGENT_COUNT);
BIND_ENUM_CONSTANT(INFO_LINK_COUNT);
BIND_ENUM_CONSTANT(INFO_POLYGON_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_MERGE_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT);
}
NavigationServer3D *NavigationServer3D::get_singleton() {
return singleton;
}
NavigationServer3D::NavigationServer3D() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_cell_size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), 1.0);
GLOBAL_DEF("navigation/2d/use_edge_connections", true);
GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1.0);
GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4.0);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/3d/default_cell_size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), 0.25);
GLOBAL_DEF_BASIC("navigation/3d/default_cell_height", 0.25);
GLOBAL_DEF("navigation/3d/default_up", Vector3(0, 1, 0));
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "navigation/3d/merge_rasterizer_cell_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001,or_greater"), 1.0);
GLOBAL_DEF("navigation/3d/use_edge_connections", true);
GLOBAL_DEF_BASIC("navigation/3d/default_edge_connection_margin", 0.25);
GLOBAL_DEF_BASIC("navigation/3d/default_link_connection_radius", 1.0);
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
GLOBAL_DEF("navigation/baking/use_crash_prevention_checks", true);
GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true);
GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true);
#ifdef DEBUG_ENABLED
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
debug_navigation_geometry_face_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_color", Color(0.5, 1.0, 1.0, 0.4));
debug_navigation_geometry_edge_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_disabled_color", Color(0.5, 0.5, 0.5, 1.0));
debug_navigation_geometry_face_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_disabled_color", Color(0.5, 0.5, 0.5, 0.4));
debug_navigation_link_connection_color = GLOBAL_DEF("debug/shapes/navigation/link_connection_color", Color(1.0, 0.5, 1.0, 1.0));
debug_navigation_link_connection_disabled_color = GLOBAL_DEF("debug/shapes/navigation/link_connection_disabled_color", Color(0.5, 0.5, 0.5, 1.0));
debug_navigation_agent_path_color = GLOBAL_DEF("debug/shapes/navigation/agent_path_color", Color(1.0, 0.0, 0.0, 1.0));
debug_navigation_enable_edge_connections = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections", true);
debug_navigation_enable_edge_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections_xray", true);
debug_navigation_enable_edge_lines = GLOBAL_DEF("debug/shapes/navigation/enable_edge_lines", true);
debug_navigation_enable_edge_lines_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_lines_xray", true);
debug_navigation_enable_geometry_face_random_color = GLOBAL_DEF("debug/shapes/navigation/enable_geometry_face_random_color", true);
debug_navigation_enable_link_connections = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections", true);
debug_navigation_enable_link_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections_xray", true);
debug_navigation_enable_agent_paths = GLOBAL_DEF("debug/shapes/navigation/enable_agent_paths", true);
debug_navigation_enable_agent_paths_xray = GLOBAL_DEF("debug/shapes/navigation/enable_agent_paths_xray", true);
debug_navigation_agent_path_point_size = GLOBAL_DEF("debug/shapes/navigation/agent_path_point_size", 4.0);
debug_navigation_avoidance_agents_radius_color = GLOBAL_DEF("debug/shapes/avoidance/agents_radius_color", Color(1.0, 1.0, 0.0, 0.25));
debug_navigation_avoidance_obstacles_radius_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_radius_color", Color(1.0, 0.5, 0.0, 0.25));
debug_navigation_avoidance_static_obstacle_pushin_face_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_face_pushin_color", Color(1.0, 0.0, 0.0, 0.0));
debug_navigation_avoidance_static_obstacle_pushin_edge_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_edge_pushin_color", Color(1.0, 0.0, 0.0, 1.0));
debug_navigation_avoidance_static_obstacle_pushout_face_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_face_pushout_color", Color(1.0, 1.0, 0.0, 0.5));
debug_navigation_avoidance_static_obstacle_pushout_edge_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_edge_pushout_color", Color(1.0, 1.0, 0.0, 1.0));
debug_navigation_avoidance_enable_agents_radius = GLOBAL_DEF("debug/shapes/avoidance/enable_agents_radius", true);
debug_navigation_avoidance_enable_obstacles_radius = GLOBAL_DEF("debug/shapes/avoidance/enable_obstacles_radius", true);
debug_navigation_avoidance_enable_obstacles_static = GLOBAL_DEF("debug/shapes/avoidance/enable_obstacles_static", true);
if (Engine::get_singleton()->is_editor_hint()) {
// enable NavigationServer3D when in Editor or else navigation mesh edge connections are invisible
// on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this
set_debug_enabled(true);
set_debug_navigation_enabled(true);
set_debug_avoidance_enabled(true);
}
#endif // DEBUG_ENABLED
}
NavigationServer3D::~NavigationServer3D() {
singleton = nullptr;
}
void NavigationServer3D::set_debug_enabled(bool p_enabled) {
#ifdef DEBUG_ENABLED
if (debug_enabled != p_enabled) {
debug_dirty = true;
}
debug_enabled = p_enabled;
if (debug_dirty) {
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_avoidance_debug_changed_signal).call_deferred();
}
#endif // DEBUG_ENABLED
}
bool NavigationServer3D::get_debug_enabled() const {
return debug_enabled;
}
#ifdef DEBUG_ENABLED
void NavigationServer3D::_emit_navigation_debug_changed_signal() {
if (navigation_debug_dirty) {
navigation_debug_dirty = false;
emit_signal(SNAME("navigation_debug_changed"));
}
}
void NavigationServer3D::_emit_avoidance_debug_changed_signal() {
if (avoidance_debug_dirty) {
avoidance_debug_dirty = false;
emit_signal(SNAME("avoidance_debug_changed"));
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_face_material() {
if (debug_navigation_geometry_face_material.is_valid()) {
return debug_navigation_geometry_face_material;
}
bool enabled_geometry_face_random_color = get_debug_navigation_enable_geometry_face_random_color();
Ref<StandardMaterial3D> face_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
face_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
face_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
face_material->set_albedo(get_debug_navigation_geometry_face_color());
face_material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
face_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_geometry_face_random_color) {
face_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
face_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
debug_navigation_geometry_face_material = face_material;
return debug_navigation_geometry_face_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_edge_material() {
if (debug_navigation_geometry_edge_material.is_valid()) {
return debug_navigation_geometry_edge_material;
}
bool enabled_edge_lines_xray = get_debug_navigation_enable_edge_lines_xray();
Ref<StandardMaterial3D> line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_material->set_albedo(get_debug_navigation_geometry_edge_color());
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_edge_lines_xray) {
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
debug_navigation_geometry_edge_material = line_material;
return debug_navigation_geometry_edge_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_face_disabled_material() {
if (debug_navigation_geometry_face_disabled_material.is_valid()) {
return debug_navigation_geometry_face_disabled_material;
}
Ref<StandardMaterial3D> face_disabled_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
face_disabled_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
face_disabled_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
face_disabled_material->set_albedo(get_debug_navigation_geometry_face_disabled_color());
face_disabled_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
debug_navigation_geometry_face_disabled_material = face_disabled_material;
return debug_navigation_geometry_face_disabled_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_edge_disabled_material() {
if (debug_navigation_geometry_edge_disabled_material.is_valid()) {
return debug_navigation_geometry_edge_disabled_material;
}
bool enabled_edge_lines_xray = get_debug_navigation_enable_edge_lines_xray();
Ref<StandardMaterial3D> line_disabled_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
line_disabled_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_disabled_material->set_albedo(get_debug_navigation_geometry_edge_disabled_color());
line_disabled_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_edge_lines_xray) {
line_disabled_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
debug_navigation_geometry_edge_disabled_material = line_disabled_material;
return debug_navigation_geometry_edge_disabled_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_edge_connections_material() {
if (debug_navigation_edge_connections_material.is_valid()) {
return debug_navigation_edge_connections_material;
}
bool enabled_edge_connections_xray = get_debug_navigation_enable_edge_connections_xray();
Ref<StandardMaterial3D> edge_connections_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
edge_connections_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
edge_connections_material->set_albedo(get_debug_navigation_edge_connection_color());
edge_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_edge_connections_xray) {
edge_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
edge_connections_material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2);
debug_navigation_edge_connections_material = edge_connections_material;
return debug_navigation_edge_connections_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connections_material() {
if (debug_navigation_link_connections_material.is_valid()) {
return debug_navigation_link_connections_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_link_connection_color);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_link_connections_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2);
debug_navigation_link_connections_material = material;
return debug_navigation_link_connections_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connections_disabled_material() {
if (debug_navigation_link_connections_disabled_material.is_valid()) {
return debug_navigation_link_connections_disabled_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_link_connection_disabled_color);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_link_connections_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2);
debug_navigation_link_connections_disabled_material = material;
return debug_navigation_link_connections_disabled_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_line_material() {
if (debug_navigation_agent_path_line_material.is_valid()) {
return debug_navigation_agent_path_line_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_agent_path_color);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_agent_paths_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2);
debug_navigation_agent_path_line_material = material;
return debug_navigation_agent_path_line_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_point_material() {
if (debug_navigation_agent_path_point_material.is_valid()) {
return debug_navigation_agent_path_point_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_albedo(debug_navigation_agent_path_color);
material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
material->set_point_size(debug_navigation_agent_path_point_size);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_agent_paths_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2);
debug_navigation_agent_path_point_material = material;
return debug_navigation_agent_path_point_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_agents_radius_material() {
if (debug_navigation_avoidance_agents_radius_material.is_valid()) {
return debug_navigation_avoidance_agents_radius_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_agents_radius_color);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
debug_navigation_avoidance_agents_radius_material = material;
return debug_navigation_avoidance_agents_radius_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_obstacles_radius_material() {
if (debug_navigation_avoidance_obstacles_radius_material.is_valid()) {
return debug_navigation_avoidance_obstacles_radius_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_obstacles_radius_color);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
debug_navigation_avoidance_obstacles_radius_material = material;
return debug_navigation_avoidance_obstacles_radius_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_face_material() {
if (debug_navigation_avoidance_static_obstacle_pushin_face_material.is_valid()) {
return debug_navigation_avoidance_static_obstacle_pushin_face_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_face_color);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
debug_navigation_avoidance_static_obstacle_pushin_face_material = material;
return debug_navigation_avoidance_static_obstacle_pushin_face_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_face_material() {
if (debug_navigation_avoidance_static_obstacle_pushout_face_material.is_valid()) {
return debug_navigation_avoidance_static_obstacle_pushout_face_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_face_color);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
debug_navigation_avoidance_static_obstacle_pushout_face_material = material;
return debug_navigation_avoidance_static_obstacle_pushout_face_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_material() {
if (debug_navigation_avoidance_static_obstacle_pushin_edge_material.is_valid()) {
return debug_navigation_avoidance_static_obstacle_pushin_edge_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
//material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
//material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_edge_color);
//material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
debug_navigation_avoidance_static_obstacle_pushin_edge_material = material;
return debug_navigation_avoidance_static_obstacle_pushin_edge_material;
}
Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_material() {
if (debug_navigation_avoidance_static_obstacle_pushout_edge_material.is_valid()) {
return debug_navigation_avoidance_static_obstacle_pushout_edge_material;
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
///material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
//material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_edge_color);
//material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
debug_navigation_avoidance_static_obstacle_pushout_edge_material = material;
return debug_navigation_avoidance_static_obstacle_pushout_edge_material;
}
void NavigationServer3D::set_debug_navigation_edge_connection_color(const Color &p_color) {
debug_navigation_edge_connection_color = p_color;
if (debug_navigation_edge_connections_material.is_valid()) {
debug_navigation_edge_connections_material->set_albedo(debug_navigation_edge_connection_color);
}
}
Color NavigationServer3D::get_debug_navigation_edge_connection_color() const {
return debug_navigation_edge_connection_color;
}
void NavigationServer3D::set_debug_navigation_geometry_edge_color(const Color &p_color) {
debug_navigation_geometry_edge_color = p_color;
if (debug_navigation_geometry_edge_material.is_valid()) {
debug_navigation_geometry_edge_material->set_albedo(debug_navigation_geometry_edge_color);
}
}
Color NavigationServer3D::get_debug_navigation_geometry_edge_color() const {
return debug_navigation_geometry_edge_color;
}
void NavigationServer3D::set_debug_navigation_geometry_face_color(const Color &p_color) {
debug_navigation_geometry_face_color = p_color;
if (debug_navigation_geometry_face_material.is_valid()) {
debug_navigation_geometry_face_material->set_albedo(debug_navigation_geometry_face_color);
}
}
Color NavigationServer3D::get_debug_navigation_geometry_face_color() const {
return debug_navigation_geometry_face_color;
}
void NavigationServer3D::set_debug_navigation_geometry_edge_disabled_color(const Color &p_color) {
debug_navigation_geometry_edge_disabled_color = p_color;
if (debug_navigation_geometry_edge_disabled_material.is_valid()) {
debug_navigation_geometry_edge_disabled_material->set_albedo(debug_navigation_geometry_edge_disabled_color);
}
}
Color NavigationServer3D::get_debug_navigation_geometry_edge_disabled_color() const {
return debug_navigation_geometry_edge_disabled_color;
}
void NavigationServer3D::set_debug_navigation_geometry_face_disabled_color(const Color &p_color) {
debug_navigation_geometry_face_disabled_color = p_color;
if (debug_navigation_geometry_face_disabled_material.is_valid()) {
debug_navigation_geometry_face_disabled_material->set_albedo(debug_navigation_geometry_face_disabled_color);
}
}
Color NavigationServer3D::get_debug_navigation_geometry_face_disabled_color() const {
return debug_navigation_geometry_face_disabled_color;
}
void NavigationServer3D::set_debug_navigation_link_connection_color(const Color &p_color) {
debug_navigation_link_connection_color = p_color;
if (debug_navigation_link_connections_material.is_valid()) {
debug_navigation_link_connections_material->set_albedo(debug_navigation_link_connection_color);
}
}
Color NavigationServer3D::get_debug_navigation_link_connection_color() const {
return debug_navigation_link_connection_color;
}
void NavigationServer3D::set_debug_navigation_link_connection_disabled_color(const Color &p_color) {
debug_navigation_link_connection_disabled_color = p_color;
if (debug_navigation_link_connections_disabled_material.is_valid()) {
debug_navigation_link_connections_disabled_material->set_albedo(debug_navigation_link_connection_disabled_color);
}
}
Color NavigationServer3D::get_debug_navigation_link_connection_disabled_color() const {
return debug_navigation_link_connection_disabled_color;
}
void NavigationServer3D::set_debug_navigation_agent_path_point_size(real_t p_point_size) {
debug_navigation_agent_path_point_size = MAX(0.1, p_point_size);
if (debug_navigation_agent_path_point_material.is_valid()) {
debug_navigation_agent_path_point_material->set_point_size(debug_navigation_agent_path_point_size);
}
}
real_t NavigationServer3D::get_debug_navigation_agent_path_point_size() const {
return debug_navigation_agent_path_point_size;
}
void NavigationServer3D::set_debug_navigation_agent_path_color(const Color &p_color) {
debug_navigation_agent_path_color = p_color;
if (debug_navigation_agent_path_line_material.is_valid()) {
debug_navigation_agent_path_line_material->set_albedo(debug_navigation_agent_path_color);
}
if (debug_navigation_agent_path_point_material.is_valid()) {
debug_navigation_agent_path_point_material->set_albedo(debug_navigation_agent_path_color);
}
}
Color NavigationServer3D::get_debug_navigation_agent_path_color() const {
return debug_navigation_agent_path_color;
}
void NavigationServer3D::set_debug_navigation_enable_edge_connections(const bool p_value) {
debug_navigation_enable_edge_connections = p_value;
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_enable_edge_connections() const {
return debug_navigation_enable_edge_connections;
}
void NavigationServer3D::set_debug_navigation_enable_edge_connections_xray(const bool p_value) {
debug_navigation_enable_edge_connections_xray = p_value;
if (debug_navigation_edge_connections_material.is_valid()) {
debug_navigation_edge_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_edge_connections_xray);
}
}
bool NavigationServer3D::get_debug_navigation_enable_edge_connections_xray() const {
return debug_navigation_enable_edge_connections_xray;
}
void NavigationServer3D::set_debug_navigation_enable_edge_lines(const bool p_value) {
debug_navigation_enable_edge_lines = p_value;
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_enable_edge_lines() const {
return debug_navigation_enable_edge_lines;
}
void NavigationServer3D::set_debug_navigation_enable_edge_lines_xray(const bool p_value) {
debug_navigation_enable_edge_lines_xray = p_value;
if (debug_navigation_geometry_edge_material.is_valid()) {
debug_navigation_geometry_edge_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_edge_lines_xray);
}
}
bool NavigationServer3D::get_debug_navigation_enable_edge_lines_xray() const {
return debug_navigation_enable_edge_lines_xray;
}
void NavigationServer3D::set_debug_navigation_enable_geometry_face_random_color(const bool p_value) {
debug_navigation_enable_geometry_face_random_color = p_value;
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_enable_geometry_face_random_color() const {
return debug_navigation_enable_geometry_face_random_color;
}
void NavigationServer3D::set_debug_navigation_enable_link_connections(const bool p_value) {
debug_navigation_enable_link_connections = p_value;
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_enable_link_connections() const {
return debug_navigation_enable_link_connections;
}
void NavigationServer3D::set_debug_navigation_enable_link_connections_xray(const bool p_value) {
debug_navigation_enable_link_connections_xray = p_value;
if (debug_navigation_link_connections_material.is_valid()) {
debug_navigation_link_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_link_connections_xray);
}
}
bool NavigationServer3D::get_debug_navigation_enable_link_connections_xray() const {
return debug_navigation_enable_link_connections_xray;
}
void NavigationServer3D::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
debug_navigation_avoidance_enable_agents_radius = p_value;
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_avoidance_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_avoidance_enable_agents_radius() const {
return debug_navigation_avoidance_enable_agents_radius;
}
void NavigationServer3D::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
debug_navigation_avoidance_enable_obstacles_radius = p_value;
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_avoidance_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_avoidance_enable_obstacles_radius() const {
return debug_navigation_avoidance_enable_obstacles_radius;
}
void NavigationServer3D::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
debug_navigation_avoidance_enable_obstacles_static = p_value;
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_avoidance_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_avoidance_enable_obstacles_static() const {
return debug_navigation_avoidance_enable_obstacles_static;
}
void NavigationServer3D::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
debug_navigation_avoidance_agents_radius_color = p_color;
if (debug_navigation_avoidance_agents_radius_material.is_valid()) {
debug_navigation_avoidance_agents_radius_material->set_albedo(debug_navigation_avoidance_agents_radius_color);
}
}
Color NavigationServer3D::get_debug_navigation_avoidance_agents_radius_color() const {
return debug_navigation_avoidance_agents_radius_color;
}
void NavigationServer3D::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
debug_navigation_avoidance_obstacles_radius_color = p_color;
if (debug_navigation_avoidance_obstacles_radius_material.is_valid()) {
debug_navigation_avoidance_obstacles_radius_material->set_albedo(debug_navigation_avoidance_obstacles_radius_color);
}
}
Color NavigationServer3D::get_debug_navigation_avoidance_obstacles_radius_color() const {
return debug_navigation_avoidance_obstacles_radius_color;
}
void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushin_face_color = p_color;
if (debug_navigation_avoidance_static_obstacle_pushin_face_material.is_valid()) {
debug_navigation_avoidance_static_obstacle_pushin_face_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_face_color);
}
}
Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
return debug_navigation_avoidance_static_obstacle_pushin_face_color;
}
void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushout_face_color = p_color;
if (debug_navigation_avoidance_static_obstacle_pushout_face_material.is_valid()) {
debug_navigation_avoidance_static_obstacle_pushout_face_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_face_color);
}
}
Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
return debug_navigation_avoidance_static_obstacle_pushout_face_color;
}
void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushin_edge_color = p_color;
if (debug_navigation_avoidance_static_obstacle_pushin_edge_material.is_valid()) {
debug_navigation_avoidance_static_obstacle_pushin_edge_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_edge_color);
}
}
Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
return debug_navigation_avoidance_static_obstacle_pushin_edge_color;
}
void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushout_edge_color = p_color;
if (debug_navigation_avoidance_static_obstacle_pushout_edge_material.is_valid()) {
debug_navigation_avoidance_static_obstacle_pushout_edge_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_edge_color);
}
}
Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
return debug_navigation_avoidance_static_obstacle_pushout_edge_color;
}
void NavigationServer3D::set_debug_navigation_enable_agent_paths(const bool p_value) {
if (debug_navigation_enable_agent_paths != p_value) {
debug_dirty = true;
}
debug_navigation_enable_agent_paths = p_value;
if (debug_dirty) {
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
}
}
bool NavigationServer3D::get_debug_navigation_enable_agent_paths() const {
return debug_navigation_enable_agent_paths;
}
void NavigationServer3D::set_debug_navigation_enable_agent_paths_xray(const bool p_value) {
debug_navigation_enable_agent_paths_xray = p_value;
if (debug_navigation_agent_path_line_material.is_valid()) {
debug_navigation_agent_path_line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_agent_paths_xray);
}
if (debug_navigation_agent_path_point_material.is_valid()) {
debug_navigation_agent_path_point_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_agent_paths_xray);
}
}
bool NavigationServer3D::get_debug_navigation_enable_agent_paths_xray() const {
return debug_navigation_enable_agent_paths_xray;
}
void NavigationServer3D::set_debug_navigation_enabled(bool p_enabled) {
debug_navigation_enabled = p_enabled;
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_navigation_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_navigation_enabled() const {
return debug_navigation_enabled;
}
void NavigationServer3D::set_debug_avoidance_enabled(bool p_enabled) {
debug_avoidance_enabled = p_enabled;
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer3D::_emit_avoidance_debug_changed_signal).call_deferred();
}
bool NavigationServer3D::get_debug_avoidance_enabled() const {
return debug_avoidance_enabled;
}
#endif // DEBUG_ENABLED
void NavigationServer3D::query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const {
ERR_FAIL_COND(!p_query_parameters.is_valid());
ERR_FAIL_COND(!p_query_result.is_valid());
const NavigationUtilities::PathQueryResult _query_result = _query_path(p_query_parameters->get_parameters());
p_query_result->set_path(_query_result.path);
p_query_result->set_path_types(_query_result.path_types);
p_query_result->set_path_rids(_query_result.path_rids);
p_query_result->set_path_owner_ids(_query_result.path_owner_ids);
}
///////////////////////////////////////////////////////
NavigationServer3DCallback NavigationServer3DManager::create_callback = nullptr;
void NavigationServer3DManager::set_default_server(NavigationServer3DCallback p_callback) {
create_callback = p_callback;
}
NavigationServer3D *NavigationServer3DManager::new_default_server() {
if (create_callback == nullptr) {
return nullptr;
}
return create_callback();
}

View file

@ -0,0 +1,566 @@
/**************************************************************************/
/* navigation_server_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_SERVER_3D_H
#define NAVIGATION_SERVER_3D_H
#include "core/object/class_db.h"
#include "core/templates/rid.h"
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_mesh.h"
#include "servers/navigation/navigation_path_query_parameters_3d.h"
#include "servers/navigation/navigation_path_query_result_3d.h"
/// This server uses the concept of internal mutability.
/// All the constant functions can be called in multithread because internally
/// the server takes care to schedule the functions access.
///
/// Note: All the `set` functions are commands executed during the `sync` phase,
/// don't expect that a change is immediately propagated.
class NavigationServer3D : public Object {
GDCLASS(NavigationServer3D, Object);
static NavigationServer3D *singleton;
protected:
static void _bind_methods();
public:
/// Thread safe, can be used across many threads.
static NavigationServer3D *get_singleton();
virtual TypedArray<RID> get_maps() const = 0;
/// Create a new map.
virtual RID map_create() = 0;
/// Set map active.
virtual void map_set_active(RID p_map, bool p_active) = 0;
/// Returns true if the map is active.
virtual bool map_is_active(RID p_map) const = 0;
/// Set the map UP direction.
virtual void map_set_up(RID p_map, Vector3 p_up) = 0;
/// Returns the map UP direction.
virtual Vector3 map_get_up(RID p_map) const = 0;
/// Set the map cell size used to weld the navigation mesh polygons.
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) = 0;
/// Returns the map cell size.
virtual real_t map_get_cell_size(RID p_map) const = 0;
virtual void map_set_cell_height(RID p_map, real_t p_height) = 0;
virtual real_t map_get_cell_height(RID p_map) const = 0;
virtual void map_set_merge_rasterizer_cell_scale(RID p_map, float p_value) = 0;
virtual float map_get_merge_rasterizer_cell_scale(RID p_map) const = 0;
virtual void map_set_use_edge_connections(RID p_map, bool p_enabled) = 0;
virtual bool map_get_use_edge_connections(RID p_map) const = 0;
/// Set the map edge connection margin used to weld the compatible region edges.
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
/// Returns the edge connection margin of this map.
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
/// Set the map link connection radius used to attach links to the nav mesh.
virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) = 0;
/// Returns the link connection radius of this map.
virtual real_t map_get_link_connection_radius(RID p_map) const = 0;
/// Returns the navigation path to reach the destination from the origin.
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0;
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const = 0;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const = 0;
virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const = 0;
virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const = 0;
virtual TypedArray<RID> map_get_links(RID p_map) const = 0;
virtual TypedArray<RID> map_get_regions(RID p_map) const = 0;
virtual TypedArray<RID> map_get_agents(RID p_map) const = 0;
virtual TypedArray<RID> map_get_obstacles(RID p_map) const = 0;
virtual void map_force_update(RID p_map) = 0;
virtual uint32_t map_get_iteration_id(RID p_map) const = 0;
virtual Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
/// Creates a new region.
virtual RID region_create() = 0;
virtual void region_set_enabled(RID p_region, bool p_enabled) = 0;
virtual bool region_get_enabled(RID p_region) const = 0;
virtual void region_set_use_edge_connections(RID p_region, bool p_enabled) = 0;
virtual bool region_get_use_edge_connections(RID p_region) const = 0;
/// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) = 0;
virtual real_t region_get_enter_cost(RID p_region) const = 0;
/// Set the travel_cost of a region
virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) = 0;
virtual real_t region_get_travel_cost(RID p_region) const = 0;
/// Set the node which manages this region.
virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) = 0;
virtual ObjectID region_get_owner_id(RID p_region) const = 0;
virtual bool region_owns_point(RID p_region, const Vector3 &p_point) const = 0;
/// Set the map of this region.
virtual void region_set_map(RID p_region, RID p_map) = 0;
virtual RID region_get_map(RID p_region) const = 0;
/// Set the region's layers
virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) = 0;
virtual uint32_t region_get_navigation_layers(RID p_region) const = 0;
/// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform3D p_transform) = 0;
virtual Transform3D region_get_transform(RID p_region) const = 0;
/// Set the navigation mesh of this region.
virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) = 0;
#ifndef DISABLE_DEPRECATED
/// Bake the navigation mesh.
virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) = 0;
#endif // DISABLE_DEPRECATED
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const = 0;
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0;
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0;
virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
/// Creates a new link between positions in the nav map.
virtual RID link_create() = 0;
/// Set the map of this link.
virtual void link_set_map(RID p_link, RID p_map) = 0;
virtual RID link_get_map(RID p_link) const = 0;
virtual void link_set_enabled(RID p_link, bool p_enabled) = 0;
virtual bool link_get_enabled(RID p_link) const = 0;
/// Set whether this link travels in both directions.
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const = 0;
/// Set the link's layers.
virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) = 0;
virtual uint32_t link_get_navigation_layers(RID p_link) const = 0;
/// Set the start position of the link.
virtual void link_set_start_position(RID p_link, Vector3 p_position) = 0;
virtual Vector3 link_get_start_position(RID p_link) const = 0;
/// Set the end position of the link.
virtual void link_set_end_position(RID p_link, Vector3 p_position) = 0;
virtual Vector3 link_get_end_position(RID p_link) const = 0;
/// Set the enter cost of the link.
virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) = 0;
virtual real_t link_get_enter_cost(RID p_link) const = 0;
/// Set the travel cost of the link.
virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) = 0;
virtual real_t link_get_travel_cost(RID p_link) const = 0;
/// Set the node which manages this link.
virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) = 0;
virtual ObjectID link_get_owner_id(RID p_link) const = 0;
/// Creates the agent.
virtual RID agent_create() = 0;
/// Put the agent in the map.
virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const = 0;
virtual void agent_set_paused(RID p_agent, bool p_paused) = 0;
virtual bool agent_get_paused(RID p_agent) const = 0;
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) = 0;
virtual bool agent_get_avoidance_enabled(RID p_agent) const = 0;
virtual void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) = 0;
virtual bool agent_get_use_3d_avoidance(RID p_agent) const = 0;
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
/// larger this number, the longer the running
/// time of the simulation. If the number is too
/// low, the simulation will not be safe.
/// Must be non-negative.
virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0;
virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0;
/// The maximum number of other agents this
/// agent takes into account in the navigation.
/// The larger this number, the longer the
/// running time of the simulation. If the
/// number is too low, the simulation will not
/// be safe.
virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
virtual int agent_get_max_neighbors(RID p_agent) const = 0;
// Sets the minimum amount of time in seconds that an agent's
// must be able to stay on the calculated velocity while still avoiding collisions with agent's
// if this value is set to high an agent will often fall back to using a very low velocity just to be safe
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0;
virtual real_t agent_get_time_horizon_agents(RID p_agent) const = 0;
/// Sets the minimum amount of time in seconds that an agent's
// must be able to stay on the calculated velocity while still avoiding collisions with obstacle's
// if this value is set to high an agent will often fall back to using a very low velocity just to be safe
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0;
virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const = 0;
/// The radius of this agent.
/// Must be non-negative.
virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0;
virtual real_t agent_get_radius(RID p_agent) const = 0;
virtual void agent_set_height(RID p_agent, real_t p_height) = 0;
virtual real_t agent_get_height(RID p_agent) const = 0;
/// The maximum speed of this agent.
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0;
virtual real_t agent_get_max_speed(RID p_agent) const = 0;
/// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) = 0;
/// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
/// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
virtual Vector3 agent_get_velocity(RID p_agent) const = 0;
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector3 p_position) = 0;
virtual Vector3 agent_get_position(RID p_agent) const = 0;
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const = 0;
/// Callback called at the end of the RVO process
virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0;
virtual bool agent_has_avoidance_callback(RID p_agent) const = 0;
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0;
virtual uint32_t agent_get_avoidance_layers(RID p_agent) const = 0;
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0;
virtual uint32_t agent_get_avoidance_mask(RID p_agent) const = 0;
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0;
virtual real_t agent_get_avoidance_priority(RID p_agent) const = 0;
/// Creates the obstacle.
virtual RID obstacle_create() = 0;
virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0;
virtual RID obstacle_get_map(RID p_obstacle) const = 0;
virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) = 0;
virtual bool obstacle_get_paused(RID p_obstacle) const = 0;
virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) = 0;
virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const = 0;
virtual void obstacle_set_use_3d_avoidance(RID p_obstacle, bool p_enabled) = 0;
virtual bool obstacle_get_use_3d_avoidance(RID p_obstacle) const = 0;
virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0;
virtual real_t obstacle_get_radius(RID p_obstacle) const = 0;
virtual void obstacle_set_height(RID p_obstacle, real_t p_height) = 0;
virtual real_t obstacle_get_height(RID p_obstacle) const = 0;
virtual void obstacle_set_velocity(RID p_obstacle, Vector3 p_velocity) = 0;
virtual Vector3 obstacle_get_velocity(RID p_obstacle) const = 0;
virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) = 0;
virtual Vector3 obstacle_get_position(RID p_obstacle) const = 0;
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) = 0;
virtual Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const = 0;
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0;
virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0;
/// Destroy the `RID`
virtual void free(RID p_object) = 0;
/// Control activation of this server.
virtual void set_active(bool p_active) = 0;
/// Process the collision avoidance agents.
/// The result of this process is needed by the physics server,
/// so this must be called in the main thread.
/// Note: This function is not thread safe.
virtual void process(real_t delta_time) = 0;
virtual void init() = 0;
virtual void sync() = 0;
virtual void finish() = 0;
/// Returns a customized navigation path using a query parameters object
virtual void query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const;
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
#ifndef _3D_DISABLED
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const = 0;
#endif // _3D_DISABLED
virtual RID source_geometry_parser_create() = 0;
virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) = 0;
virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) = 0;
NavigationServer3D();
~NavigationServer3D() override;
enum ProcessInfo {
INFO_ACTIVE_MAPS,
INFO_REGION_COUNT,
INFO_AGENT_COUNT,
INFO_LINK_COUNT,
INFO_POLYGON_COUNT,
INFO_EDGE_COUNT,
INFO_EDGE_MERGE_COUNT,
INFO_EDGE_CONNECTION_COUNT,
INFO_EDGE_FREE_COUNT,
};
virtual int get_process_info(ProcessInfo p_info) const = 0;
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
private:
bool debug_enabled = false;
#ifdef DEBUG_ENABLED
bool debug_dirty = true;
bool debug_navigation_enabled = false;
bool navigation_debug_dirty = true;
void _emit_navigation_debug_changed_signal();
bool debug_avoidance_enabled = false;
bool avoidance_debug_dirty = true;
void _emit_avoidance_debug_changed_signal();
Color debug_navigation_edge_connection_color = Color(1.0, 0.0, 1.0, 1.0);
Color debug_navigation_geometry_edge_color = Color(0.5, 1.0, 1.0, 1.0);
Color debug_navigation_geometry_face_color = Color(0.5, 1.0, 1.0, 0.4);
Color debug_navigation_geometry_edge_disabled_color = Color(0.5, 0.5, 0.5, 1.0);
Color debug_navigation_geometry_face_disabled_color = Color(0.5, 0.5, 0.5, 0.4);
Color debug_navigation_link_connection_color = Color(1.0, 0.5, 1.0, 1.0);
Color debug_navigation_link_connection_disabled_color = Color(0.5, 0.5, 0.5, 1.0);
Color debug_navigation_agent_path_color = Color(1.0, 0.0, 0.0, 1.0);
real_t debug_navigation_agent_path_point_size = 4.0;
Color debug_navigation_avoidance_agents_radius_color = Color(1.0, 1.0, 0.0, 0.25);
Color debug_navigation_avoidance_obstacles_radius_color = Color(1.0, 0.5, 0.0, 0.25);
Color debug_navigation_avoidance_static_obstacle_pushin_face_color = Color(1.0, 0.0, 0.0, 0.0);
Color debug_navigation_avoidance_static_obstacle_pushout_face_color = Color(1.0, 1.0, 0.0, 0.5);
Color debug_navigation_avoidance_static_obstacle_pushin_edge_color = Color(1.0, 0.0, 0.0, 1.0);
Color debug_navigation_avoidance_static_obstacle_pushout_edge_color = Color(1.0, 1.0, 0.0, 1.0);
bool debug_navigation_enable_edge_connections = true;
bool debug_navigation_enable_edge_connections_xray = true;
bool debug_navigation_enable_edge_lines = true;
bool debug_navigation_enable_edge_lines_xray = true;
bool debug_navigation_enable_geometry_face_random_color = true;
bool debug_navigation_enable_link_connections = true;
bool debug_navigation_enable_link_connections_xray = true;
bool debug_navigation_enable_agent_paths = true;
bool debug_navigation_enable_agent_paths_xray = true;
bool debug_navigation_avoidance_enable_agents_radius = true;
bool debug_navigation_avoidance_enable_obstacles_radius = true;
bool debug_navigation_avoidance_enable_obstacles_static = true;
Ref<StandardMaterial3D> debug_navigation_geometry_edge_material;
Ref<StandardMaterial3D> debug_navigation_geometry_face_material;
Ref<StandardMaterial3D> debug_navigation_geometry_edge_disabled_material;
Ref<StandardMaterial3D> debug_navigation_geometry_face_disabled_material;
Ref<StandardMaterial3D> debug_navigation_edge_connections_material;
Ref<StandardMaterial3D> debug_navigation_link_connections_material;
Ref<StandardMaterial3D> debug_navigation_link_connections_disabled_material;
Ref<StandardMaterial3D> debug_navigation_avoidance_agents_radius_material;
Ref<StandardMaterial3D> debug_navigation_avoidance_obstacles_radius_material;
Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushin_face_material;
Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushout_face_material;
Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushin_edge_material;
Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushout_edge_material;
Ref<StandardMaterial3D> debug_navigation_agent_path_line_material;
Ref<StandardMaterial3D> debug_navigation_agent_path_point_material;
public:
void set_debug_navigation_enabled(bool p_enabled);
bool get_debug_navigation_enabled() const;
void set_debug_avoidance_enabled(bool p_enabled);
bool get_debug_avoidance_enabled() const;
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
void set_debug_navigation_geometry_edge_color(const Color &p_color);
Color get_debug_navigation_geometry_edge_color() const;
void set_debug_navigation_geometry_face_color(const Color &p_color);
Color get_debug_navigation_geometry_face_color() const;
void set_debug_navigation_geometry_edge_disabled_color(const Color &p_color);
Color get_debug_navigation_geometry_edge_disabled_color() const;
void set_debug_navigation_geometry_face_disabled_color(const Color &p_color);
Color get_debug_navigation_geometry_face_disabled_color() const;
void set_debug_navigation_link_connection_color(const Color &p_color);
Color get_debug_navigation_link_connection_color() const;
void set_debug_navigation_link_connection_disabled_color(const Color &p_color);
Color get_debug_navigation_link_connection_disabled_color() const;
void set_debug_navigation_agent_path_color(const Color &p_color);
Color get_debug_navigation_agent_path_color() const;
void set_debug_navigation_avoidance_agents_radius_color(const Color &p_color);
Color get_debug_navigation_avoidance_agents_radius_color() const;
void set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color);
Color get_debug_navigation_avoidance_obstacles_radius_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const;
void set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color);
Color get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const;
void set_debug_navigation_enable_edge_connections(const bool p_value);
bool get_debug_navigation_enable_edge_connections() const;
void set_debug_navigation_enable_edge_connections_xray(const bool p_value);
bool get_debug_navigation_enable_edge_connections_xray() const;
void set_debug_navigation_enable_edge_lines(const bool p_value);
bool get_debug_navigation_enable_edge_lines() const;
void set_debug_navigation_enable_edge_lines_xray(const bool p_value);
bool get_debug_navigation_enable_edge_lines_xray() const;
void set_debug_navigation_enable_geometry_face_random_color(const bool p_value);
bool get_debug_navigation_enable_geometry_face_random_color() const;
void set_debug_navigation_enable_link_connections(const bool p_value);
bool get_debug_navigation_enable_link_connections() const;
void set_debug_navigation_enable_link_connections_xray(const bool p_value);
bool get_debug_navigation_enable_link_connections_xray() const;
void set_debug_navigation_enable_agent_paths(const bool p_value);
bool get_debug_navigation_enable_agent_paths() const;
void set_debug_navigation_enable_agent_paths_xray(const bool p_value);
bool get_debug_navigation_enable_agent_paths_xray() const;
void set_debug_navigation_agent_path_point_size(real_t p_point_size);
real_t get_debug_navigation_agent_path_point_size() const;
void set_debug_navigation_avoidance_enable_agents_radius(const bool p_value);
bool get_debug_navigation_avoidance_enable_agents_radius() const;
void set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value);
bool get_debug_navigation_avoidance_enable_obstacles_radius() const;
void set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value);
bool get_debug_navigation_avoidance_enable_obstacles_static() const;
Ref<StandardMaterial3D> get_debug_navigation_geometry_face_material();
Ref<StandardMaterial3D> get_debug_navigation_geometry_edge_material();
Ref<StandardMaterial3D> get_debug_navigation_geometry_face_disabled_material();
Ref<StandardMaterial3D> get_debug_navigation_geometry_edge_disabled_material();
Ref<StandardMaterial3D> get_debug_navigation_edge_connections_material();
Ref<StandardMaterial3D> get_debug_navigation_link_connections_material();
Ref<StandardMaterial3D> get_debug_navigation_link_connections_disabled_material();
Ref<StandardMaterial3D> get_debug_navigation_agent_path_line_material();
Ref<StandardMaterial3D> get_debug_navigation_agent_path_point_material();
Ref<StandardMaterial3D> get_debug_navigation_avoidance_agents_radius_material();
Ref<StandardMaterial3D> get_debug_navigation_avoidance_obstacles_radius_material();
Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushin_face_material();
Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushout_face_material();
Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
#endif // DEBUG_ENABLED
};
typedef NavigationServer3D *(*NavigationServer3DCallback)();
/// Manager used for the server singleton registration
class NavigationServer3DManager {
static NavigationServer3DCallback create_callback;
public:
static void set_default_server(NavigationServer3DCallback p_callback);
static NavigationServer3D *new_default_server();
};
VARIANT_ENUM_CAST(NavigationServer3D::ProcessInfo);
#endif // NAVIGATION_SERVER_3D_H

View file

@ -0,0 +1,204 @@
/**************************************************************************/
/* navigation_server_3d_dummy.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 NAVIGATION_SERVER_3D_DUMMY_H
#define NAVIGATION_SERVER_3D_DUMMY_H
#include "servers/navigation_server_3d.h"
class NavigationServer3DDummy : public NavigationServer3D {
GDCLASS(NavigationServer3DDummy, NavigationServer3D);
public:
TypedArray<RID> get_maps() const override { return TypedArray<RID>(); }
RID map_create() override { return RID(); }
void map_set_active(RID p_map, bool p_active) override {}
bool map_is_active(RID p_map) const override { return false; }
void map_set_up(RID p_map, Vector3 p_up) override {}
Vector3 map_get_up(RID p_map) const override { return Vector3(); }
void map_set_cell_size(RID p_map, real_t p_cell_size) override {}
real_t map_get_cell_size(RID p_map) const override { return 0; }
void map_set_cell_height(RID p_map, real_t p_cell_height) override {}
real_t map_get_cell_height(RID p_map) const override { return 0; }
void map_set_merge_rasterizer_cell_scale(RID p_map, float p_value) override {}
float map_get_merge_rasterizer_cell_scale(RID p_map) const override { return 1.0; }
void map_set_use_edge_connections(RID p_map, bool p_enabled) override {}
bool map_get_use_edge_connections(RID p_map) const override { return false; }
void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) override {}
real_t map_get_edge_connection_margin(RID p_map) const override { return 0; }
void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override {}
real_t map_get_link_connection_radius(RID p_map) const override { return 0; }
Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const override { return Vector<Vector3>(); }
Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const override { return Vector3(); }
Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override { return Vector3(); }
Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override { return Vector3(); }
RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const override { return RID(); }
Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); }
TypedArray<RID> map_get_links(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_regions(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_obstacles(RID p_map) const override { return TypedArray<RID>(); }
void map_force_update(RID p_map) override {}
uint32_t map_get_iteration_id(RID p_map) const override { return 0; }
RID region_create() override { return RID(); }
void region_set_enabled(RID p_region, bool p_enabled) override {}
bool region_get_enabled(RID p_region) const override { return false; }
void region_set_use_edge_connections(RID p_region, bool p_enabled) override {}
bool region_get_use_edge_connections(RID p_region) const override { return false; }
void region_set_enter_cost(RID p_region, real_t p_enter_cost) override {}
real_t region_get_enter_cost(RID p_region) const override { return 0; }
void region_set_travel_cost(RID p_region, real_t p_travel_cost) override {}
real_t region_get_travel_cost(RID p_region) const override { return 0; }
void region_set_owner_id(RID p_region, ObjectID p_owner_id) override {}
ObjectID region_get_owner_id(RID p_region) const override { return ObjectID(); }
bool region_owns_point(RID p_region, const Vector3 &p_point) const override { return false; }
void region_set_map(RID p_region, RID p_map) override {}
RID region_get_map(RID p_region) const override { return RID(); }
void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {}
uint32_t region_get_navigation_layers(RID p_region) const override { return 0; }
void region_set_transform(RID p_region, Transform3D p_transform) override {}
Transform3D region_get_transform(RID p_region) const override { return Transform3D(); }
void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) override {}
#ifndef DISABLE_DEPRECATED
void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override {}
#endif // DISABLE_DEPRECATED
int region_get_connections_count(RID p_region) const override { return 0; }
Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector3(); }
Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector3(); }
Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); }
RID link_create() override { return RID(); }
void link_set_map(RID p_link, RID p_map) override {}
RID link_get_map(RID p_link) const override { return RID(); }
void link_set_enabled(RID p_link, bool p_enabled) override {}
bool link_get_enabled(RID p_link) const override { return false; }
void link_set_bidirectional(RID p_link, bool p_bidirectional) override {}
bool link_is_bidirectional(RID p_link) const override { return false; }
void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) override {}
uint32_t link_get_navigation_layers(RID p_link) const override { return 0; }
void link_set_start_position(RID p_link, Vector3 p_position) override {}
Vector3 link_get_start_position(RID p_link) const override { return Vector3(); }
void link_set_end_position(RID p_link, Vector3 p_position) override {}
Vector3 link_get_end_position(RID p_link) const override { return Vector3(); }
void link_set_enter_cost(RID p_link, real_t p_enter_cost) override {}
real_t link_get_enter_cost(RID p_link) const override { return 0; }
void link_set_travel_cost(RID p_link, real_t p_travel_cost) override {}
real_t link_get_travel_cost(RID p_link) const override { return 0; }
void link_set_owner_id(RID p_link, ObjectID p_owner_id) override {}
ObjectID link_get_owner_id(RID p_link) const override { return ObjectID(); }
RID agent_create() override { return RID(); }
void agent_set_map(RID p_agent, RID p_map) override {}
RID agent_get_map(RID p_agent) const override { return RID(); }
void agent_set_paused(RID p_agent, bool p_paused) override {}
bool agent_get_paused(RID p_agent) const override { return false; }
void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override {}
bool agent_get_avoidance_enabled(RID p_agent) const override { return false; }
void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) override {}
bool agent_get_use_3d_avoidance(RID p_agent) const override { return false; }
void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {}
real_t agent_get_neighbor_distance(RID p_agent) const override { return 0; }
void agent_set_max_neighbors(RID p_agent, int p_count) override {}
int agent_get_max_neighbors(RID p_agent) const override { return 0; }
void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {}
real_t agent_get_time_horizon_agents(RID p_agent) const override { return 0; }
void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {}
real_t agent_get_time_horizon_obstacles(RID p_agent) const override { return 0; }
void agent_set_radius(RID p_agent, real_t p_radius) override {}
real_t agent_get_radius(RID p_agent) const override { return 0; }
void agent_set_height(RID p_agent, real_t p_height) override {}
real_t agent_get_height(RID p_agent) const override { return 0; }
void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {}
real_t agent_get_max_speed(RID p_agent) const override { return 0; }
void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) override {}
void agent_set_velocity(RID p_agent, Vector3 p_velocity) override {}
Vector3 agent_get_velocity(RID p_agent) const override { return Vector3(); }
void agent_set_position(RID p_agent, Vector3 p_position) override {}
Vector3 agent_get_position(RID p_agent) const override { return Vector3(); }
bool agent_is_map_changed(RID p_agent) const override { return false; }
void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {}
bool agent_has_avoidance_callback(RID p_agent) const override { return false; }
void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {}
uint32_t agent_get_avoidance_layers(RID p_agent) const override { return 0; }
void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {}
uint32_t agent_get_avoidance_mask(RID p_agent) const override { return 0; }
void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {}
real_t agent_get_avoidance_priority(RID p_agent) const override { return 0; }
RID obstacle_create() override { return RID(); }
void obstacle_set_map(RID p_obstacle, RID p_map) override {}
RID obstacle_get_map(RID p_obstacle) const override { return RID(); }
void obstacle_set_paused(RID p_obstacle, bool p_paused) override {}
bool obstacle_get_paused(RID p_obstacle) const override { return false; }
void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override {}
bool obstacle_get_avoidance_enabled(RID p_obstacle) const override { return false; }
void obstacle_set_use_3d_avoidance(RID p_obstacle, bool p_enabled) override {}
bool obstacle_get_use_3d_avoidance(RID p_obstacle) const override { return false; }
void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {}
real_t obstacle_get_radius(RID p_obstacle) const override { return 0; }
void obstacle_set_height(RID p_obstacle, real_t p_height) override {}
real_t obstacle_get_height(RID p_obstacle) const override { return 0; }
void obstacle_set_velocity(RID p_obstacle, Vector3 p_velocity) override {}
Vector3 obstacle_get_velocity(RID p_obstacle) const override { return Vector3(); }
void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
Vector3 obstacle_get_position(RID p_obstacle) const override { return Vector3(); }
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const override { return Vector<Vector3>(); }
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override { return 0; }
#ifndef _3D_DISABLED
void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override { return false; }
#endif // _3D_DISABLED
RID source_geometry_parser_create() override { return RID(); }
void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override {}
Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override { return Vector<Vector3>(); }
void free(RID p_object) override {}
void set_active(bool p_active) override {}
void process(real_t delta_time) override {}
void init() override {}
void sync() override {}
void finish() override {}
NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override { return NavigationUtilities::PathQueryResult(); }
int get_process_info(ProcessInfo p_info) const override { return 0; }
void set_debug_enabled(bool p_enabled) {}
bool get_debug_enabled() const { return false; }
};
#endif // NAVIGATION_SERVER_3D_DUMMY_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View file

@ -0,0 +1,314 @@
/**************************************************************************/
/* godot_area_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "godot_area_2d.h"
#include "godot_body_2d.h"
#include "godot_space_2d.h"
GodotArea2D::BodyKey::BodyKey(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
rid = p_body->get_self();
instance_id = p_body->get_instance_id();
body_shape = p_body_shape;
area_shape = p_area_shape;
}
GodotArea2D::BodyKey::BodyKey(GodotArea2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
rid = p_body->get_self();
instance_id = p_body->get_instance_id();
body_shape = p_body_shape;
area_shape = p_area_shape;
}
void GodotArea2D::_shapes_changed() {
if (!moved_list.in_list() && get_space()) {
get_space()->area_add_to_moved_list(&moved_list);
}
}
void GodotArea2D::set_transform(const Transform2D &p_transform) {
if (!moved_list.in_list() && get_space()) {
get_space()->area_add_to_moved_list(&moved_list);
}
_set_transform(p_transform);
_set_inv_transform(p_transform.affine_inverse());
}
void GodotArea2D::set_space(GodotSpace2D *p_space) {
if (get_space()) {
if (monitor_query_list.in_list()) {
get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
}
if (moved_list.in_list()) {
get_space()->area_remove_from_moved_list(&moved_list);
}
}
monitored_bodies.clear();
monitored_areas.clear();
_set_space(p_space);
}
void GodotArea2D::set_monitor_callback(const Callable &p_callback) {
_unregister_shapes();
monitor_callback = p_callback;
monitored_bodies.clear();
monitored_areas.clear();
_shape_changed();
if (!moved_list.in_list() && get_space()) {
get_space()->area_add_to_moved_list(&moved_list);
}
}
void GodotArea2D::set_area_monitor_callback(const Callable &p_callback) {
_unregister_shapes();
area_monitor_callback = p_callback;
monitored_bodies.clear();
monitored_areas.clear();
_shape_changed();
if (!moved_list.in_list() && get_space()) {
get_space()->area_add_to_moved_list(&moved_list);
}
}
void GodotArea2D::_set_space_override_mode(PhysicsServer2D::AreaSpaceOverrideMode &r_mode, PhysicsServer2D::AreaSpaceOverrideMode p_new_mode) {
bool do_override = p_new_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
if (do_override == (r_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED)) {
return;
}
_unregister_shapes();
r_mode = p_new_mode;
_shape_changed();
}
void GodotArea2D::set_param(PhysicsServer2D::AreaParameter p_param, const Variant &p_value) {
switch (p_param) {
case PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE:
_set_space_override_mode(gravity_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value);
break;
case PhysicsServer2D::AREA_PARAM_GRAVITY:
gravity = p_value;
break;
case PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR:
gravity_vector = p_value;
break;
case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT:
gravity_is_point = p_value;
break;
case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE:
gravity_point_unit_distance = p_value;
break;
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE:
_set_space_override_mode(linear_damping_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value);
break;
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP:
linear_damp = p_value;
break;
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE:
_set_space_override_mode(angular_damping_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value);
break;
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP:
angular_damp = p_value;
break;
case PhysicsServer2D::AREA_PARAM_PRIORITY:
priority = p_value;
break;
}
}
Variant GodotArea2D::get_param(PhysicsServer2D::AreaParameter p_param) const {
switch (p_param) {
case PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE:
return gravity_override_mode;
case PhysicsServer2D::AREA_PARAM_GRAVITY:
return gravity;
case PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR:
return gravity_vector;
case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT:
return gravity_is_point;
case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE:
return gravity_point_unit_distance;
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE:
return linear_damping_override_mode;
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP:
return linear_damp;
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE:
return angular_damping_override_mode;
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP:
return angular_damp;
case PhysicsServer2D::AREA_PARAM_PRIORITY:
return priority;
}
return Variant();
}
void GodotArea2D::_queue_monitor_update() {
ERR_FAIL_NULL(get_space());
if (!monitor_query_list.in_list()) {
get_space()->area_add_to_monitor_query_list(&monitor_query_list);
}
}
void GodotArea2D::set_monitorable(bool p_monitorable) {
if (monitorable == p_monitorable) {
return;
}
monitorable = p_monitorable;
_set_static(!monitorable);
_shapes_changed();
}
void GodotArea2D::call_queries() {
if (!monitor_callback.is_null() && !monitored_bodies.is_empty()) {
if (monitor_callback.is_valid()) {
Variant res[5];
Variant *resptr[5];
for (int i = 0; i < 5; i++) {
resptr[i] = &res[i];
}
for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_bodies.begin(); E;) {
if (E->value.state == 0) { // Nothing happened
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
++next;
monitored_bodies.remove(E);
E = next;
continue;
}
res[0] = E->value.state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED;
res[1] = E->key.rid;
res[2] = E->key.instance_id;
res[3] = E->key.body_shape;
res[4] = E->key.area_shape;
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
++next;
monitored_bodies.remove(E);
E = next;
Callable::CallError ce;
Variant ret;
monitor_callback.callp((const Variant **)resptr, 5, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(monitor_callback, (const Variant **)resptr, 5, ce));
}
}
} else {
monitored_bodies.clear();
monitor_callback = Callable();
}
}
if (!area_monitor_callback.is_null() && !monitored_areas.is_empty()) {
if (area_monitor_callback.is_valid()) {
Variant res[5];
Variant *resptr[5];
for (int i = 0; i < 5; i++) {
resptr[i] = &res[i];
}
for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_areas.begin(); E;) {
if (E->value.state == 0) { // Nothing happened
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
++next;
monitored_areas.remove(E);
E = next;
continue;
}
res[0] = E->value.state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED;
res[1] = E->key.rid;
res[2] = E->key.instance_id;
res[3] = E->key.body_shape;
res[4] = E->key.area_shape;
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
++next;
monitored_areas.remove(E);
E = next;
Callable::CallError ce;
Variant ret;
area_monitor_callback.callp((const Variant **)resptr, 5, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(area_monitor_callback, (const Variant **)resptr, 5, ce));
}
}
} else {
monitored_areas.clear();
area_monitor_callback = Callable();
}
}
}
void GodotArea2D::compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const {
if (is_gravity_point()) {
const real_t gr_unit_dist = get_gravity_point_unit_distance();
Vector2 v = get_transform().xform(get_gravity_vector()) - p_position;
if (gr_unit_dist > 0) {
const real_t v_length_sq = v.length_squared();
if (v_length_sq > 0) {
const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq;
r_gravity = v.normalized() * gravity_strength;
} else {
r_gravity = Vector2();
}
} else {
r_gravity = v.normalized() * get_gravity();
}
} else {
r_gravity = get_gravity_vector() * get_gravity();
}
}
GodotArea2D::GodotArea2D() :
GodotCollisionObject2D(TYPE_AREA),
monitor_query_list(this),
moved_list(this) {
_set_static(true); //areas are not active by default
}
GodotArea2D::~GodotArea2D() {
}

View file

@ -0,0 +1,191 @@
/**************************************************************************/
/* godot_area_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 GODOT_AREA_2D_H
#define GODOT_AREA_2D_H
#include "godot_collision_object_2d.h"
#include "core/templates/self_list.h"
#include "servers/physics_server_2d.h"
class GodotSpace2D;
class GodotBody2D;
class GodotConstraint2D;
class GodotArea2D : public GodotCollisionObject2D {
PhysicsServer2D::AreaSpaceOverrideMode gravity_override_mode = PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
PhysicsServer2D::AreaSpaceOverrideMode linear_damping_override_mode = PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
PhysicsServer2D::AreaSpaceOverrideMode angular_damping_override_mode = PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
real_t gravity = 9.80665;
Vector2 gravity_vector = Vector2(0, -1);
bool gravity_is_point = false;
real_t gravity_point_unit_distance = 0.0;
real_t linear_damp = 0.1;
real_t angular_damp = 1.0;
int priority = 0;
bool monitorable = false;
Callable monitor_callback;
Callable area_monitor_callback;
SelfList<GodotArea2D> monitor_query_list;
SelfList<GodotArea2D> moved_list;
struct BodyKey {
RID rid;
ObjectID instance_id;
uint32_t body_shape = 0;
uint32_t area_shape = 0;
static uint32_t hash(const BodyKey &p_key) {
uint32_t h = hash_one_uint64(p_key.rid.get_id());
h = hash_murmur3_one_64(p_key.instance_id, h);
h = hash_murmur3_one_32(p_key.area_shape, h);
return hash_fmix32(hash_murmur3_one_32(p_key.body_shape, h));
}
_FORCE_INLINE_ bool operator==(const BodyKey &p_key) const {
return rid == p_key.rid && instance_id == p_key.instance_id && body_shape == p_key.body_shape && area_shape == p_key.area_shape;
}
_FORCE_INLINE_ BodyKey() {}
BodyKey(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
BodyKey(GodotArea2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
};
struct BodyState {
int state = 0;
_FORCE_INLINE_ void inc() { state++; }
_FORCE_INLINE_ void dec() { state--; }
};
HashMap<BodyKey, BodyState, BodyKey> monitored_bodies;
HashMap<BodyKey, BodyState, BodyKey> monitored_areas;
HashSet<GodotConstraint2D *> constraints;
virtual void _shapes_changed() override;
void _queue_monitor_update();
void _set_space_override_mode(PhysicsServer2D::AreaSpaceOverrideMode &r_mode, PhysicsServer2D::AreaSpaceOverrideMode p_new_mode);
public:
void set_monitor_callback(const Callable &p_callback);
_FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback.is_valid(); }
void set_area_monitor_callback(const Callable &p_callback);
_FORCE_INLINE_ bool has_area_monitor_callback() const { return area_monitor_callback.is_valid(); }
_FORCE_INLINE_ void add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
_FORCE_INLINE_ void remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
_FORCE_INLINE_ void add_area_to_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape);
_FORCE_INLINE_ void remove_area_from_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape);
void set_param(PhysicsServer2D::AreaParameter p_param, const Variant &p_value);
Variant get_param(PhysicsServer2D::AreaParameter p_param) const;
_FORCE_INLINE_ void set_gravity(real_t p_gravity) { gravity = p_gravity; }
_FORCE_INLINE_ real_t get_gravity() const { return gravity; }
_FORCE_INLINE_ void set_gravity_vector(const Vector2 &p_gravity) { gravity_vector = p_gravity; }
_FORCE_INLINE_ Vector2 get_gravity_vector() const { return gravity_vector; }
_FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point = p_enable; }
_FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; }
_FORCE_INLINE_ void set_gravity_point_unit_distance(real_t scale) { gravity_point_unit_distance = scale; }
_FORCE_INLINE_ real_t get_gravity_point_unit_distance() const { return gravity_point_unit_distance; }
_FORCE_INLINE_ void set_linear_damp(real_t p_linear_damp) { linear_damp = p_linear_damp; }
_FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; }
_FORCE_INLINE_ void set_angular_damp(real_t p_angular_damp) { angular_damp = p_angular_damp; }
_FORCE_INLINE_ real_t get_angular_damp() const { return angular_damp; }
_FORCE_INLINE_ void set_priority(int p_priority) { priority = p_priority; }
_FORCE_INLINE_ int get_priority() const { return priority; }
_FORCE_INLINE_ void add_constraint(GodotConstraint2D *p_constraint) { constraints.insert(p_constraint); }
_FORCE_INLINE_ void remove_constraint(GodotConstraint2D *p_constraint) { constraints.erase(p_constraint); }
_FORCE_INLINE_ const HashSet<GodotConstraint2D *> &get_constraints() const { return constraints; }
_FORCE_INLINE_ void clear_constraints() { constraints.clear(); }
void set_monitorable(bool p_monitorable);
_FORCE_INLINE_ bool is_monitorable() const { return monitorable; }
void set_transform(const Transform2D &p_transform);
void set_space(GodotSpace2D *p_space) override;
void call_queries();
void compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const;
GodotArea2D();
~GodotArea2D();
};
void GodotArea2D::add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
BodyKey bk(p_body, p_body_shape, p_area_shape);
monitored_bodies[bk].inc();
if (!monitor_query_list.in_list()) {
_queue_monitor_update();
}
}
void GodotArea2D::remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
BodyKey bk(p_body, p_body_shape, p_area_shape);
monitored_bodies[bk].dec();
if (get_space() && !monitor_query_list.in_list()) {
_queue_monitor_update();
}
}
void GodotArea2D::add_area_to_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape) {
BodyKey bk(p_area, p_area_shape, p_self_shape);
monitored_areas[bk].inc();
if (!monitor_query_list.in_list()) {
_queue_monitor_update();
}
}
void GodotArea2D::remove_area_from_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape) {
BodyKey bk(p_area, p_area_shape, p_self_shape);
monitored_areas[bk].dec();
if (get_space() && !monitor_query_list.in_list()) {
_queue_monitor_update();
}
}
#endif // GODOT_AREA_2D_H

Some files were not shown because too many files have changed in this diff Show more