initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

41
servers/SCsub Normal file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
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, "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")
if not env["disable_physics_2d"]:
env.add_source_files(env.servers_sources, "physics_server_2d.cpp")
env.add_source_files(env.servers_sources, "physics_server_2d_wrap_mt.cpp")
if not env["disable_physics_3d"]:
env.add_source_files(env.servers_sources, "physics_server_3d.cpp")
env.add_source_files(env.servers_sources, "physics_server_3d_wrap_mt.cpp")
if not env["disable_xr"]:
env.add_source_files(env.servers_sources, "xr_server.cpp")
SConscript("xr/SCsub")
lib = env.add_library("servers", env.servers_sources)
env.Prepend(LIBS=[lib])

8
servers/audio/SCsub Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")
SConscript("effects/SCsub")

View File

@@ -0,0 +1,150 @@
/**************************************************************************/
/* 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/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,86 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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() {}
};

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_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_CALL(_instantiate, ret);
return ret;
}
void AudioEffect::_bind_methods() {
GDVIRTUAL_BIND(_instantiate);
}
AudioEffect::AudioEffect() {
}

View File

@@ -0,0 +1,61 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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_REQUIRED(_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_REQUIRED(Ref<AudioEffectInstance>, _instantiate)
static void _bind_methods();
public:
virtual Ref<AudioEffectInstance> instantiate();
AudioEffect();
};

View File

@@ -0,0 +1,262 @@
/**************************************************************************/
/* 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"
#include "core/math/math_funcs.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 * std::sqrt(Q + 1);
p_coeffs->b1 = 0.0;
p_coeffs->b2 = -alpha * std::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 += std::cos(freq) * p_coeffs->b1;
cy -= std::sin(freq) * p_coeffs->b1;
cx += std::cos(2 * freq) * p_coeffs->b2;
cy -= std::sin(2 * freq) * p_coeffs->b2;
float H = cx * cx + cy * cy;
cx = 1.0;
cy = 0.0;
cx -= std::cos(freq) * p_coeffs->a1;
cy += std::sin(freq) * p_coeffs->a1;
cx -= std::cos(2 * freq) * p_coeffs->a2;
cy += std::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,123 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/typedefs.h"
class AudioFilterSW {
public:
struct Coeffs {
double a1 = 0.0;
double a2 = 0.0;
double b0 = 0.0;
double b1 = 0.0;
double b2 = 0.0;
};
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;
}

View File

@@ -0,0 +1,260 @@
/**************************************************************************/
/* 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/audio_frame.h"
#include "core/os/memory.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);
}
// Downmix to stereo. Apply -3dB to center, and sides, -6dB to rear.
// four channels - channel order: front left, front right, rear left, rear right
if constexpr (C == 4) {
float v0 = rb[(pos << 2) + 0] + rb[(pos << 2) + 2] / 2;
float v1 = rb[(pos << 2) + 1] + rb[(pos << 2) + 3] / 2;
float v0n = rb[(pos_next << 2) + 0] + rb[(pos_next << 2) + 2] / 2;
float v1n = rb[(pos_next << 2) + 1] + rb[(pos_next << 2) + 3] / 2;
v0 += (v0n - v0) * frac;
v1 += (v1n - v1) * frac;
p_dest[i] = AudioFrame(v0, v1);
}
// six channels - channel order: front left, center, front right, rear left, rear right, LFE
if constexpr (C == 6) {
float v0 = rb[(pos * 6) + 0] + rb[(pos * 6) + 1] / Math::SQRT2 + rb[(pos * 6) + 3] / 2;
float v1 = rb[(pos * 6) + 2] + rb[(pos * 6) + 1] / Math::SQRT2 + rb[(pos * 6) + 4] / 2;
float v0n = rb[(pos_next * 6) + 0] + rb[(pos_next * 6) + 1] / Math::SQRT2 + rb[(pos_next * 6) + 3] / 2;
float v1n = rb[(pos_next * 6) + 2] + rb[(pos_next * 6) + 1] / Math::SQRT2 + rb[(pos_next * 6) + 4] / 2;
v0 += (v0n - v0) * frac;
v1 += (v1n - v1) * frac;
p_dest[i] = AudioFrame(v0, v1);
}
// eight channels - channel order: front left, center, front right, side left, side right, rear left, rear
// right, LFE
if constexpr (C == 8) {
float v0 = rb[(pos << 3) + 0] + rb[(pos << 3) + 1] / Math::SQRT2 + rb[(pos << 3) + 3] / Math::SQRT2 + rb[(pos << 3) + 5] / 2;
float v1 = rb[(pos << 3) + 2] + rb[(pos << 3) + 1] / Math::SQRT2 + rb[(pos << 3) + 4] / Math::SQRT2 + rb[(pos << 3) + 6] / 2;
float v0n = rb[(pos_next << 3) + 0] + rb[(pos_next << 3) + 1] / Math::SQRT2 + rb[(pos_next << 3) + 3] / Math::SQRT2 + rb[(pos_next << 3) + 5] / 2;
float v1n = rb[(pos_next << 3) + 2] + rb[(pos_next << 3) + 1] / Math::SQRT2 + rb[(pos_next << 3) + 4] / Math::SQRT2 + rb[(pos_next << 3) + 6] / 2;
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 * playback_speed) / 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;
case 8:
src_read = _resample<8>(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 && p_channels != 8, ERR_INVALID_PARAMETER);
int desired_rb_bits = nearest_shift((uint32_t)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;
}
void AudioRBResampler::set_playback_speed(double p_playback_speed) {
playback_speed = p_playback_speed;
}
double AudioRBResampler::get_playback_speed() const {
return playback_speed;
}
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,185 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/math/audio_frame.h"
#include "core/templates/safe_refcount.h"
#include "core/typedefs.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;
double playback_speed = 1.0;
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 - w) + (r - 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;
case 8: {
for (uint32_t i = 0; i < p_frames; i++) {
rb[(wp << 3) + 0] = read_buf[(i << 3) + 0];
rb[(wp << 3) + 1] = read_buf[(i << 3) + 1];
rb[(wp << 3) + 2] = read_buf[(i << 3) + 2];
rb[(wp << 3) + 3] = read_buf[(i << 3) + 3];
rb[(wp << 3) + 4] = read_buf[(i << 3) + 4];
rb[(wp << 3) + 5] = read_buf[(i << 3) + 5];
rb[(wp << 3) + 6] = read_buf[(i << 3) + 6];
rb[(wp << 3) + 7] = read_buf[(i << 3) + 7];
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();
void set_playback_speed(double p_playback_speed);
double get_playback_speed() const;
AudioRBResampler();
~AudioRBResampler();
};

View File

@@ -0,0 +1,882 @@
/**************************************************************************/
/* 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"
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_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret);
return ret;
}
PackedVector2Array AudioStreamPlayback::_mix_audio_bind(float p_rate_scale, int p_frames) {
Vector<AudioFrame> frames = mix_audio(p_rate_scale, p_frames);
PackedVector2Array res;
res.resize(frames.size());
Vector2 *res_ptrw = res.ptrw();
for (int i = 0; i < frames.size(); i++) {
res_ptrw[i] = Vector2(frames[i].left, frames[i].right);
}
return res;
}
Vector<AudioFrame> AudioStreamPlayback::mix_audio(float p_rate_scale, int p_frames) {
Vector<AudioFrame> res;
res.resize(p_frames);
int frames = mix(res.ptrw(), p_rate_scale, p_frames);
res.resize(frames);
return res;
}
void AudioStreamPlayback::start_playback(double p_from_pos) {
start(p_from_pos);
}
void AudioStreamPlayback::stop_playback() {
stop();
}
void AudioStreamPlayback::seek_playback(double p_time) {
seek(p_time);
}
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);
ClassDB::bind_method(D_METHOD("mix_audio", "rate_scale", "frames"), &AudioStreamPlayback::_mix_audio_bind);
ClassDB::bind_method(D_METHOD("start", "from_pos"), &AudioStreamPlayback::start_playback, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("seek", "time"), &AudioStreamPlayback::seek_playback, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayback::stop_playback);
ClassDB::bind_method(D_METHOD("get_loop_count"), &AudioStreamPlayback::get_loop_count);
ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayback::get_playback_position);
ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayback::is_playing);
}
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_CALL(_mix_resampled, p_buffer, p_frames, ret);
return ret;
}
float AudioStreamPlaybackResampled::get_stream_sampling_rate() {
float ret = 0;
GDVIRTUAL_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;
float h11 = mu2 * (mu - 1);
float z = mu2 - h11;
float h01 = z - h11;
float h10 = mu - z;
p_buffer[i] = y1 + (y2 - y1) * h01 + ((y2 - y0) * h10 + (y3 - y1) * h11) * 0.5;
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 = false;
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;
}
Dictionary AudioStream::get_tags() const {
Dictionary ret;
GDVIRTUAL_CALL(_get_tags, 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_tags);
GDVIRTUAL_BIND(_get_parameter_list)
GDVIRTUAL_BIND(_has_loop);
GDVIRTUAL_BIND(_get_bar_beats);
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;
}
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_input_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 {
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_input_mix_rate();
}
void AudioStreamPlaybackMicrophone::start(double p_from_pos) {
if (active) {
return;
}
if (!GLOBAL_GET_CACHED(bool, "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 {
if (!last_playback.is_valid()) {
return 0;
}
return last_playback->get_length();
}
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;
{
// GH-10238 : Pitch_scale is multiplicative, so picking a random number for it without log
// conversion will bias it towards higher pitches (0.5 is down one octave, 2.0 is up one octave).
// See: https://pressbooks.pub/sound/chapter/pitch-and-frequency-in-music/
float range_from = Math::log(1.0f / randomizer->random_pitch_scale);
float range_to = Math::log(randomizer->random_pitch_scale);
pitch_scale = Math::exp(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. */
/**************************************************************************/
#pragma once
#include "core/io/resource.h"
#include "scene/property_list_helper.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;
Ref<AudioStreamPlayback> stream_playback;
float offset = 0.0f;
float pitch_scale = 1.0;
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();
PackedVector2Array _mix_audio_bind(float p_rate_scale, int p_frames);
GDVIRTUAL1(_start, double)
GDVIRTUAL0(_stop)
GDVIRTUAL0RC(bool, _is_playing)
GDVIRTUAL0RC(int, _get_loop_count)
GDVIRTUAL0RC(double, _get_playback_position)
GDVIRTUAL1(_seek, double)
GDVIRTUAL3R_REQUIRED(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();
Vector<AudioFrame> mix_audio(float p_rate_scale, int p_frames);
void start_playback(double p_from_pos = 0.0);
void stop_playback();
void seek_playback(double p_time);
};
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_REQUIRED(int, _mix_resampled, GDExtensionPtr<AudioFrame>, int)
GDVIRTUAL0RC_REQUIRED(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(Dictionary, _get_tags);
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 Dictionary get_tags() 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;
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;
};
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);

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View File

@@ -0,0 +1,83 @@
/**************************************************************************/
/* 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::set_volume_linear(float p_volume) {
set_volume_db(Math::linear_to_db(p_volume));
}
float AudioEffectAmplify::get_volume_linear() const {
return Math::db_to_linear(get_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);
ClassDB::bind_method(D_METHOD("set_volume_linear", "volume"), &AudioEffectAmplify::set_volume_linear);
ClassDB::bind_method(D_METHOD("get_volume_linear"), &AudioEffectAmplify::get_volume_linear);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01,suffix:dB"), "set_volume_db", "get_volume_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_linear", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_volume_linear", "get_volume_linear");
}
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. */
/**************************************************************************/
#pragma once
#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;
void set_volume_linear(float p_volume);
float get_volume_linear() const;
AudioEffectAmplify();
};

View File

