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
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:
41
servers/SCsub
Normal file
41
servers/SCsub
Normal 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
8
servers/audio/SCsub
Normal 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")
|
||||
150
servers/audio/audio_driver_dummy.cpp
Normal file
150
servers/audio/audio_driver_dummy.cpp
Normal 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;
|
||||
}
|
||||
86
servers/audio/audio_driver_dummy.h
Normal file
86
servers/audio/audio_driver_dummy.h
Normal 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() {}
|
||||
};
|
||||
59
servers/audio/audio_effect.cpp
Normal file
59
servers/audio/audio_effect.cpp
Normal 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() {
|
||||
}
|
||||
61
servers/audio/audio_effect.h
Normal file
61
servers/audio/audio_effect.h
Normal 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();
|
||||
};
|
||||
262
servers/audio/audio_filter_sw.cpp
Normal file
262
servers/audio/audio_filter_sw.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
123
servers/audio/audio_filter_sw.h
Normal file
123
servers/audio/audio_filter_sw.h
Normal 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;
|
||||
}
|
||||
260
servers/audio/audio_rb_resampler.cpp
Normal file
260
servers/audio/audio_rb_resampler.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
185
servers/audio/audio_rb_resampler.h
Normal file
185
servers/audio/audio_rb_resampler.h
Normal 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();
|
||||
};
|
||||
882
servers/audio/audio_stream.cpp
Normal file
882
servers/audio/audio_stream.cpp
Normal 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);
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
376
servers/audio/audio_stream.h
Normal file
376
servers/audio/audio_stream.h
Normal 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);
|
||||
6
servers/audio/effects/SCsub
Normal file
6
servers/audio/effects/SCsub
Normal 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")
|
||||
83
servers/audio/effects/audio_effect_amplify.cpp
Normal file
83
servers/audio/effects/audio_effect_amplify.cpp
Normal 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;
|
||||
}
|
||||
66
servers/audio/effects/audio_effect_amplify.h
Normal file
66
servers/audio/effects/audio_effect_amplify.h
Normal 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();
|
||||
};
|
||||
140
servers/audio/effects/audio_effect_capture.cpp
Normal file
140
servers/audio/effects/audio_effect_capture.cpp
Normal 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;
|
||||
}
|
||||
77
servers/audio/effects/audio_effect_capture.h
Normal file
77
servers/audio/effects/audio_effect_capture.h
Normal 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;
|
||||
};
|
||||
360
servers/audio/effects/audio_effect_chorus.cpp
Normal file
360
servers/audio/effects/audio_effect_chorus.cpp
Normal 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;
|
||||
}
|
||||
131
servers/audio/effects/audio_effect_chorus.h
Normal file
131
servers/audio/effects/audio_effect_chorus.h
Normal 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();
|
||||
};
|
||||
240
servers/audio/effects/audio_effect_compressor.cpp
Normal file
240
servers/audio/effects/audio_effect_compressor.cpp
Normal 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;
|
||||
}
|
||||
91
servers/audio/effects/audio_effect_compressor.h
Normal file
91
servers/audio/effects/audio_effect_compressor.h
Normal 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();
|
||||
};
|
||||
307
servers/audio/effects/audio_effect_delay.cpp
Normal file
307
servers/audio/effects/audio_effect_delay.cpp
Normal 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");
|
||||
}
|
||||
132
servers/audio/effects/audio_effect_delay.h
Normal file
132
servers/audio/effects/audio_effect_delay.h
Normal 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() {}
|
||||
};
|
||||
181
servers/audio/effects/audio_effect_distortion.cpp
Normal file
181
servers/audio/effects/audio_effect_distortion.cpp
Normal 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;
|
||||
}
|
||||
90
servers/audio/effects/audio_effect_distortion.h
Normal file
90
servers/audio/effects/audio_effect_distortion.h
Normal 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)
|
||||
135
servers/audio/effects/audio_effect_eq.cpp
Normal file
135
servers/audio/effects/audio_effect_eq.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
98
servers/audio/effects/audio_effect_eq.h
Normal file
98
servers/audio/effects/audio_effect_eq.h
Normal 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) {}
|
||||
};
|
||||
173
servers/audio/effects/audio_effect_filter.cpp
Normal file
173
servers/audio/effects/audio_effect_filter.cpp
Normal 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;
|
||||
}
|
||||
167
servers/audio/effects/audio_effect_filter.h
Normal file
167
servers/audio/effects/audio_effect_filter.h
Normal 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) {}
|
||||
};
|
||||
161
servers/audio/effects/audio_effect_hard_limiter.cpp
Normal file
161
servers/audio/effects/audio_effect_hard_limiter.cpp
Normal 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");
|
||||
}
|
||||
86
servers/audio/effects/audio_effect_hard_limiter.h
Normal file
86
servers/audio/effects/audio_effect_hard_limiter.h
Normal 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;
|
||||
};
|
||||
134
servers/audio/effects/audio_effect_limiter.cpp
Normal file
134
servers/audio/effects/audio_effect_limiter.cpp
Normal 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;
|
||||
}
|
||||
76
servers/audio/effects/audio_effect_limiter.h
Normal file
76
servers/audio/effects/audio_effect_limiter.h
Normal 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();
|
||||
};
|
||||
67
servers/audio/effects/audio_effect_panner.cpp
Normal file
67
servers/audio/effects/audio_effect_panner.cpp
Normal 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;
|
||||
}
|
||||
61
servers/audio/effects/audio_effect_panner.h
Normal file
61
servers/audio/effects/audio_effect_panner.h
Normal 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();
|
||||
};
|
||||
159
servers/audio/effects/audio_effect_phaser.cpp
Normal file
159
servers/audio/effects/audio_effect_phaser.cpp
Normal 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;
|
||||
}
|
||||
103
servers/audio/effects/audio_effect_phaser.h
Normal file
103
servers/audio/effects/audio_effect_phaser.h
Normal 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();
|
||||
};
|
||||
371
servers/audio/effects/audio_effect_pitch_shift.cpp
Normal file
371
servers/audio/effects/audio_effect_pitch_shift.cpp
Normal 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);
|
||||
}
|
||||
111
servers/audio/effects/audio_effect_pitch_shift.h
Normal file
111
servers/audio/effects/audio_effect_pitch_shift.h
Normal 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);
|
||||
294
servers/audio/effects/audio_effect_record.cpp
Normal file
294
servers/audio/effects/audio_effect_record.cpp
Normal 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();
|
||||
}
|
||||
95
servers/audio/effects/audio_effect_record.h
Normal file
95
servers/audio/effects/audio_effect_record.h
Normal 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();
|
||||
};
|
||||
199
servers/audio/effects/audio_effect_reverb.cpp
Normal file
199
servers/audio/effects/audio_effect_reverb.cpp
Normal 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;
|
||||
}
|
||||
94
servers/audio/effects/audio_effect_reverb.h
Normal file
94
servers/audio/effects/audio_effect_reverb.h
Normal 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();
|
||||
};
|
||||
283
servers/audio/effects/audio_effect_spectrum_analyzer.cpp
Normal file
283
servers/audio/effects/audio_effect_spectrum_analyzer.cpp
Normal 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;
|
||||
}
|
||||
104
servers/audio/effects/audio_effect_spectrum_analyzer.h
Normal file
104
servers/audio/effects/audio_effect_spectrum_analyzer.h
Normal 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);
|
||||
145
servers/audio/effects/audio_effect_stereo_enhance.cpp
Normal file
145
servers/audio/effects/audio_effect_stereo_enhance.cpp
Normal 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() {}
|
||||
82
servers/audio/effects/audio_effect_stereo_enhance.h
Normal file
82
servers/audio/effects/audio_effect_stereo_enhance.h
Normal 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();
|
||||
};
|
||||
244
servers/audio/effects/audio_stream_generator.cpp
Normal file
244
servers/audio/effects/audio_stream_generator.cpp
Normal 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;
|
||||
}
|
||||
113
servers/audio/effects/audio_stream_generator.h
Normal file
113
servers/audio/effects/audio_stream_generator.h
Normal 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);
|
||||
201
servers/audio/effects/eq_filter.cpp
Normal file
201
servers/audio/effects/eq_filter.cpp
Normal 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() {
|
||||
}
|
||||
98
servers/audio/effects/eq_filter.h
Normal file
98
servers/audio/effects/eq_filter.h
Normal 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;
|
||||
}
|
||||
340
servers/audio/effects/reverb_filter.cpp
Normal file
340
servers/audio/effects/reverb_filter.cpp
Normal 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();
|
||||
}
|
||||
115
servers/audio/effects/reverb_filter.h
Normal file
115
servers/audio/effects/reverb_filter.h
Normal 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
2183
servers/audio_server.cpp
Normal file
File diff suppressed because it is too large
Load Diff
558
servers/audio_server.h
Normal file
558
servers/audio_server.h
Normal 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
6
servers/camera/SCsub
Normal 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")
|
||||
317
servers/camera/camera_feed.cpp
Normal file
317
servers/camera/camera_feed.cpp
Normal 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;
|
||||
}
|
||||
132
servers/camera/camera_feed.h
Normal file
132
servers/camera/camera_feed.h
Normal 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
186
servers/camera_server.cpp
Normal 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
118
servers/camera_server.h
Normal 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
6
servers/debugger/SCsub
Normal 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")
|
||||
538
servers/debugger/servers_debugger.cpp
Normal file
538
servers/debugger/servers_debugger.cpp
Normal 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;
|
||||
}
|
||||
129
servers/debugger/servers_debugger.h
Normal file
129
servers/debugger/servers_debugger.h
Normal 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
6
servers/display/SCsub
Normal 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")
|
||||
451
servers/display/native_menu.cpp
Normal file
451
servers/display/native_menu.cpp
Normal 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.");
|
||||
}
|
||||
157
servers/display/native_menu.h
Normal file
157
servers/display/native_menu.h
Normal 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);
|
||||
46
servers/display_server.compat.inc
Normal file
46
servers/display_server.compat.inc
Normal 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
2148
servers/display_server.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1013
servers/display_server.h
Normal file
1013
servers/display_server.h
Normal file
File diff suppressed because it is too large
Load Diff
211
servers/display_server_headless.h
Normal file
211
servers/display_server_headless.h
Normal 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
11
servers/extensions/SCsub
Normal 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")
|
||||
360
servers/extensions/physics_server_2d_extension.cpp
Normal file
360
servers/extensions/physics_server_2d_extension.cpp
Normal 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() {
|
||||
}
|
||||
460
servers/extensions/physics_server_2d_extension.h
Normal file
460
servers/extensions/physics_server_2d_extension.h
Normal 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();
|
||||
};
|
||||
447
servers/extensions/physics_server_3d_extension.cpp
Normal file
447
servers/extensions/physics_server_3d_extension.cpp
Normal 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() {
|
||||
}
|
||||
555
servers/extensions/physics_server_3d_extension.h
Normal file
555
servers/extensions/physics_server_3d_extension.h
Normal 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();
|
||||
};
|
||||
6
servers/movie_writer/SCsub
Normal file
6
servers/movie_writer/SCsub
Normal 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")
|
||||
261
servers/movie_writer/movie_writer.cpp
Normal file
261
servers/movie_writer/movie_writer.cpp
Normal 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("--------------------------------------------------------------------------------");
|
||||
}
|
||||
91
servers/movie_writer/movie_writer.h
Normal file
91
servers/movie_writer/movie_writer.h
Normal 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();
|
||||
};
|
||||
170
servers/movie_writer/movie_writer_pngwav.cpp
Normal file
170
servers/movie_writer/movie_writer_pngwav.cpp
Normal 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")));
|
||||
}
|
||||
68
servers/movie_writer/movie_writer_pngwav.h
Normal file
68
servers/movie_writer/movie_writer_pngwav.h
Normal 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
12
servers/navigation/SCsub
Normal 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")
|
||||
158
servers/navigation/nav_heap.h
Normal file
158
servers/navigation/nav_heap.h
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
86
servers/navigation/navigation_globals.h
Normal file
86
servers/navigation/navigation_globals.h
Normal 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
|
||||
242
servers/navigation/navigation_path_query_parameters_2d.cpp
Normal file
242
servers/navigation/navigation_path_query_parameters_2d.cpp
Normal 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);
|
||||
}
|
||||
130
servers/navigation/navigation_path_query_parameters_2d.h
Normal file
130
servers/navigation/navigation_path_query_parameters_2d.h
Normal 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);
|
||||
242
servers/navigation/navigation_path_query_parameters_3d.cpp
Normal file
242
servers/navigation/navigation_path_query_parameters_3d.cpp
Normal 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);
|
||||
}
|
||||
130
servers/navigation/navigation_path_query_parameters_3d.h
Normal file
130
servers/navigation/navigation_path_query_parameters_3d.h
Normal 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);
|
||||
147
servers/navigation/navigation_path_query_result_2d.cpp
Normal file
147
servers/navigation/navigation_path_query_result_2d.cpp
Normal 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);
|
||||
}
|
||||
74
servers/navigation/navigation_path_query_result_2d.h
Normal file
74
servers/navigation/navigation_path_query_result_2d.h
Normal 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);
|
||||
147
servers/navigation/navigation_path_query_result_3d.cpp
Normal file
147
servers/navigation/navigation_path_query_result_3d.cpp
Normal 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);
|
||||
}
|
||||
75
servers/navigation/navigation_path_query_result_3d.h
Normal file
75
servers/navigation/navigation_path_query_result_3d.h
Normal 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);
|
||||
61
servers/navigation/navigation_utilities.h
Normal file
61
servers/navigation/navigation_utilities.h
Normal 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
|
||||
46
servers/navigation_server_2d.compat.inc
Normal file
46
servers/navigation_server_2d.compat.inc
Normal 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
|
||||
587
servers/navigation_server_2d.cpp
Normal file
587
servers/navigation_server_2d.cpp
Normal 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;
|
||||
}
|
||||
445
servers/navigation_server_2d.h
Normal file
445
servers/navigation_server_2d.h
Normal 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);
|
||||
194
servers/navigation_server_2d_dummy.h
Normal file
194
servers/navigation_server_2d_dummy.h
Normal 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; }
|
||||
};
|
||||
46
servers/navigation_server_3d.compat.inc
Normal file
46
servers/navigation_server_3d.compat.inc
Normal 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
|
||||
1073
servers/navigation_server_3d.cpp
Normal file
1073
servers/navigation_server_3d.cpp
Normal file
File diff suppressed because it is too large
Load Diff
531
servers/navigation_server_3d.h
Normal file
531
servers/navigation_server_3d.h
Normal 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);
|
||||
214
servers/navigation_server_3d_dummy.h
Normal file
214
servers/navigation_server_3d_dummy.h
Normal 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
Reference in New Issue
Block a user