@@ -0,0 +1,140 @@
/**************************************************************************/
/* 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"
#include "servers/audio_server.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((uint32_t)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,77 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/math/audio_frame.h"
#include "core/object/ref_counted.h"
#include "core/templates/ring_buffer.h"
#include "servers/audio/audio_effect.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;
};

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 = std::rint(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 = std::exp(-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 = std::sin(phase * Math::TAU) * max_depth_frames;
int wave_delay_frames = std::rint(std::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_slicec('/', 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,131 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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:
static constexpr int32_t MAX_DELAY_MS = 50;
static constexpr int32_t MAX_DEPTH_MS = 20;
static constexpr int32_t MAX_WIDTH_MS = 50;
static constexpr int32_t MAX_VOICES = 4;
static constexpr int32_t CYCLES_FRAC = 16;
static constexpr int32_t CYCLES_MASK = (1 << CYCLES_FRAC) - 1;
static constexpr int32_t MAX_CHANNELS = 4;
static constexpr int32_t 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();
};

View File

@@ -0,0 +1,240 @@
/**************************************************************************/
/* 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 = std::exp(-1 / (0.00001f * sample_rate));
float ratrelcoef = std::exp(-1 / (0.5f * sample_rate));
float attime = base->attack_us / 1000000.0;
float reltime = base->release_ms / 1000.0;
float atcoef = std::exp(-1 / (attime * sample_rate));
float relcoef = std::exp(-1 / (reltime * sample_rate));
float makeup = Math::db_to_linear(base->gain);
float mix = base->mix;
float gr_meter_decay = std::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 (!Engine::get_singleton()->is_editor_hint()) {
return;
}
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,91 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

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 = std::exp(-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,132 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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() {}
};

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=std::exp(-Math::TAU*keep_hf_hz.get()/(mix_rate*(float)OVERSAMPLE));
float lpf_c = std::exp(-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 = std::pow(10, drive_f * drive_f * 3.0) - 1.0 + 0.001;
float atan_div = 1.0 / (std::atan(atan_mult) * (1.0 + drive_f * 8));
float lofi_mult = std::pow(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 = std::pow(std::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 = std::atan(a * atan_mult) * atan_div;
} break;
case AudioEffectDistortion::MODE_LOFI: {
a = std::floor(a * lofi_mult + 0.5) / lofi_mult;
} break;
case AudioEffectDistortion::MODE_OVERDRIVE: {
const double x = a * 0.686306;
const double z = 1 + std::exp(std::sqrt(std::abs(x)) * -0.75);
a = (std::exp(x) - std::exp(-x * z)) / (std::exp(x) + std::exp(-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 * std::abs(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,90 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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)

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,98 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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) {}
};

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,167 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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) {}
};

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(Math::abs(sample_left), Math::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,86 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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;
};

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,76 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

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,61 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

View File

@@ -0,0 +1,159 @@
/**************************************************************************/
/* 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 "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) * ((std::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,103 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

View File

@@ -0,0 +1,371 @@
/**************************************************************************/
/* 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*std::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.*std::sqrt(real*real + imag*imag);
phase = std::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 */
size_t fftBufferSize = static_cast<size_t>(fftFrameSize) * sizeof(float);
if (unlikely(fftBufferSize > MAX_FRAME_LENGTH)) {
ERR_PRINT_ONCE("Invalid FFT frame size for PitchShift. This is a bug, please report.");
return;
}
memset(gSynMagn, 0, fftBufferSize);
memset(gSynFreq, 0, fftBufferSize);
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*std::cos(phase);
gFFTworksp[2*k+1] = magn*std::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*std::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, fftBufferSize);
/* 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)(std::log((double)fftFrameSize)/std::log(2.)+.5); k++) {
le <<= 1;
le2 = le>>1;
ur = 1.0;
ui = 0.0;
arg = Math::PI / (le2>>1);
wr = std::cos(arg);
wi = sign*std::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) {
// Avoid distortion by skipping processing if pitch_scale is 1.0.
if (Math::is_equal_approx(base->pitch_scale, 1.0f)) {
for (int i = 0; i < p_frame_count; i++) {
p_dst_frames[i] = p_src_frames[i];
}
return;
}
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,111 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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 : unsigned int {
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);

View File

@@ -0,0 +1,294 @@
/**************************************************************************/
/* 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"
#include "core/io/marshalls.h"
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;
// Reusing 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.is_valid()) {
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.is_valid()) {
current_instance->finish();
}
}
void AudioEffectRecord::set_recording_active(bool p_record) {
if (p_record) {
if (current_instance.is_null()) {
WARN_PRINT("Recording should not be set as active before Godot has initialized.");
return;
}
ensure_thread_stopped();
current_instance->init();
} else {
if (current_instance.is_valid()) {
current_instance->is_recording = false;
}
}
}
bool AudioEffectRecord::is_recording_active() const {
if (current_instance.is_null()) {
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;
AudioStreamWAV::_compress_ima_adpcm(left, bleft);
AudioStreamWAV::_compress_ima_adpcm(right, bright);
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 if (dst_format == AudioStreamWAV::FORMAT_QOA) {
qoa_desc desc = {};
desc.samples = current_instance->recording_data.size() / 2;
desc.samplerate = AudioServer::get_singleton()->get_mix_rate();
desc.channels = 2;
AudioStreamWAV::_compress_qoa(current_instance->recording_data, dst_data, &desc);
} 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,Quite OK Audio"), "set_format", "get_format");
}
AudioEffectRecord::AudioEffectRecord() {
format = AudioStreamWAV::FORMAT_16_BITS;
}
AudioEffectRecord::~AudioEffectRecord() {
ensure_thread_stopped();
}

View File

@@ -0,0 +1,95 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

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,94 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

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)(std::log((double)fftFrameSize) / std::log(2.) + .5); k++) {
le <<= 1;
le2 = le >> 1;
ur = 1.0;
ui = 0.0;
arg = Math::PI / (le2 >> 1);
wr = std::cos(arg);
wi = sign * std::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 remainder_sec = (temporal_fft_pos / mix_rate); //subtract remainder from mix time
last_fft_time = time - uint64_t(remainder_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,104 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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);

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 left = p_src_frames[i].left;
float right = p_src_frames[i].right;
float center = (left + right) / 2.0f;
left = (center + (left - center) * intensity);
right = (center + (right - center) * intensity);
if (surround_mode) {
float val = (left + right) / 2.0;
delay_ringbuff[ringbuff_pos & ringbuff_mask] = val;
float out = delay_ringbuff[(ringbuff_pos - delay_frames) & ringbuff_mask] * surround_amount;
left += out;
right += -out;
} else {
float val = right;
delay_ringbuff[ringbuff_pos & ringbuff_mask] = val;
// The right channel is delayed.
right = delay_ringbuff[(ringbuff_pos - delay_frames) & ringbuff_mask];
}
p_dst_frames[i].left = left;
p_dst_frames[i].right = right;
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,82 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

View File

@@ -0,0 +1,244 @@
/**************************************************************************/
/* 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_mix_rate_mode(AudioStreamGenerator::AudioStreamGeneratorMixRate p_mix_rate_mode) {
ERR_FAIL_INDEX(p_mix_rate_mode, AudioStreamGeneratorMixRate::MIX_RATE_MAX);
mix_rate_mode = p_mix_rate_mode;
}
AudioStreamGenerator::AudioStreamGeneratorMixRate AudioStreamGenerator::get_mix_rate_mode() const {
return mix_rate_mode;
}
void AudioStreamGenerator::set_buffer_length(float p_seconds) {
buffer_len = p_seconds;
}
float AudioStreamGenerator::get_buffer_length() const {
return buffer_len;
}
float AudioStreamGenerator::_get_target_rate() const {
switch (mix_rate_mode) {
case AudioStreamGeneratorMixRate::MIX_RATE_OUTPUT:
return AudioServer::get_singleton()->get_mix_rate();
case AudioStreamGeneratorMixRate::MIX_RATE_INPUT:
return AudioServer::get_singleton()->get_input_mix_rate();
default:
return mix_rate;
}
}
Ref<AudioStreamPlayback> AudioStreamGenerator::instantiate_playback() {
Ref<AudioStreamGeneratorPlayback> playback;
playback.instantiate();
playback->generator = this;
uint32_t target_buffer_size = _get_target_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_mix_rate_mode", "mode"), &AudioStreamGenerator::set_mix_rate_mode);
ClassDB::bind_method(D_METHOD("get_mix_rate_mode"), &AudioStreamGenerator::get_mix_rate_mode);
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::INT, "mix_rate_mode", PROPERTY_HINT_ENUM, "System Output Rate,System Input Rate,Custom Rate"), "set_mix_rate_mode", "get_mix_rate_mode");
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");
BIND_ENUM_CONSTANT(MIX_RATE_OUTPUT);
BIND_ENUM_CONSTANT(MIX_RATE_INPUT);
BIND_ENUM_CONSTANT(MIX_RATE_CUSTOM);
BIND_ENUM_CONSTANT(MIX_RATE_MAX);
}
////////////////
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_target_rate();
return p_frames;
}
float AudioStreamGeneratorPlayback::get_stream_sampling_rate() {
return generator->_get_target_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,113 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/templates/ring_buffer.h"
#include "servers/audio/audio_stream.h"
class AudioStreamGenerator : public AudioStream {
GDCLASS(AudioStreamGenerator, AudioStream);
public:
enum AudioStreamGeneratorMixRate {
MIX_RATE_OUTPUT,
MIX_RATE_INPUT,
MIX_RATE_CUSTOM,
MIX_RATE_MAX,
};
private:
AudioStreamGeneratorMixRate mix_rate_mode = MIX_RATE_CUSTOM;
float mix_rate = 44100;
float buffer_len = 0.5;
protected:
static void _bind_methods();
public:
float _get_target_rate() const;
void set_mix_rate(float p_mix_rate);
float get_mix_rate() const;
void set_mix_rate_mode(AudioStreamGeneratorMixRate p_mix_rate_mode);
AudioStreamGeneratorMixRate get_mix_rate_mode() 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();
};
VARIANT_ENUM_CAST(AudioStreamGenerator::AudioStreamGeneratorMixRate);

View File

@@ -0,0 +1,201 @@
/**************************************************************************/
/* 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"
#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 = std::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) (std::log((m_f)) / std::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 = std::round(frq / std::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(std::cos(th)) - 2.0 * side_gain2 * std::cos(th_l) * std::cos(th) + side_gain2 - POW2(std::sin(th_l));
double c2b = 2.0 * side_gain2 * POW2(std::cos(th_l)) + side_gain2 * POW2(std::cos(th)) - 2.0 * side_gain2 * std::cos(th_l) * std::cos(th) - side_gain2 + POW2(std::sin(th_l));
double c2c = 0.25 * side_gain2 * POW2(std::cos(th)) - 0.5 * side_gain2 * std::cos(th_l) * std::cos(th) + 0.25 * side_gain2 - 0.25 * POW2(std::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) * std::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,98 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/templates/vector.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;
}

View File

@@ -0,0 +1,340 @@
/**************************************************************************/
/* 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/audio_frame.h"
#include "core/os/memory.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 = std::rint((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 = std::exp(-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 - std::rint((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-std::rint((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 - std::rint((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 = std::rint(params.extra_spread_base * params.mix_rate);
int len = std::rint(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 = std::rint(params.extra_spread_base * params.mix_rate);
int len = std::rint(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 = std::exp(-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,115 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
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();
};

2183
servers/audio_server.cpp Normal file

File diff suppressed because it is too large Load Diff

558
servers/audio_server.h Normal file
View File

@@ -0,0 +1,558 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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 int get_input_mix_rate() const { return get_mix_rate(); }
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 double get_sample_playback_position(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;
#ifdef DEBUG_ENABLED
bool debug_mute = false;
#endif // DEBUG_ENABLED
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 {
// The state machine for audio stream playbacks is as follows:
// 1. The playback is created and added to the playback list in the playing state.
// 2. The playback is (maybe) paused, and the state is set to FADE_OUT_TO_PAUSE.
// 2.1. The playback is mixed after being paused, and the audio server thread atomically sets the state to PAUSED after performing a brief fade-out.
// 3. The playback is (maybe) deleted, and the state is set to FADE_OUT_TO_DELETION.
// 3.1. The playback is mixed after being deleted, and the audio server thread atomically sets the state to AWAITING_DELETION after performing a brief fade-out.
// NOTE: The playback is not deallocated at this time because allocation and deallocation are not realtime-safe.
// 4. The playback is removed and deallocated on the main thread using the SafeList maybe_cleanup method.
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;
void _delete_stream_playback(Ref<AudioStreamPlayback> p_playback);
void _delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_node);
// 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);
#ifdef DEBUG_ENABLED
void set_debug_mute(bool p_mute);
bool get_debug_mute() const;
#endif // DEBUG_ENABLED
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_volume_linear(int p_bus, float p_volume_linear);
float get_bus_volume_linear(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;
String get_driver_name() 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 get_input_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);
double get_sample_playback_position(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;

6
servers/camera/SCsub Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View File

@@ -0,0 +1,317 @@
/**************************************************************************/
/* 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() {
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_image", "rgb_image"), &CameraFeed::set_rgb_image);
ClassDB::bind_method(D_METHOD("set_ycbcr_image", "ycbcr_image"), &CameraFeed::set_ycbcr_image);
ClassDB::bind_method(D_METHOD("set_external", "width", "height"), &CameraFeed::set_external);
ClassDB::bind_method(D_METHOD("get_texture_tex_id", "feed_image_type"), &CameraFeed::get_texture_tex_id);
ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype);
ClassDB::bind_method(D_METHOD("get_formats"), &CameraFeed::get_formats);
ClassDB::bind_method(D_METHOD("set_format", "index", "parameters"), &CameraFeed::set_format);
GDVIRTUAL_BIND(_activate_feed);
GDVIRTUAL_BIND(_deactivate_feed);
ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_SIGNAL(MethodInfo("format_changed"));
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");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "formats"), "", "get_formats");
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_EXTERNAL);
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()) {
active = true;
}
} else {
// just deactivate it
deactivate_feed();
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];
}
uint64_t CameraFeed::get_texture_tex_id(CameraServer::FeedImage p_which) {
return RenderingServer::get_singleton()->texture_get_native_handle(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_image(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);
// Defer `format_changed` signals to ensure they are emitted on Godot's main thread.
// This also makes sure the datatype of the feed is updated before the emission.
call_deferred("emit_signal", format_changed_signal_name);
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img);
}
datatype = CameraFeed::FEED_RGB;
// Most of the time the pixel data of camera devices comes from threads outside Godot.
// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
call_deferred("emit_signal", frame_changed_signal_name);
}
}
void CameraFeed::set_ycbcr_image(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);
// Defer `format_changed` signals to ensure they are emitted on Godot's main thread.
// This also makes sure the datatype of the feed is updated before the emission.
call_deferred("emit_signal", format_changed_signal_name);
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img);
}
datatype = CameraFeed::FEED_YCBCR;
// Most of the time the pixel data of camera devices comes from threads outside Godot.
// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
call_deferred("emit_signal", frame_changed_signal_name);
}
}
void CameraFeed::set_ycbcr_images(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);
}
// Defer `format_changed` signals to ensure they are emitted on Godot's main thread.
// This also makes sure the datatype of the feed is updated before the emission.
call_deferred("emit_signal", format_changed_signal_name);
} 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;
// Most of the time the pixel data of camera devices comes from threads outside Godot.
// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
call_deferred("emit_signal", frame_changed_signal_name);
}
}
void CameraFeed::set_external(int p_width, int p_height) {
if ((base_width != p_width) || (base_height != p_height)) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = p_width;
base_height = p_height;
RID new_texture = RenderingServer::get_singleton()->texture_external_create(p_width, p_height, 0);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_YCBCR_IMAGE], new_texture);
}
datatype = CameraFeed::FEED_EXTERNAL;
// Most of the time the pixel data of camera devices comes from threads outside Godot.
// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
call_deferred("emit_signal", frame_changed_signal_name);
}
bool CameraFeed::activate_feed() {
bool ret = true;
GDVIRTUAL_CALL(_activate_feed, ret);
return ret;
}
void CameraFeed::deactivate_feed() {
GDVIRTUAL_CALL(_deactivate_feed);
}
bool CameraFeed::set_format(int p_index, const Dictionary &p_parameters) {
return false;
}
Array CameraFeed::get_formats() const {
return Array();
}
CameraFeed::FeedFormat CameraFeed::get_format() const {
FeedFormat feed_format = {};
return feed_format;
}

View File

@@ -0,0 +1,132 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/io/image.h"
#include "core/math/transform_2d.h"
#include "servers/camera_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
FEED_EXTERNAL, // specific for android atm, camera feed is managed externally, assumed RGB for now
};
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
const StringName format_changed_signal_name = "format_changed";
const StringName frame_changed_signal_name = "frame_changed";
protected:
struct FeedFormat {
int width = 0;
int height = 0;
String format;
int frame_numerator = 0;
int frame_denominator = 0;
uint32_t pixel_format = 0;
};
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
int base_width = 0;
int base_height = 0;
Vector<FeedFormat> formats;
Dictionary parameters;
int selected_format = -1;
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);
uint64_t get_texture_tex_id(CameraServer::FeedImage p_which);
CameraFeed();
CameraFeed(String p_name, FeedPosition p_position = CameraFeed::FEED_UNSPECIFIED);
virtual ~CameraFeed();
FeedDataType get_datatype() const;
void set_rgb_image(const Ref<Image> &p_rgb_img);
void set_ycbcr_image(const Ref<Image> &p_ycbcr_img);
void set_ycbcr_images(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img);
void set_external(int p_width, int p_height);
virtual bool set_format(int p_index, const Dictionary &p_parameters);
virtual Array get_formats() const;
virtual FeedFormat get_format() const;
virtual bool activate_feed();
virtual void deactivate_feed();
GDVIRTUAL0R(bool, _activate_feed)
GDVIRTUAL0(_deactivate_feed)
};
VARIANT_ENUM_CAST(CameraFeed::FeedDataType);
VARIANT_ENUM_CAST(CameraFeed::FeedPosition);

186
servers/camera_server.cpp Normal file
View File

@@ -0,0 +1,186 @@
/**************************************************************************/
/* 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("set_monitoring_feeds", "is_monitoring_feeds"), &CameraServer::set_monitoring_feeds);
ClassDB::bind_method(D_METHOD("is_monitoring_feeds"), &CameraServer::is_monitoring_feeds);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring_feeds"), "set_monitoring_feeds", "is_monitoring_feeds");
ADD_PROPERTY_DEFAULT("monitoring_feeds", false);
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")));
ADD_SIGNAL(MethodInfo(feeds_updated_signal_name));
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;
}
void CameraServer::set_monitoring_feeds(bool p_monitoring_feeds) {
monitoring_feeds = p_monitoring_feeds;
}
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) {
ERR_FAIL_COND_V_MSG(!monitoring_feeds, -1, "CameraServer is not actively monitoring feeds; call set_monitoring_feeds(true) first.");
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) {
ERR_FAIL_COND_V_MSG(!monitoring_feeds, nullptr, "CameraServer is not actively monitoring feeds; call set_monitoring_feeds(true) first.");
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_COND_V_MSG(!monitoring_feeds, nullptr, "CameraServer is not actively monitoring feeds; call set_monitoring_feeds(true) first.");
ERR_FAIL_INDEX_V(p_index, feeds.size(), nullptr);
return feeds[p_index];
}
int CameraServer::get_feed_count() {
ERR_FAIL_COND_V_MSG(!monitoring_feeds, 0, "CameraServer is not actively monitoring feeds; call set_monitoring_feeds(true) first.");
return feeds.size();
}
TypedArray<CameraFeed> CameraServer::get_feeds() {
ERR_FAIL_COND_V_MSG(!monitoring_feeds, {}, "CameraServer is not actively monitoring feeds; call set_monitoring_feeds(true) first.");
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) {
ERR_FAIL_COND_V_MSG(!monitoring_feeds, RID(), "CameraServer is not actively monitoring feeds; call set_monitoring_feeds(true) first.");
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;
}

118
servers/camera_server.h Normal file
View File

@@ -0,0 +1,118 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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)();
static inline constexpr const char feeds_updated_signal_name[] = "camera_feeds_updated";
private:
protected:
static CreateFunc create_func;
bool monitoring_feeds = false;
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;
}
virtual void set_monitoring_feeds(bool p_monitoring_feeds);
_FORCE_INLINE_ bool is_monitoring_feeds() const { return monitoring_feeds; }
// 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);

6
servers/debugger/SCsub Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View File

@@ -0,0 +1,538 @@
/**************************************************************************/
/* 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/resource_loader.h"
#include "core/object/script_language.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 = { 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 = { name, 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 = { frame_number, frame_time, process_time, physics_time, physics_frame_time, 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 = { frame_number, 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);
// Send hardware information from the remote device so that it's accurate for remote debugging.
Array hardware_info = {
OS::get_singleton()->get_processor_name(),
RenderingServer::get_singleton()->get_video_adapter_name()
};
EngineDebugger::get_singleton()->send_message("visual:hardware_info", hardware_info);
}
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;
switch (E.type) {
case RS::TextureType::TEXTURE_TYPE_2D:
info.type = "Texture2D";
break;
case RS::TextureType::TEXTURE_TYPE_3D:
info.type = "Texture3D";
break;
case RS::TextureType::TEXTURE_TYPE_LAYERED:
info.type = "TextureLayered";
break;
}
String possible_type = _get_resource_type_from_path(E.path);
if (!possible_type.is_empty()) {
info.type = possible_type;
}
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);
}
List<RS::MeshInfo> mesh_info;
RS::get_singleton()->mesh_debug_usage(&mesh_info);
for (const RS::MeshInfo &E : mesh_info) {
ServersDebugger::ResourceInfo info;
info.path = E.path;
// We use 64-bit integers to avoid overflow, if for whatever reason, the sum is bigger than 4GB.
uint64_t vram = E.vertex_buffer_size + E.attribute_buffer_size + E.skin_buffer_size + E.index_buffer_size + E.blend_shape_buffer_size + E.lod_index_buffers_size;
// But can info.vram even hold that, and why is it an int instead of an uint?
info.vram = vram;
// Even though these empty meshes can be indicative of issues somewhere else
// for UX reasons, we don't want to show them.
if (vram == 0 && E.path.is_empty()) {
continue;
}
info.id = E.mesh;
info.type = "Mesh";
String possible_type = _get_resource_type_from_path(E.path);
if (!possible_type.is_empty()) {
info.type = possible_type;
}
info.format = itos(E.vertex_count) + " Vertices";
usage.infos.push_back(info);
}
EngineDebugger::get_singleton()->send_message("servers:memory_usage", usage.serialize());
}
// Done on a best-effort basis.
String ServersDebugger::_get_resource_type_from_path(const String &p_path) {
if (p_path.is_empty()) {
return "";
}
if (!ResourceLoader::exists(p_path)) {
return "";
}
if (ResourceCache::has(p_path)) {
Ref<Resource> resource = ResourceCache::get_ref(p_path);
return resource->get_class();
} else {
// This doesn't work all the time for embedded resources.
String resource_type = ResourceLoader::get_resource_type(p_path);
if (resource_type != "") {
return resource_type;
}
}
return "";
}
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,129 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
String _get_resource_type_from_path(const String &p_path);
ServersDebugger();
public:
static void initialize();
static void deinitialize();
~ServersDebugger();
};

6
servers/display/SCsub Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
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,157 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/input/input.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);

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* display_server.compat.inc */
/**************************************************************************/
/* 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 DISABLE_DEPRECATED
Error DisplayServer::_file_dialog_show_bind_compat_98194(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) {
return file_dialog_show(p_title, p_current_directory, p_filename, p_show_hidden, p_mode, p_filters, p_callback, MAIN_WINDOW_ID);
}
Error DisplayServer::_file_dialog_with_options_show_bind_compat_98194(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) {
return file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, MAIN_WINDOW_ID);
}
void DisplayServer::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("file_dialog_show", "title", "current_directory", "filename", "show_hidden", "mode", "filters", "callback"), &DisplayServer::_file_dialog_show_bind_compat_98194);
ClassDB::bind_compatibility_method(D_METHOD("file_dialog_with_options_show", "title", "current_directory", "root", "filename", "show_hidden", "mode", "filters", "options", "callback"), &DisplayServer::_file_dialog_with_options_show_bind_compat_98194);
}
#endif

2148
servers/display_server.cpp Normal file

File diff suppressed because it is too large Load Diff

1013
servers/display_server.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/display_server.h"
#include "servers/rendering/dummy/rasterizer_dummy.h"
class DisplayServerHeadless : public DisplayServer {
GDSOFTCLASS(DisplayServerHeadless, 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, int64_t p_parent_window, 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 INVALID_SCREEN; }
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 {}
void mouse_set_mode_override(MouseMode p_mode) override {}
void mouse_set_mode_override_enabled(bool p_override_enabled) 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, WindowID p_window_id) 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, WindowID p_window_id) 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;
}
}
};

11
servers/extensions/SCsub Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
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,360 @@
/**************************************************************************/
/* 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(_set_collision_layer, "layer");
GDVIRTUAL_BIND(_get_collision_layer);
GDVIRTUAL_BIND(_set_collision_mask, "mask");
GDVIRTUAL_BIND(_get_collision_mask);
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_step, "space", "delta")
GDVIRTUAL_BIND(_space_flush_queries, "space")
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(_space_get_last_process_info, "space", "process_info");
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,460 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.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)
EXBIND1(set_collision_layer, uint32_t);
EXBIND0RC(uint32_t, get_collision_layer);
EXBIND1(set_collision_mask, uint32_t);
EXBIND0RC(uint32_t, get_collision_mask);
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_REQUIRED(bool, _intersect_ray, const Vector2 &, const Vector2 &, uint32_t, bool, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionRayResult>)
GDVIRTUAL7R_REQUIRED(int, _intersect_point, const Vector2 &, ObjectID, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
GDVIRTUAL9R_REQUIRED(int, _intersect_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
GDVIRTUAL9R_REQUIRED(bool, _cast_motion, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>)
GDVIRTUAL10R_REQUIRED(bool, _collide_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
GDVIRTUAL8R_REQUIRED(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_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_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_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_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_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_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_REQUIRED(bool, _shape_collide, RID, const Transform2D &, const Vector2 &, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
GDVIRTUAL8R_REQUIRED(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_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 */
EXBIND2(space_step, RID, real_t)
EXBIND1(space_flush_queries, RID)
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_REQUIRED(TypedArray<RID>, _body_get_collision_exceptions, RID)
void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
GDVIRTUAL_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_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_REQUIRED(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_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_REQUIRED(_free_rid, RID)
virtual void free(RID p_rid) override {
GDVIRTUAL_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)
EXBIND2R(int, space_get_last_process_info, RID, ProcessInfo)
EXBIND0RC(bool, is_flushing_queries)
EXBIND1R(int, get_process_info, ProcessInfo)
PhysicsServer2DExtension();
~PhysicsServer2DExtension();
};

View File

@@ -0,0 +1,447 @@
/**************************************************************************/
/* 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(_set_collision_layer, "layer");
GDVIRTUAL_BIND(_get_collision_layer);
GDVIRTUAL_BIND(_set_collision_mask, "mask");
GDVIRTUAL_BIND(_get_collision_mask);
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_step, "space", "delta")
GDVIRTUAL_BIND(_space_flush_queries, "space")
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_shrinking_factor, "body", "shrinking_factor");
GDVIRTUAL_BIND(_soft_body_get_shrinking_factor, "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");
GDVIRTUAL_BIND(_soft_body_apply_point_impulse, "body", "point_index", "impulse");
GDVIRTUAL_BIND(_soft_body_apply_point_force, "body", "point_index", "force");
GDVIRTUAL_BIND(_soft_body_apply_central_impulse, "body", "impulse");
GDVIRTUAL_BIND(_soft_body_apply_central_force, "body", "force");
/* 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(_space_get_last_process_info, "space", "process_info");
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,555 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/script_language.h"
#include "core/variant/native_ptr.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)
EXBIND1(set_collision_layer, uint32_t);
EXBIND0RC(uint32_t, get_collision_layer);
EXBIND1(set_collision_mask, uint32_t);
EXBIND0RC(uint32_t, get_collision_mask);
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_REQUIRED(bool, _intersect_ray, const Vector3 &, const Vector3 &, uint32_t, bool, bool, bool, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionRayResult>)
GDVIRTUAL6R_REQUIRED(int, _intersect_point, const Vector3 &, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
GDVIRTUAL9R_REQUIRED(int, _intersect_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
GDVIRTUAL10R_REQUIRED(bool, _cast_motion, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
GDVIRTUAL10R_REQUIRED(bool, _collide_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector3>, int, GDExtensionPtr<int>)
GDVIRTUAL8R_REQUIRED(bool, _rest_info, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
GDVIRTUAL2RC_REQUIRED(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_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_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_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_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_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_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_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 */
EXBIND2(space_step, RID, real_t)
EXBIND1(space_flush_queries, RID)
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_REQUIRED(TypedArray<RID>, _body_get_collision_exceptions, RID)
void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
GDVIRTUAL_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_REQUIRED(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_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_REQUIRED(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_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_shrinking_factor, RID, real_t)
EXBIND1RC(real_t, soft_body_get_shrinking_factor, 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)
EXBIND3(soft_body_apply_point_impulse, RID, int, const Vector3 &)
EXBIND3(soft_body_apply_point_force, RID, int, const Vector3 &)
EXBIND2(soft_body_apply_central_impulse, RID, const Vector3 &)
EXBIND2(soft_body_apply_central_force, RID, const Vector3 &)
/* 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_REQUIRED(_free_rid, RID)
virtual void free(RID p_rid) override {
GDVIRTUAL_CALL(_free_rid, p_rid);
}
EXBIND2R(int, space_get_last_process_info, RID, ProcessInfo)
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();
};

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.servers_sources, "*.cpp")

View File

@@ -0,0 +1,261 @@
/**************************************************************************/
/* 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 "scene/main/window.h"
#include "servers/audio/audio_driver_dummy.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_CALL(_get_audio_mix_rate, ret);
return ret;
}
AudioServer::SpeakerMode MovieWriter::get_audio_speaker_mode() const {
AudioServer::SpeakerMode ret = AudioServer::SPEAKER_MODE_STEREO;
GDVIRTUAL_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_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_CALL(_write_frame, p_image, p_audio_data, ret);
return ret;
}
void MovieWriter::write_end() {
GDVIRTUAL_CALL(_write_end);
}
bool MovieWriter::handles_file(const String &p_path) const {
bool ret = false;
GDVIRTUAL_CALL(_handles_file, p_path, ret);
return ret;
}
void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
Vector<String> exts;
GDVIRTUAL_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));
// When using Display/Window/Stretch/Mode = Viewport, use the project's
// configured viewport size instead of the size of the window in the OS
Size2i actual_movie_size = p_movie_size;
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
if (stretch_mode == "viewport") {
actual_movie_size.width = GLOBAL_GET("display/window/size/viewport_width");
actual_movie_size.height = GLOBAL_GET("display/window/size/viewport_height");
print_line(vformat("Movie Maker mode using project viewport size: %dx%d",
actual_movie_size.width, actual_movie_size.height));
}
// 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;
encoding_time_usec = 0;
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(actual_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/video_quality", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), 0.75);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "editor/movie_writer/ogv/audio_quality", PROPERTY_HINT_RANGE, "-0.1,1.0,0.01"), 0.5);
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/movie_writer/ogv/encoding_speed", PROPERTY_HINT_ENUM, "Fastest (Lowest Efficiency):4,Fast (Low Efficiency):3,Slow (High Efficiency):2,Slowest (Highest Efficiency):1"), 4);
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/movie_writer/ogv/keyframe_interval", PROPERTY_HINT_RANGE, "1,1024,1"), 64);
// 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 int frame_remainder = Engine::get_singleton()->get_frames_drawn() % fps;
const String movie_time = vformat("%s:%s:%s:%s",
String::num(movie_time_seconds / 3600, 0).pad_zeros(2),
String::num((movie_time_seconds % 3600) / 60, 0).pad_zeros(2),
String::num(movie_time_seconds % 60, 0).pad_zeros(2),
String::num(frame_remainder, 0).pad_zeros(2));
Window *main_window = Window::get_from_id(DisplayServer::MAIN_WINDOW_ID);
if (main_window) {
main_window->set_title(vformat("MovieWriter: Frame %d (time: %s) - %s", Engine::get_singleton()->get_frames_drawn(), movie_time, project_name));
}
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);
if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) {
vp_tex->convert(Image::FORMAT_RGBA8);
vp_tex->linear_to_srgb();
}
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());
uint64_t encoding_start_usec = Time::get_singleton()->get_ticks_usec();
write_frame(vp_tex, audio_mix_buffer.ptr());
uint64_t encoding_end_usec = Time::get_singleton()->get_ticks_usec();
encoding_time_usec += encoding_end_usec - encoding_start_usec;
}
void MovieWriter::end() {
uint64_t encoding_start_usec = Time::get_singleton()->get_ticks_usec();
write_end();
uint64_t encoding_end_usec = Time::get_singleton()->get_ticks_usec();
encoding_time_usec += encoding_end_usec - encoding_start_usec;
// 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 int frame_remainder = Engine::get_singleton()->get_frames_drawn() % fps;
const String movie_time = vformat("%s:%s:%s:%s",
String::num(movie_time_seconds / 3600, 0).pad_zeros(2),
String::num((movie_time_seconds % 3600) / 60, 0).pad_zeros(2),
String::num(movie_time_seconds % 60, 0).pad_zeros(2),
String::num(frame_remainder, 0).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, 0).pad_zeros(2),
String::num((real_time_seconds % 3600) / 60, 0).pad_zeros(2),
String::num(real_time_seconds % 60, 0).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(MAX(1, movie_time_seconds)) / MAX(1, real_time_seconds)) * 100));
print_line(vformat("CPU render time: %.2f seconds (average: %.2f ms/frame)", cpu_time / 1000, cpu_time / Engine::get_singleton()->get_frames_drawn()));
print_line(vformat("GPU render time: %.2f seconds (average: %.2f ms/frame)", gpu_time / 1000, gpu_time / Engine::get_singleton()->get_frames_drawn()));
print_line(vformat("Encoding time: %.2f seconds (average: %.2f ms/frame)", encoding_time_usec / 1000000.f, encoding_time_usec / 1000.f / Engine::get_singleton()->get_frames_drawn()));
print_line("--------------------------------------------------------------------------------");
}

View File

@@ -0,0 +1,91 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/io/image.h"
#include "core/templates/local_vector.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;
uint64_t encoding_time_usec = 0;
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_REQUIRED(uint32_t, _get_audio_mix_rate)
GDVIRTUAL0RC_REQUIRED(AudioServer::SpeakerMode, _get_audio_speaker_mode)
GDVIRTUAL1RC_REQUIRED(bool, _handles_file, const String &)
GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_supported_extensions)
GDVIRTUAL3R_REQUIRED(Error, _write_begin, const Size2i &, uint32_t, const String &)
GDVIRTUAL2R_REQUIRED(Error, _write_frame, const Ref<Image> &, GDExtensionConstPtr<int32_t>)
GDVIRTUAL0_REQUIRED(_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();
};

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_null(), 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,68 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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();
};

12
servers/navigation/SCsub Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
if not env["disable_navigation_2d"]:
env.add_source_files(env.servers_sources, "navigation_path_query_parameters_2d.cpp")
env.add_source_files(env.servers_sources, "navigation_path_query_result_2d.cpp")
if not env["disable_navigation_3d"]:
env.add_source_files(env.servers_sources, "navigation_path_query_parameters_3d.cpp")
env.add_source_files(env.servers_sources, "navigation_path_query_result_3d.cpp")

View File

@@ -0,0 +1,158 @@
/**************************************************************************/
/* nav_heap.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. */
/**************************************************************************/
#pragma once
#include "core/templates/local_vector.h"
template <typename T>
struct NoopIndexer {
void operator()(const T &p_value, uint32_t p_index) {}
};
/**
* A max-heap implementation that notifies of element index changes.
*/
template <typename T, typename LessThan = Comparator<T>, typename Indexer = NoopIndexer<T>>
class Heap {
LocalVector<T> _buffer;
LessThan _less_than;
Indexer _indexer;
public:
static constexpr uint32_t INVALID_INDEX = UINT32_MAX;
void reserve(uint32_t p_size) {
_buffer.reserve(p_size);
}
uint32_t size() const {
return _buffer.size();
}
bool is_empty() const {
return _buffer.is_empty();
}
void push(const T &p_element) {
_buffer.push_back(p_element);
_indexer(p_element, _buffer.size() - 1);
_shift_up(_buffer.size() - 1);
}
T pop() {
ERR_FAIL_COND_V_MSG(_buffer.is_empty(), T(), "Can't pop an empty heap.");
T value = _buffer[0];
_indexer(value, INVALID_INDEX);
if (_buffer.size() > 1) {
_buffer[0] = _buffer[_buffer.size() - 1];
_indexer(_buffer[0], 0);
_buffer.remove_at(_buffer.size() - 1);
_shift_down(0);
} else {
_buffer.remove_at(_buffer.size() - 1);
}
return value;
}
/**
* Update the position of the element in the heap if necessary.
*/
void shift(uint32_t p_index) {
ERR_FAIL_UNSIGNED_INDEX_MSG(p_index, _buffer.size(), "Heap element index is out of range.");
if (!_shift_up(p_index)) {
_shift_down(p_index);
}
}
void clear() {
for (const T &value : _buffer) {
_indexer(value, INVALID_INDEX);
}
_buffer.clear();
}
Heap() {}
Heap(const LessThan &p_less_than) :
_less_than(p_less_than) {}
Heap(const Indexer &p_indexer) :
_indexer(p_indexer) {}
Heap(const LessThan &p_less_than, const Indexer &p_indexer) :
_less_than(p_less_than), _indexer(p_indexer) {}
private:
bool _shift_up(uint32_t p_index) {
T value = _buffer[p_index];
uint32_t current_index = p_index;
uint32_t parent_index = (current_index - 1) / 2;
while (current_index > 0 && _less_than(_buffer[parent_index], value)) {
_buffer[current_index] = _buffer[parent_index];
_indexer(_buffer[current_index], current_index);
current_index = parent_index;
parent_index = (current_index - 1) / 2;
}
if (current_index != p_index) {
_buffer[current_index] = value;
_indexer(value, current_index);
return true;
} else {
return false;
}
}
bool _shift_down(uint32_t p_index) {
T value = _buffer[p_index];
uint32_t current_index = p_index;
uint32_t child_index = 2 * current_index + 1;
while (child_index < _buffer.size()) {
if (child_index + 1 < _buffer.size() &&
_less_than(_buffer[child_index], _buffer[child_index + 1])) {
child_index++;
}
if (_less_than(_buffer[child_index], value)) {
break;
}
_buffer[current_index] = _buffer[child_index];
_indexer(_buffer[current_index], current_index);
current_index = child_index;
child_index = 2 * current_index + 1;
}
if (current_index != p_index) {
_buffer[current_index] = value;
_indexer(value, current_index);
return true;
} else {
return false;
}
}
};

View File

@@ -0,0 +1,86 @@
/**************************************************************************/
/* navigation_globals.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. */
/**************************************************************************/
#pragma once
namespace NavigationDefaults3D {
// Rasterization.
// To find the polygons edges the vertices are displaced in a grid where
// each cell has the following cell_size and cell_height.
constexpr float NAV_MESH_CELL_HEIGHT = 0.25f; // Must match ProjectSettings default 3D cell_height and NavigationMesh cell_height.
constexpr float NAV_MESH_CELL_SIZE = 0.25f; // Must match ProjectSettings default 3D cell_size and NavigationMesh cell_size.
constexpr float NAV_MESH_CELL_SIZE_MIN = 0.01f;
constexpr const char *const NAV_MESH_CELL_SIZE_HINT = "0.001,100,0.001,or_greater";
// Map.
constexpr float EDGE_CONNECTION_MARGIN = 0.25f;
constexpr float LINK_CONNECTION_RADIUS = 1.0f;
constexpr int path_search_max_polygons = 4096;
// Agent.
constexpr float AVOIDANCE_AGENT_HEIGHT = 1.0;
constexpr float AVOIDANCE_AGENT_RADIUS = 0.5;
constexpr float AVOIDANCE_AGENT_MAX_SPEED = 10.0;
constexpr float AVOIDANCE_AGENT_TIME_HORIZON_AGENTS = 1.0;
constexpr float AVOIDANCE_AGENT_TIME_HORIZON_OBSTACLES = 0.0;
constexpr int AVOIDANCE_AGENT_MAX_NEIGHBORS = 10;
constexpr float AVOIDANCE_AGENT_NEIGHBOR_DISTANCE = 50.0;
} //namespace NavigationDefaults3D
namespace NavigationDefaults2D {
// Rasterization.
// Same as in 3D but larger since 1px is treated as 1m.
constexpr float NAV_MESH_CELL_SIZE = 1.0f; // Must match ProjectSettings default 2D cell_size.
constexpr float NAV_MESH_CELL_SIZE_MIN = 0.01f;
constexpr const char *const NAV_MESH_CELL_SIZE_HINT = "0.001,100,0.001,or_greater";
// Map.
constexpr float EDGE_CONNECTION_MARGIN = 1.0f;
constexpr float LINK_CONNECTION_RADIUS = 4.0f;
constexpr int path_search_max_polygons = 4096;
// Agent.
constexpr float AVOIDANCE_AGENT_RADIUS = 10.0;
constexpr float AVOIDANCE_AGENT_MAX_SPEED = 100.0;
constexpr float AVOIDANCE_AGENT_TIME_HORIZON_AGENTS = 1.0;
constexpr float AVOIDANCE_AGENT_TIME_HORIZON_OBSTACLES = 0.0;
constexpr int AVOIDANCE_AGENT_MAX_NEIGHBORS = 10;
constexpr float AVOIDANCE_AGENT_NEIGHBOR_DISTANCE = 500.0;
} //namespace NavigationDefaults2D

View File

@@ -0,0 +1,242 @@
/**************************************************************************/
/* 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) {
pathfinding_algorithm = p_pathfinding_algorithm;
}
NavigationPathQueryParameters2D::PathfindingAlgorithm NavigationPathQueryParameters2D::get_pathfinding_algorithm() const {
return pathfinding_algorithm;
}
void NavigationPathQueryParameters2D::set_path_postprocessing(const NavigationPathQueryParameters2D::PathPostProcessing p_path_postprocessing) {
path_postprocessing = p_path_postprocessing;
}
NavigationPathQueryParameters2D::PathPostProcessing NavigationPathQueryParameters2D::get_path_postprocessing() const {
return path_postprocessing;
}
void NavigationPathQueryParameters2D::set_map(RID p_map) {
map = p_map;
}
RID NavigationPathQueryParameters2D::get_map() const {
return map;
}
void NavigationPathQueryParameters2D::set_start_position(Vector2 p_start_position) {
start_position = p_start_position;
}
Vector2 NavigationPathQueryParameters2D::get_start_position() const {
return start_position;
}
void NavigationPathQueryParameters2D::set_target_position(Vector2 p_target_position) {
target_position = p_target_position;
}
Vector2 NavigationPathQueryParameters2D::get_target_position() const {
return target_position;
}
void NavigationPathQueryParameters2D::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
}
uint32_t NavigationPathQueryParameters2D::get_navigation_layers() const {
return navigation_layers;
}
void NavigationPathQueryParameters2D::set_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags) {
metadata_flags = (int64_t)p_flags;
}
BitField<NavigationPathQueryParameters2D::PathMetadataFlags> NavigationPathQueryParameters2D::get_metadata_flags() const {
return (int64_t)metadata_flags;
}
void NavigationPathQueryParameters2D::set_simplify_path(bool p_enabled) {
simplify_path = p_enabled;
}
bool NavigationPathQueryParameters2D::get_simplify_path() const {
return simplify_path;
}
void NavigationPathQueryParameters2D::set_simplify_epsilon(real_t p_epsilon) {
simplify_epsilon = MAX(0.0, p_epsilon);
}
real_t NavigationPathQueryParameters2D::get_simplify_epsilon() const {
return simplify_epsilon;
}
void NavigationPathQueryParameters2D::set_included_regions(const TypedArray<RID> &p_regions) {
_included_regions.resize(p_regions.size());
for (uint32_t i = 0; i < _included_regions.size(); i++) {
_included_regions[i] = p_regions[i];
}
}
TypedArray<RID> NavigationPathQueryParameters2D::get_included_regions() const {
TypedArray<RID> r_regions;
r_regions.resize(_included_regions.size());
for (uint32_t i = 0; i < _included_regions.size(); i++) {
r_regions[i] = _included_regions[i];
}
return r_regions;
}
void NavigationPathQueryParameters2D::set_excluded_regions(const TypedArray<RID> &p_regions) {
_excluded_regions.resize(p_regions.size());
for (uint32_t i = 0; i < _excluded_regions.size(); i++) {
_excluded_regions[i] = p_regions[i];
}
}
TypedArray<RID> NavigationPathQueryParameters2D::get_excluded_regions() const {
TypedArray<RID> r_regions;
r_regions.resize(_excluded_regions.size());
for (uint32_t i = 0; i < _excluded_regions.size(); i++) {
r_regions[i] = _excluded_regions[i];
}
return r_regions;
}
void NavigationPathQueryParameters2D::set_path_return_max_length(float p_length) {
path_return_max_length = MAX(0.0, p_length);
}
float NavigationPathQueryParameters2D::get_path_return_max_length() const {
return path_return_max_length;
}
void NavigationPathQueryParameters2D::set_path_return_max_radius(float p_radius) {
path_return_max_radius = MAX(0.0, p_radius);
}
float NavigationPathQueryParameters2D::get_path_return_max_radius() const {
return path_return_max_radius;
}
void NavigationPathQueryParameters2D::set_path_search_max_polygons(int p_max_polygons) {
path_search_max_polygons = p_max_polygons;
}
int NavigationPathQueryParameters2D::get_path_search_max_polygons() const {
return path_search_max_polygons;
}
void NavigationPathQueryParameters2D::set_path_search_max_distance(float p_distance) {
path_search_max_distance = MAX(0.0, p_distance);
}
float NavigationPathQueryParameters2D::get_path_search_max_distance() const {
return path_search_max_distance;
}
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);
ClassDB::bind_method(D_METHOD("set_included_regions", "regions"), &NavigationPathQueryParameters2D::set_included_regions);
ClassDB::bind_method(D_METHOD("get_included_regions"), &NavigationPathQueryParameters2D::get_included_regions);
ClassDB::bind_method(D_METHOD("set_excluded_regions", "regions"), &NavigationPathQueryParameters2D::set_excluded_regions);
ClassDB::bind_method(D_METHOD("get_excluded_regions"), &NavigationPathQueryParameters2D::get_excluded_regions);
ClassDB::bind_method(D_METHOD("set_path_return_max_length", "length"), &NavigationPathQueryParameters2D::set_path_return_max_length);
ClassDB::bind_method(D_METHOD("get_path_return_max_length"), &NavigationPathQueryParameters2D::get_path_return_max_length);
ClassDB::bind_method(D_METHOD("set_path_return_max_radius", "radius"), &NavigationPathQueryParameters2D::set_path_return_max_radius);
ClassDB::bind_method(D_METHOD("get_path_return_max_radius"), &NavigationPathQueryParameters2D::get_path_return_max_radius);
ClassDB::bind_method(D_METHOD("set_path_search_max_polygons", "max_polygons"), &NavigationPathQueryParameters2D::set_path_search_max_polygons);
ClassDB::bind_method(D_METHOD("get_path_search_max_polygons"), &NavigationPathQueryParameters2D::get_path_search_max_polygons);
ClassDB::bind_method(D_METHOD("set_path_search_max_distance", "distance"), &NavigationPathQueryParameters2D::set_path_search_max_distance);
ClassDB::bind_method(D_METHOD("get_path_search_max_distance"), &NavigationPathQueryParameters2D::get_path_search_max_distance);
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,None"), "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");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "excluded_regions", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_excluded_regions", "get_excluded_regions");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "included_regions", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_included_regions", "get_included_regions");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_return_max_length"), "set_path_return_max_length", "get_path_return_max_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_return_max_radius"), "set_path_return_max_radius", "get_path_return_max_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_search_max_polygons"), "set_path_search_max_polygons", "get_path_search_max_polygons");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_search_max_distance"), "set_path_search_max_distance", "get_path_search_max_distance");
BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_NONE);
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,130 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/object/ref_counted.h"
#include "servers/navigation/navigation_globals.h"
#include "servers/navigation/navigation_utilities.h"
class NavigationPathQueryParameters2D : public RefCounted {
GDCLASS(NavigationPathQueryParameters2D, RefCounted);
protected:
static void _bind_methods();
public:
enum PathfindingAlgorithm {
PATHFINDING_ALGORITHM_ASTAR = NavigationUtilities::PATHFINDING_ALGORITHM_ASTAR,
};
enum PathPostProcessing {
PATH_POSTPROCESSING_CORRIDORFUNNEL = NavigationUtilities::PATH_POSTPROCESSING_CORRIDORFUNNEL,
PATH_POSTPROCESSING_EDGECENTERED = NavigationUtilities::PATH_POSTPROCESSING_EDGECENTERED,
PATH_POSTPROCESSING_NONE = NavigationUtilities::PATH_POSTPROCESSING_NONE,
};
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
};
private:
PathfindingAlgorithm pathfinding_algorithm = PATHFINDING_ALGORITHM_ASTAR;
PathPostProcessing path_postprocessing = PATH_POSTPROCESSING_CORRIDORFUNNEL;
RID map;
Vector2 start_position;
Vector2 target_position;
uint32_t navigation_layers = 1;
BitField<PathMetadataFlags> metadata_flags = PATH_METADATA_INCLUDE_ALL;
bool simplify_path = false;
real_t simplify_epsilon = 0.0;
LocalVector<RID> _excluded_regions;
LocalVector<RID> _included_regions;
float path_return_max_length = 0.0;
float path_return_max_radius = 0.0;
int path_search_max_polygons = NavigationDefaults2D::path_search_max_polygons;
float path_search_max_distance = 0.0;
public:
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(RID p_map);
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;
void set_excluded_regions(const TypedArray<RID> &p_regions);
TypedArray<RID> get_excluded_regions() const;
void set_included_regions(const TypedArray<RID> &p_regions);
TypedArray<RID> get_included_regions() const;
void set_path_return_max_length(float p_length);
float get_path_return_max_length() const;
void set_path_return_max_radius(float p_radius);
float get_path_return_max_radius() const;
void set_path_search_max_polygons(int p_max_polygons);
int get_path_search_max_polygons() const;
void set_path_search_max_distance(float p_distance);
float get_path_search_max_distance() const;
};
VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathfindingAlgorithm);
VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathPostProcessing);
VARIANT_BITFIELD_CAST(NavigationPathQueryParameters2D::PathMetadataFlags);

View File

@@ -0,0 +1,242 @@
/**************************************************************************/
/* 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) {
pathfinding_algorithm = p_pathfinding_algorithm;
}
NavigationPathQueryParameters3D::PathfindingAlgorithm NavigationPathQueryParameters3D::get_pathfinding_algorithm() const {
return pathfinding_algorithm;
}
void NavigationPathQueryParameters3D::set_path_postprocessing(const NavigationPathQueryParameters3D::PathPostProcessing p_path_postprocessing) {
path_postprocessing = p_path_postprocessing;
}
NavigationPathQueryParameters3D::PathPostProcessing NavigationPathQueryParameters3D::get_path_postprocessing() const {
return path_postprocessing;
}
void NavigationPathQueryParameters3D::set_map(RID p_map) {
map = p_map;
}
RID NavigationPathQueryParameters3D::get_map() const {
return map;
}
void NavigationPathQueryParameters3D::set_start_position(Vector3 p_start_position) {
start_position = p_start_position;
}
Vector3 NavigationPathQueryParameters3D::get_start_position() const {
return start_position;
}
void NavigationPathQueryParameters3D::set_target_position(Vector3 p_target_position) {
target_position = p_target_position;
}
Vector3 NavigationPathQueryParameters3D::get_target_position() const {
return target_position;
}
void NavigationPathQueryParameters3D::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
}
uint32_t NavigationPathQueryParameters3D::get_navigation_layers() const {
return navigation_layers;
}
void NavigationPathQueryParameters3D::set_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags) {
metadata_flags = (int64_t)p_flags;
}
BitField<NavigationPathQueryParameters3D::PathMetadataFlags> NavigationPathQueryParameters3D::get_metadata_flags() const {
return (int64_t)metadata_flags;
}
void NavigationPathQueryParameters3D::set_simplify_path(bool p_enabled) {
simplify_path = p_enabled;
}
bool NavigationPathQueryParameters3D::get_simplify_path() const {
return simplify_path;
}
void NavigationPathQueryParameters3D::set_simplify_epsilon(real_t p_epsilon) {
simplify_epsilon = MAX(0.0, p_epsilon);
}
real_t NavigationPathQueryParameters3D::get_simplify_epsilon() const {
return simplify_epsilon;
}
void NavigationPathQueryParameters3D::set_included_regions(const TypedArray<RID> &p_regions) {
_included_regions.resize(p_regions.size());
for (uint32_t i = 0; i < _included_regions.size(); i++) {
_included_regions[i] = p_regions[i];
}
}
TypedArray<RID> NavigationPathQueryParameters3D::get_included_regions() const {
TypedArray<RID> r_regions;
r_regions.resize(_included_regions.size());
for (uint32_t i = 0; i < _included_regions.size(); i++) {
r_regions[i] = _included_regions[i];
}
return r_regions;
}
void NavigationPathQueryParameters3D::set_excluded_regions(const TypedArray<RID> &p_regions) {
_excluded_regions.resize(p_regions.size());
for (uint32_t i = 0; i < _excluded_regions.size(); i++) {
_excluded_regions[i] = p_regions[i];
}
}
TypedArray<RID> NavigationPathQueryParameters3D::get_excluded_regions() const {
TypedArray<RID> r_regions;
r_regions.resize(_excluded_regions.size());
for (uint32_t i = 0; i < _excluded_regions.size(); i++) {
r_regions[i] = _excluded_regions[i];
}
return r_regions;
}
void NavigationPathQueryParameters3D::set_path_return_max_length(float p_length) {
path_return_max_length = MAX(0.0, p_length);
}
float NavigationPathQueryParameters3D::get_path_return_max_length() const {
return path_return_max_length;
}
void NavigationPathQueryParameters3D::set_path_return_max_radius(float p_radius) {
path_return_max_radius = MAX(0.0, p_radius);
}
float NavigationPathQueryParameters3D::get_path_return_max_radius() const {
return path_return_max_radius;
}
void NavigationPathQueryParameters3D::set_path_search_max_polygons(int p_max_polygons) {
path_search_max_polygons = p_max_polygons;
}
int NavigationPathQueryParameters3D::get_path_search_max_polygons() const {
return path_search_max_polygons;
}
void NavigationPathQueryParameters3D::set_path_search_max_distance(float p_distance) {
path_search_max_distance = MAX(0.0, p_distance);
}
float NavigationPathQueryParameters3D::get_path_search_max_distance() const {
return path_search_max_distance;
}
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);
ClassDB::bind_method(D_METHOD("set_included_regions", "regions"), &NavigationPathQueryParameters3D::set_included_regions);
ClassDB::bind_method(D_METHOD("get_included_regions"), &NavigationPathQueryParameters3D::get_included_regions);
ClassDB::bind_method(D_METHOD("set_excluded_regions", "regions"), &NavigationPathQueryParameters3D::set_excluded_regions);
ClassDB::bind_method(D_METHOD("get_excluded_regions"), &NavigationPathQueryParameters3D::get_excluded_regions);
ClassDB::bind_method(D_METHOD("set_path_return_max_length", "length"), &NavigationPathQueryParameters3D::set_path_return_max_length);
ClassDB::bind_method(D_METHOD("get_path_return_max_length"), &NavigationPathQueryParameters3D::get_path_return_max_length);
ClassDB::bind_method(D_METHOD("set_path_return_max_radius", "radius"), &NavigationPathQueryParameters3D::set_path_return_max_radius);
ClassDB::bind_method(D_METHOD("get_path_return_max_radius"), &NavigationPathQueryParameters3D::get_path_return_max_radius);
ClassDB::bind_method(D_METHOD("set_path_search_max_polygons", "max_polygons"), &NavigationPathQueryParameters3D::set_path_search_max_polygons);
ClassDB::bind_method(D_METHOD("get_path_search_max_polygons"), &NavigationPathQueryParameters3D::get_path_search_max_polygons);
ClassDB::bind_method(D_METHOD("set_path_search_max_distance", "distance"), &NavigationPathQueryParameters3D::set_path_search_max_distance);
ClassDB::bind_method(D_METHOD("get_path_search_max_distance"), &NavigationPathQueryParameters3D::get_path_search_max_distance);
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,None"), "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");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "excluded_regions", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_excluded_regions", "get_excluded_regions");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "included_regions", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_included_regions", "get_included_regions");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_return_max_length"), "set_path_return_max_length", "get_path_return_max_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_return_max_radius"), "set_path_return_max_radius", "get_path_return_max_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_search_max_polygons"), "set_path_search_max_polygons", "get_path_search_max_polygons");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_search_max_distance"), "set_path_search_max_distance", "get_path_search_max_distance");
BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED);
BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_NONE);
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,130 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/object/ref_counted.h"
#include "servers/navigation/navigation_globals.h"
#include "servers/navigation/navigation_utilities.h"
class NavigationPathQueryParameters3D : public RefCounted {
GDCLASS(NavigationPathQueryParameters3D, RefCounted);
protected:
static void _bind_methods();
public:
enum PathfindingAlgorithm {
PATHFINDING_ALGORITHM_ASTAR = NavigationUtilities::PATHFINDING_ALGORITHM_ASTAR,
};
enum PathPostProcessing {
PATH_POSTPROCESSING_CORRIDORFUNNEL = NavigationUtilities::PATH_POSTPROCESSING_CORRIDORFUNNEL,
PATH_POSTPROCESSING_EDGECENTERED = NavigationUtilities::PATH_POSTPROCESSING_EDGECENTERED,
PATH_POSTPROCESSING_NONE = NavigationUtilities::PATH_POSTPROCESSING_NONE,
};
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
};
private:
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_METADATA_INCLUDE_ALL;
bool simplify_path = false;
real_t simplify_epsilon = 0.0;
LocalVector<RID> _excluded_regions;
LocalVector<RID> _included_regions;
float path_return_max_length = 0.0;
float path_return_max_radius = 0.0;
int path_search_max_polygons = NavigationDefaults3D::path_search_max_polygons;
float path_search_max_distance = 0.0;
public:
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(RID p_map);
RID get_map() const;
void set_start_position(Vector3 p_start_position);
Vector3 get_start_position() const;
void set_target_position(Vector3 p_target_position);
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;
void set_excluded_regions(const TypedArray<RID> &p_regions);
TypedArray<RID> get_excluded_regions() const;
void set_included_regions(const TypedArray<RID> &p_regions);
TypedArray<RID> get_included_regions() const;
void set_path_return_max_length(float p_length);
float get_path_return_max_length() const;
void set_path_return_max_radius(float p_radius);
float get_path_return_max_radius() const;
void set_path_search_max_polygons(int p_max_polygons);
int get_path_search_max_polygons() const;
void set_path_search_max_distance(float p_distance);
float get_path_search_max_distance() const;
};
VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathfindingAlgorithm);
VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathPostProcessing);
VARIANT_BITFIELD_CAST(NavigationPathQueryParameters3D::PathMetadataFlags);

View File

@@ -0,0 +1,147 @@
/**************************************************************************/
/* 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::set_data(const LocalVector<Vector2> &p_path, const LocalVector<int32_t> &p_path_types, const LocalVector<RID> &p_path_rids, const LocalVector<int64_t> &p_path_owner_ids) {
path.clear();
path_types.clear();
path_rids.clear();
path_owner_ids.clear();
{
path.resize(p_path.size());
Vector2 *w = path.ptrw();
const Vector2 *r = p_path.ptr();
for (uint32_t i = 0; i < p_path.size(); i++) {
w[i] = r[i];
}
}
{
path_types.resize(p_path_types.size());
int32_t *w = path_types.ptrw();
const int32_t *r = p_path_types.ptr();
for (uint32_t i = 0; i < p_path_types.size(); i++) {
w[i] = r[i];
}
}
{
path_rids.resize(p_path_rids.size());
for (uint32_t i = 0; i < p_path_rids.size(); i++) {
path_rids[i] = p_path_rids[i];
}
}
{
path_owner_ids.resize(p_path_owner_ids.size());
int64_t *w = path_owner_ids.ptrw();
const int64_t *r = p_path_owner_ids.ptr();
for (uint32_t i = 0; i < p_path_owner_ids.size(); i++) {
w[i] = r[i];
}
}
}
void NavigationPathQueryResult2D::set_path_length(float p_length) {
path_length = p_length;
}
float NavigationPathQueryResult2D::get_path_length() const {
return path_length;
}
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("set_path_length", "length"), &NavigationPathQueryResult2D::set_path_length);
ClassDB::bind_method(D_METHOD("get_path_length"), &NavigationPathQueryResult2D::get_path_length);
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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_length"), "set_path_length", "get_path_length");
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_REGION);
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_LINK);
}

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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;
float path_length = 0.0;
protected:
static void _bind_methods();
public:
enum PathSegmentType {
PATH_SEGMENT_TYPE_REGION = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION,
PATH_SEGMENT_TYPE_LINK = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK,
};
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 set_path_length(float p_length);
float get_path_length() const;
void reset();
void set_data(const LocalVector<Vector2> &p_path, const LocalVector<int32_t> &p_path_types, const LocalVector<RID> &p_path_rids, const LocalVector<int64_t> &p_path_owner_ids);
};
VARIANT_ENUM_CAST(NavigationPathQueryResult2D::PathSegmentType);

View File

@@ -0,0 +1,147 @@
/**************************************************************************/
/* 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::set_data(const LocalVector<Vector3> &p_path, const LocalVector<int32_t> &p_path_types, const LocalVector<RID> &p_path_rids, const LocalVector<int64_t> &p_path_owner_ids) {
path.clear();
path_types.clear();
path_rids.clear();
path_owner_ids.clear();
{
path.resize(p_path.size());
Vector3 *w = path.ptrw();
const Vector3 *r = p_path.ptr();
for (uint32_t i = 0; i < p_path.size(); i++) {
w[i] = r[i];
}
}
{
path_types.resize(p_path_types.size());
int32_t *w = path_types.ptrw();
const int32_t *r = p_path_types.ptr();
for (uint32_t i = 0; i < p_path_types.size(); i++) {
w[i] = r[i];
}
}
{
path_rids.resize(p_path_rids.size());
for (uint32_t i = 0; i < p_path_rids.size(); i++) {
path_rids[i] = p_path_rids[i];
}
}
{
path_owner_ids.resize(p_path_owner_ids.size());
int64_t *w = path_owner_ids.ptrw();
const int64_t *r = p_path_owner_ids.ptr();
for (uint32_t i = 0; i < p_path_owner_ids.size(); i++) {
w[i] = r[i];
}
}
}
void NavigationPathQueryResult3D::set_path_length(float p_length) {
path_length = p_length;
}
float NavigationPathQueryResult3D::get_path_length() const {
return path_length;
}
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("set_path_length", "length"), &NavigationPathQueryResult3D::set_path_length);
ClassDB::bind_method(D_METHOD("get_path_length"), &NavigationPathQueryResult3D::get_path_length);
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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_length"), "set_path_length", "get_path_length");
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_REGION);
BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_LINK);
}

View File

@@ -0,0 +1,75 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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;
float path_length = 0.0;
protected:
static void _bind_methods();
public:
enum PathSegmentType {
PATH_SEGMENT_TYPE_REGION = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION,
PATH_SEGMENT_TYPE_LINK = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK,
};
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 set_path_length(float p_length);
float get_path_length() const;
void reset();
void set_data(const LocalVector<Vector3> &p_path, const LocalVector<int32_t> &p_path_types, const LocalVector<RID> &p_path_rids, const LocalVector<int64_t> &p_path_owner_ids);
};
VARIANT_ENUM_CAST(NavigationPathQueryResult3D::PathSegmentType);

View File

@@ -0,0 +1,61 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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,
PATH_POSTPROCESSING_NONE,
};
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
};
} //namespace NavigationUtilities

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* navigation_server_2d.compat.inc */
/**************************************************************************/
/* 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 DISABLE_DEPRECATED
Vector<Vector2> NavigationServer2D::_map_get_path_bind_compat_100129(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
return const_cast<NavigationServer2D *>(this)->map_get_path(p_map, p_origin, p_destination, p_optimize, p_navigation_layers);
}
void NavigationServer2D::_query_path_bind_compat_100129(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const {
return const_cast<NavigationServer2D *>(this)->query_path(p_query_parameters, p_query_result, Callable());
}
void NavigationServer2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::_map_get_path_bind_compat_100129, DEFVAL(1));
ClassDB::bind_compatibility_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::_query_path_bind_compat_100129);
}
#endif // DISABLE_DEPRECATED

View File

@@ -0,0 +1,587 @@
/**************************************************************************/
/* 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 "navigation_server_2d.compat.inc"
#include "core/config/project_settings.h"
#include "scene/main/node.h"
#include "servers/navigation/navigation_globals.h"
#include "servers/navigation_server_2d_dummy.h"
NavigationServer2D *NavigationServer2D::singleton = nullptr;
RWLock NavigationServer2D::geometry_parser_rwlock;
RID_Owner<NavMeshGeometryParser2D> NavigationServer2D::geometry_parser_owner;
LocalVector<NavMeshGeometryParser2D *> NavigationServer2D::generator_parsers;
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_merge_rasterizer_cell_scale", "map", "scale"), &NavigationServer2D::map_set_merge_rasterizer_cell_scale);
ClassDB::bind_method(D_METHOD("map_get_merge_rasterizer_cell_scale", "map"), &NavigationServer2D::map_get_merge_rasterizer_cell_scale);
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_set_use_async_iterations", "map", "enabled"), &NavigationServer2D::map_set_use_async_iterations);
ClassDB::bind_method(D_METHOD("map_get_use_async_iterations", "map"), &NavigationServer2D::map_get_use_async_iterations);
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", "callback"), &NavigationServer2D::query_path, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
ClassDB::bind_method(D_METHOD("region_get_iteration_id", "region"), &NavigationServer2D::region_get_iteration_id);
ClassDB::bind_method(D_METHOD("region_set_use_async_iterations", "region", "enabled"), &NavigationServer2D::region_set_use_async_iterations);
ClassDB::bind_method(D_METHOD("region_get_use_async_iterations", "region"), &NavigationServer2D::region_get_use_async_iterations);
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_closest_point", "region", "to_point"), &NavigationServer2D::region_get_closest_point);
ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point);
ClassDB::bind_method(D_METHOD("region_get_bounds", "region"), &NavigationServer2D::region_get_bounds);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create);
ClassDB::bind_method(D_METHOD("link_get_iteration_id", "link"), &NavigationServer2D::link_get_iteration_id);
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_active", "active"), &NavigationServer2D::set_active);
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"));
ADD_SIGNAL(MethodInfo("avoidance_debug_changed"));
ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &NavigationServer2D::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);
BIND_ENUM_CONSTANT(INFO_OBSTACLE_COUNT);
}
NavigationServer2D *NavigationServer2D::get_singleton() {
return singleton;
}
NavigationServer2D::NavigationServer2D() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_cell_size", PROPERTY_HINT_RANGE, NavigationDefaults2D::NAV_MESH_CELL_SIZE_HINT), NavigationDefaults2D::NAV_MESH_CELL_SIZE);
GLOBAL_DEF("navigation/2d/use_edge_connections", true);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "navigation/2d/merge_rasterizer_cell_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001,or_greater"), 1.0);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_edge_connection_margin", PROPERTY_HINT_RANGE, "0.01,10,0.001,or_greater"), NavigationDefaults2D::EDGE_CONNECTION_MARGIN);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_link_connection_radius", PROPERTY_HINT_RANGE, "0.01,10,0.001,or_greater"), NavigationDefaults2D::LINK_CONNECTION_RADIUS);
#ifdef DEBUG_ENABLED
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/2d/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/2d/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
debug_navigation_geometry_face_color = GLOBAL_DEF("debug/shapes/navigation/2d/geometry_face_color", Color(0.5, 1.0, 1.0, 0.4));
debug_navigation_geometry_edge_disabled_color = GLOBAL_DEF("debug/shapes/navigation/2d/geometry_edge_disabled_color", Color(0.5, 0.5, 0.5, 1.0));
debug_navigation_geometry_face_disabled_color = GLOBAL_DEF("debug/shapes/navigation/2d/geometry_face_disabled_color", Color(0.5, 0.5, 0.5, 0.4));
debug_navigation_link_connection_color = GLOBAL_DEF("debug/shapes/navigation/2d/link_connection_color", Color(1.0, 0.5, 1.0, 1.0));
debug_navigation_link_connection_disabled_color = GLOBAL_DEF("debug/shapes/navigation/2d/link_connection_disabled_color", Color(0.5, 0.5, 0.5, 1.0));
debug_navigation_agent_path_color = GLOBAL_DEF("debug/shapes/navigation/2d/agent_path_color", Color(1.0, 0.0, 0.0, 1.0));
debug_navigation_enable_edge_connections = GLOBAL_DEF("debug/shapes/navigation/2d/enable_edge_connections", true);
debug_navigation_enable_edge_lines = GLOBAL_DEF("debug/shapes/navigation/2d/enable_edge_lines", true);
debug_navigation_enable_geometry_face_random_color = GLOBAL_DEF("debug/shapes/navigation/2d/enable_geometry_face_random_color", true);
debug_navigation_enable_link_connections = GLOBAL_DEF("debug/shapes/navigation/2d/enable_link_connections", true);
debug_navigation_enable_agent_paths = GLOBAL_DEF("debug/shapes/navigation/2d/enable_agent_paths", true);
debug_navigation_agent_path_point_size = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "debug/shapes/navigation/2d/agent_path_point_size", PROPERTY_HINT_RANGE, "0.01,10,0.001,or_greater"), 4.0);
debug_navigation_avoidance_agents_radius_color = GLOBAL_DEF("debug/shapes/avoidance/2d/agents_radius_color", Color(1.0, 1.0, 0.0, 0.25));
debug_navigation_avoidance_obstacles_radius_color = GLOBAL_DEF("debug/shapes/avoidance/2d/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/2d/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/2d/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/2d/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/2d/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/2d/enable_agents_radius", true);
debug_navigation_avoidance_enable_obstacles_radius = GLOBAL_DEF("debug/shapes/avoidance/2d/enable_obstacles_radius", true);
debug_navigation_avoidance_enable_obstacles_static = GLOBAL_DEF("debug/shapes/avoidance/2d/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
}
NavigationServer2D::~NavigationServer2D() {
singleton = nullptr;
RWLockWrite write_lock(geometry_parser_rwlock);
for (NavMeshGeometryParser2D *parser : generator_parsers) {
geometry_parser_owner.free(parser->self);
}
generator_parsers.clear();
}
RID NavigationServer2D::source_geometry_parser_create() {
RWLockWrite write_lock(geometry_parser_rwlock);
RID rid = geometry_parser_owner.make_rid();
NavMeshGeometryParser2D *parser = geometry_parser_owner.get_or_null(rid);
parser->self = rid;
generator_parsers.push_back(parser);
return rid;
}
void NavigationServer2D::free(RID p_object) {
if (!geometry_parser_owner.owns(p_object)) {
return;
}
RWLockWrite write_lock(geometry_parser_rwlock);
NavMeshGeometryParser2D *parser = geometry_parser_owner.get_or_null(p_object);
ERR_FAIL_NULL(parser);
generator_parsers.erase(parser);
geometry_parser_owner.free(parser->self);
}
void NavigationServer2D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
RWLockWrite write_lock(geometry_parser_rwlock);
NavMeshGeometryParser2D *parser = geometry_parser_owner.get_or_null(p_parser);
ERR_FAIL_NULL(parser);
parser->callback = p_callback;
}
void NavigationServer2D::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, &NavigationServer2D::_emit_navigation_debug_changed_signal).call_deferred();
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer2D::_emit_avoidance_debug_changed_signal).call_deferred();
}
#endif // DEBUG_ENABLED
}
bool NavigationServer2D::get_debug_enabled() const {
return debug_enabled;
}
#ifdef DEBUG_ENABLED
void NavigationServer2D::_emit_navigation_debug_changed_signal() {
if (navigation_debug_dirty) {
navigation_debug_dirty = false;
emit_signal(SNAME("navigation_debug_changed"));
}
}
void NavigationServer2D::_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
void NavigationServer2D::set_debug_navigation_enabled(bool p_enabled) {
debug_navigation_enabled = p_enabled;
navigation_debug_dirty = true;
callable_mp(this, &NavigationServer2D::_emit_navigation_debug_changed_signal).call_deferred();
}
bool NavigationServer2D::get_debug_navigation_enabled() const {
return debug_navigation_enabled;
}
void NavigationServer2D::set_debug_avoidance_enabled(bool p_enabled) {
debug_avoidance_enabled = p_enabled;
avoidance_debug_dirty = true;
callable_mp(this, &NavigationServer2D::_emit_avoidance_debug_changed_signal).call_deferred();
}
bool NavigationServer2D::get_debug_avoidance_enabled() const {
return debug_avoidance_enabled;
}
void NavigationServer2D::set_debug_navigation_edge_connection_color(const Color &p_color) {
debug_navigation_edge_connection_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_edge_connection_color() const {
return debug_navigation_edge_connection_color;
}
void NavigationServer2D::set_debug_navigation_geometry_face_color(const Color &p_color) {
debug_navigation_geometry_face_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_geometry_face_color() const {
return debug_navigation_geometry_face_color;
}
void NavigationServer2D::set_debug_navigation_geometry_face_disabled_color(const Color &p_color) {
debug_navigation_geometry_face_disabled_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_geometry_face_disabled_color() const {
return debug_navigation_geometry_face_disabled_color;
}
void NavigationServer2D::set_debug_navigation_link_connection_color(const Color &p_color) {
debug_navigation_link_connection_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_link_connection_color() const {
return debug_navigation_link_connection_color;
}
void NavigationServer2D::set_debug_navigation_link_connection_disabled_color(const Color &p_color) {
debug_navigation_link_connection_disabled_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_link_connection_disabled_color() const {
return debug_navigation_link_connection_disabled_color;
}
void NavigationServer2D::set_debug_navigation_geometry_edge_color(const Color &p_color) {
debug_navigation_geometry_edge_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_geometry_edge_color() const {
return debug_navigation_geometry_edge_color;
}
void NavigationServer2D::set_debug_navigation_geometry_edge_disabled_color(const Color &p_color) {
debug_navigation_geometry_edge_disabled_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_geometry_edge_disabled_color() const {
return debug_navigation_geometry_edge_disabled_color;
}
void NavigationServer2D::set_debug_navigation_enable_edge_connections(const bool p_value) {
debug_navigation_enable_edge_connections = p_value;
}
bool NavigationServer2D::get_debug_navigation_enable_edge_connections() const {
return debug_navigation_enable_edge_connections;
}
void NavigationServer2D::set_debug_navigation_enable_geometry_face_random_color(const bool p_value) {
debug_navigation_enable_geometry_face_random_color = p_value;
}
bool NavigationServer2D::get_debug_navigation_enable_geometry_face_random_color() const {
return debug_navigation_enable_geometry_face_random_color;
}
void NavigationServer2D::set_debug_navigation_enable_edge_lines(const bool p_value) {
debug_navigation_enable_edge_lines = p_value;
}
bool NavigationServer2D::get_debug_navigation_enable_edge_lines() const {
return debug_navigation_enable_edge_lines;
}
void NavigationServer2D::set_debug_navigation_agent_path_color(const Color &p_color) {
debug_navigation_agent_path_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_agent_path_color() const {
return debug_navigation_agent_path_color;
}
void NavigationServer2D::set_debug_navigation_enable_agent_paths(const bool p_value) {
debug_navigation_enable_agent_paths = p_value;
}
bool NavigationServer2D::get_debug_navigation_enable_agent_paths() const {
return debug_navigation_enable_agent_paths;
}
void NavigationServer2D::set_debug_navigation_agent_path_point_size(real_t p_point_size) {
debug_navigation_agent_path_point_size = p_point_size;
}
real_t NavigationServer2D::get_debug_navigation_agent_path_point_size() const {
return debug_navigation_agent_path_point_size;
}
void NavigationServer2D::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
debug_navigation_avoidance_enable_agents_radius = p_value;
}
bool NavigationServer2D::get_debug_navigation_avoidance_enable_agents_radius() const {
return debug_navigation_avoidance_enable_agents_radius;
}
void NavigationServer2D::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
debug_navigation_avoidance_enable_obstacles_radius = p_value;
}
bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_radius() const {
return debug_navigation_avoidance_enable_obstacles_radius;
}
void NavigationServer2D::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
debug_navigation_avoidance_agents_radius_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_avoidance_agents_radius_color() const {
return debug_navigation_avoidance_agents_radius_color;
}
void NavigationServer2D::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
debug_navigation_avoidance_obstacles_radius_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_avoidance_obstacles_radius_color() const {
return debug_navigation_avoidance_obstacles_radius_color;
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushin_face_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
return debug_navigation_avoidance_static_obstacle_pushin_face_color;
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushout_face_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
return debug_navigation_avoidance_static_obstacle_pushout_face_color;
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushin_edge_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
return debug_navigation_avoidance_static_obstacle_pushin_edge_color;
}
void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
debug_navigation_avoidance_static_obstacle_pushout_edge_color = p_color;
}
Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
return debug_navigation_avoidance_static_obstacle_pushout_edge_color;
}
void NavigationServer2D::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
debug_navigation_avoidance_enable_obstacles_static = p_value;
}
bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_static() const {
return debug_navigation_avoidance_enable_obstacles_static;
}
#endif // DEBUG_ENABLED
///////////////////////////////////////////////////////
static NavigationServer2D *navigation_server_2d = nullptr;
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();
}
void NavigationServer2DManager::initialize_server() {
ERR_FAIL_COND(navigation_server_2d != nullptr);
// Init 2D Navigation Server
navigation_server_2d = NavigationServer2DManager::new_default_server();
if (!navigation_server_2d) {
WARN_VERBOSE("Failed to initialize NavigationServer2D. Fall back to dummy server.");
navigation_server_2d = memnew(NavigationServer2DDummy);
}
ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D.");
navigation_server_2d->init();
}
void NavigationServer2DManager::finalize_server() {
ERR_FAIL_NULL(navigation_server_2d);
navigation_server_2d->finish();
memdelete(navigation_server_2d);
navigation_server_2d = nullptr;
}

View File

@@ -0,0 +1,445 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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
struct NavMeshGeometryParser2D {
RID self;
Callable callback;
};
class NavigationServer2D : public Object {
GDCLASS(NavigationServer2D, Object);
static NavigationServer2D *singleton;
protected:
static void _bind_methods();
public:
static NavigationServer2D *get_singleton();
virtual TypedArray<RID> get_maps() const = 0;
/* MAP API */
virtual RID map_create() = 0;
virtual void map_set_active(RID p_map, bool p_active) = 0;
virtual bool map_is_active(RID p_map) const = 0;
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) = 0;
virtual real_t map_get_cell_size(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;
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) = 0;
virtual real_t map_get_link_connection_radius(RID p_map) const = 0;
virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) = 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 void map_set_use_async_iterations(RID p_map, bool p_enabled) = 0;
virtual bool map_get_use_async_iterations(RID p_map) const = 0;
virtual Vector2 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
/* REGION API */
virtual RID region_create() = 0;
virtual uint32_t region_get_iteration_id(RID p_region) const = 0;
virtual void region_set_use_async_iterations(RID p_region, bool p_enabled) = 0;
virtual bool region_get_use_async_iterations(RID p_region) const = 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;
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;
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;
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;
virtual void region_set_map(RID p_region, RID p_map) = 0;
virtual RID region_get_map(RID p_region) const = 0;
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;
virtual void region_set_transform(RID p_region, Transform2D p_transform) = 0;
virtual Transform2D region_get_transform(RID p_region) const = 0;
virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) = 0;
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_closest_point(RID p_region, const Vector2 &p_point) const = 0;
virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
virtual Rect2 region_get_bounds(RID p_region) const = 0;
/* LINK API */
virtual RID link_create() = 0;
virtual uint32_t link_get_iteration_id(RID p_link) const = 0;
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;
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const = 0;
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;
virtual void link_set_start_position(RID p_link, Vector2 p_position) = 0;
virtual Vector2 link_get_start_position(RID p_link) const = 0;
virtual void link_set_end_position(RID p_link, Vector2 p_position) = 0;
virtual Vector2 link_get_end_position(RID p_link) const = 0;
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;
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;
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;
/* AGENT API */
virtual RID agent_create() = 0;
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_neighbor_distance(RID p_agent, real_t p_distance) = 0;
virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0;
virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
virtual int agent_get_max_neighbors(RID p_agent) const = 0;
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;
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_max_speed(RID p_agent, real_t p_max_speed) = 0;
virtual real_t agent_get_max_speed(RID p_agent) const = 0;
virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) = 0;
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0;
virtual Vector2 agent_get_velocity(RID p_agent) const = 0;
virtual void agent_set_position(RID p_agent, Vector2 p_position) = 0;
virtual Vector2 agent_get_position(RID p_agent) const = 0;
virtual bool agent_is_map_changed(RID p_agent) const = 0;
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;
/* OBSTACLE API */
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;
/* QUERY API */
virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result, const Callable &p_callback = Callable()) = 0;
/* NAVMESH BAKE API */
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;
protected:
static RWLock geometry_parser_rwlock;
static RID_Owner<NavMeshGeometryParser2D> geometry_parser_owner;
static LocalVector<NavMeshGeometryParser2D *> generator_parsers;
public:
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;
/* SERVER API */
virtual void set_active(bool p_active) = 0;
virtual void process(double p_delta_time) = 0;
virtual void physics_process(double p_delta_time) = 0;
virtual void init() = 0;
virtual void sync() = 0;
virtual void finish() = 0;
virtual void free(RID p_object) = 0;
NavigationServer2D();
~NavigationServer2D() override;
/* DEBUG API */
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,
INFO_OBSTACLE_COUNT,
};
virtual int get_process_info(ProcessInfo p_info) const = 0;
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
protected:
#ifndef DISABLE_DEPRECATED
Vector<Vector2> _map_get_path_bind_compat_100129(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
void _query_path_bind_compat_100129(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const;
static void _bind_compatibility_methods();
#endif
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_lines = true;
bool debug_navigation_enable_geometry_face_random_color = true;
bool debug_navigation_enable_link_connections = true;
bool debug_navigation_enable_agent_paths = 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;
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_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
};
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();
static void initialize_server();
static void finalize_server();
};
VARIANT_ENUM_CAST(NavigationServer2D::ProcessInfo);

View File

@@ -0,0 +1,194 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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_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<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) 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; }
void map_set_use_async_iterations(RID p_map, bool p_enabled) override {}
bool map_get_use_async_iterations(RID p_map) const override { return false; }
RID region_create() override { return RID(); }
uint32_t region_get_iteration_id(RID p_region) const override { return 0; }
void region_set_use_async_iterations(RID p_region, bool p_enabled) override {}
bool region_get_use_async_iterations(RID p_region) const override { return false; }
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_closest_point(RID p_region, const Vector2 &p_point) const override { return Vector2(); }
Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); }
Rect2 region_get_bounds(RID p_region) const override { return Rect2(); }
RID link_create() override { return RID(); }
uint32_t link_get_iteration_id(RID p_link) const override { return 0; }
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 Callable &p_callback = Callable()) override {}
void set_active(bool p_active) override {}
void process(double p_delta_time) override {}
void physics_process(double p_delta_time) override {}
void init() override {}
void sync() override {}
void finish() override {}
int get_process_info(ProcessInfo p_info) const override { return 0; }
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; }
};

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* navigation_server_3d.compat.inc */
/**************************************************************************/
/* 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 DISABLE_DEPRECATED
Vector<Vector3> NavigationServer3D::_map_get_path_bind_compat_100129(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
return const_cast<NavigationServer3D *>(this)->map_get_path(p_map, p_origin, p_destination, p_optimize, p_navigation_layers);
}
void NavigationServer3D::_query_path_bind_compat_100129(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const {
return const_cast<NavigationServer3D *>(this)->query_path(p_query_parameters, p_query_result, Callable());
}
void NavigationServer3D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer3D::_map_get_path_bind_compat_100129, DEFVAL(1));
ClassDB::bind_compatibility_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::_query_path_bind_compat_100129);
}
#endif // DISABLE_DEPRECATED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,531 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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"
struct NavMeshGeometryParser3D {
RID self;
Callable callback;
};
class NavigationServer3D : public Object {
GDCLASS(NavigationServer3D, Object);
static NavigationServer3D *singleton;
protected:
static void _bind_methods();
public:
static NavigationServer3D *get_singleton();
virtual TypedArray<RID> get_maps() const = 0;
/* MAP API */
virtual RID map_create() = 0;
virtual void map_set_active(RID p_map, bool p_active) = 0;
virtual bool map_is_active(RID p_map) const = 0;
virtual void map_set_up(RID p_map, Vector3 p_up) = 0;
virtual Vector3 map_get_up(RID p_map) const = 0;
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) = 0;
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;
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) = 0;
virtual real_t map_get_link_connection_radius(RID p_map) const = 0;
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) = 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 void map_set_use_async_iterations(RID p_map, bool p_enabled) = 0;
virtual bool map_get_use_async_iterations(RID p_map) const = 0;
virtual Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
/* REGION API */
virtual RID region_create() = 0;
virtual uint32_t region_get_iteration_id(RID p_region) const = 0;
virtual void region_set_use_async_iterations(RID p_region, bool p_enabled) = 0;
virtual bool region_get_use_async_iterations(RID p_region) const = 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;
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;
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;
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;
virtual void region_set_map(RID p_region, RID p_map) = 0;
virtual RID region_get_map(RID p_region) const = 0;
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;
virtual void region_set_transform(RID p_region, Transform3D p_transform) = 0;
virtual Transform3D region_get_transform(RID p_region) const = 0;
virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) = 0;
#ifndef DISABLE_DEPRECATED
virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) = 0;
#endif // DISABLE_DEPRECATED
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_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const = 0;
virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const = 0;
virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const = 0;
virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
virtual AABB region_get_bounds(RID p_region) const = 0;
/* LINK API */
virtual RID link_create() = 0;
virtual uint32_t link_get_iteration_id(RID p_link) const = 0;
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;
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const = 0;
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;
virtual void link_set_start_position(RID p_link, Vector3 p_position) = 0;
virtual Vector3 link_get_start_position(RID p_link) const = 0;
virtual void link_set_end_position(RID p_link, Vector3 p_position) = 0;
virtual Vector3 link_get_end_position(RID p_link) const = 0;
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;
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;
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;
/* AGENT API */
virtual RID agent_create() = 0;
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;
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;
virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
virtual int agent_get_max_neighbors(RID p_agent) const = 0;
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;
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;
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;
virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) = 0;
virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
virtual Vector3 agent_get_velocity(RID p_agent) const = 0;
virtual void agent_set_position(RID p_agent, Vector3 p_position) = 0;
virtual Vector3 agent_get_position(RID p_agent) const = 0;
virtual bool agent_is_map_changed(RID p_agent) const = 0;
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;
/* OBSTACLE API */
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;
/* QUERY API */
virtual void query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result, const Callable &p_callback = Callable()) = 0;
/* NAVMESH BAKE API */
#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;
virtual String get_baking_navigation_mesh_state_msg(Ref<NavigationMesh> p_navigation_mesh) const = 0;
#endif // _3D_DISABLED
protected:
static RWLock geometry_parser_rwlock;
static RID_Owner<NavMeshGeometryParser3D> geometry_parser_owner;
static LocalVector<NavMeshGeometryParser3D *> generator_parsers;
public:
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;
/* SERVER API */
virtual void set_active(bool p_active) = 0;
virtual void process(double p_delta_time) = 0;
virtual void physics_process(double p_delta_time) = 0;
virtual void init() = 0;
virtual void sync() = 0;
virtual void finish() = 0;
virtual void free(RID p_object) = 0;
NavigationServer3D();
~NavigationServer3D() override;
/* DEBUG API */
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,
INFO_OBSTACLE_COUNT,
};
virtual int get_process_info(ProcessInfo p_info) const = 0;
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
protected:
#ifndef DISABLE_DEPRECATED
Vector<Vector3> _map_get_path_bind_compat_100129(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
void _query_path_bind_compat_100129(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const;
static void _bind_compatibility_methods();
#endif
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();
static void initialize_server();
static void finalize_server();
};
VARIANT_ENUM_CAST(NavigationServer3D::ProcessInfo);

View File

@@ -0,0 +1,214 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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) 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; }
void map_set_use_async_iterations(RID p_map, bool p_enabled) override {}
bool map_get_use_async_iterations(RID p_map) const override { return false; }
RID region_create() override { return RID(); }
uint32_t region_get_iteration_id(RID p_region) const override { return 0; }
void region_set_use_async_iterations(RID p_region, bool p_enabled) override {}
bool region_get_use_async_iterations(RID p_region) const override { return false; }
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_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const override { return Vector3(); }
Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override { return Vector3(); }
Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override { return Vector3(); }
Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); }
AABB region_get_bounds(RID p_region) const override { return AABB(); }
RID link_create() override { return RID(); }
uint32_t link_get_iteration_id(RID p_link) const override { return 0; }
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; }
virtual void query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result, const Callable &p_callback = Callable()) override {}
#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; }
String get_baking_navigation_mesh_state_msg(Ref<NavigationMesh> p_navigation_mesh) const override { return ""; }
#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(double p_delta_time) override {}
void physics_process(double p_delta_time) override {}
void init() override {}
void sync() override {}
void finish() override {}
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; }
};

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