summaryrefslogtreecommitdiff
path: root/servers
diff options
context:
space:
mode:
Diffstat (limited to 'servers')
-rw-r--r--servers/SCsub19
-rw-r--r--servers/audio/SCsub7
-rw-r--r--servers/audio/audio_driver_dummy.cpp146
-rw-r--r--servers/audio/audio_driver_dummy.h76
-rw-r--r--servers/audio/audio_filter_sw.cpp286
-rw-r--r--servers/audio/audio_filter_sw.h119
-rw-r--r--servers/audio/audio_mixer_sw.cpp1085
-rw-r--r--servers/audio/audio_mixer_sw.h248
-rw-r--r--servers/audio/audio_server_sw.cpp1012
-rw-r--r--servers/audio/audio_server_sw.h279
-rw-r--r--servers/audio/reverb_buffers_sw.cpp33
-rw-r--r--servers/audio/reverb_buffers_sw.h38
-rw-r--r--servers/audio/reverb_sw.cpp569
-rw-r--r--servers/audio/reverb_sw.h84
-rw-r--r--servers/audio/sample_manager_sw.cpp280
-rw-r--r--servers/audio/sample_manager_sw.h129
-rw-r--r--servers/audio/voice_rb_sw.cpp34
-rw-r--r--servers/audio/voice_rb_sw.h146
-rw-r--r--servers/audio_server.cpp178
-rw-r--r--servers/audio_server.h289
-rw-r--r--servers/physics/SCsub7
-rw-r--r--servers/physics/area_pair_sw.cpp94
-rw-r--r--servers/physics/area_pair_sw.h53
-rw-r--r--servers/physics/area_sw.cpp192
-rw-r--r--servers/physics/area_sw.h172
-rw-r--r--servers/physics/body_pair_sw.cpp442
-rw-r--r--servers/physics/body_pair_sw.h97
-rw-r--r--servers/physics/body_sw.cpp631
-rw-r--r--servers/physics/body_sw.h348
-rw-r--r--servers/physics/broad_phase_basic.cpp216
-rw-r--r--servers/physics/broad_phase_basic.h101
-rw-r--r--servers/physics/broad_phase_octree.cpp133
-rw-r--r--servers/physics/broad_phase_octree.h73
-rw-r--r--servers/physics/broad_phase_sw.cpp35
-rw-r--r--servers/physics/broad_phase_sw.h73
-rw-r--r--servers/physics/collision_object_sw.cpp219
-rw-r--r--servers/physics/collision_object_sw.h119
-rw-r--r--servers/physics/collision_solver_sat.cpp1331
-rw-r--r--servers/physics/collision_solver_sat.h37
-rw-r--r--servers/physics/collision_solver_sw.cpp241
-rw-r--r--servers/physics/collision_solver_sw.h52
-rw-r--r--servers/physics/constraint_sw.cpp30
-rw-r--r--servers/physics/constraint_sw.h72
-rw-r--r--servers/physics/gjk_epa.cpp900
-rw-r--r--servers/physics/gjk_epa.h40
-rw-r--r--servers/physics/joints_sw.cpp450
-rw-r--r--servers/physics/joints_sw.h176
-rw-r--r--servers/physics/physics_server_sw.cpp1050
-rw-r--r--servers/physics/physics_server_sw.h215
-rw-r--r--servers/physics/shape_sw.cpp1664
-rw-r--r--servers/physics/shape_sw.h430
-rw-r--r--servers/physics/space_sw.cpp429
-rw-r--r--servers/physics/space_sw.h157
-rw-r--r--servers/physics/step_sw.cpp237
-rw-r--r--servers/physics/step_sw.h48
-rw-r--r--servers/physics_2d/SCsub4
-rw-r--r--servers/physics_2d/area_2d_sw.cpp193
-rw-r--r--servers/physics_2d/area_2d_sw.h172
-rw-r--r--servers/physics_2d/area_pair_2d_sw.cpp94
-rw-r--r--servers/physics_2d/area_pair_2d_sw.h53
-rw-r--r--servers/physics_2d/body_2d_sw.cpp609
-rw-r--r--servers/physics_2d/body_2d_sw.h334
-rw-r--r--servers/physics_2d/body_pair_2d_sw.cpp435
-rw-r--r--servers/physics_2d/body_pair_2d_sw.h94
-rw-r--r--servers/physics_2d/broad_phase_2d_basic.cpp192
-rw-r--r--servers/physics_2d/broad_phase_2d_basic.h100
-rw-r--r--servers/physics_2d/broad_phase_2d_hash_grid.cpp665
-rw-r--r--servers/physics_2d/broad_phase_2d_hash_grid.h192
-rw-r--r--servers/physics_2d/broad_phase_2d_sw.cpp35
-rw-r--r--servers/physics_2d/broad_phase_2d_sw.h73
-rw-r--r--servers/physics_2d/collision_object_2d_sw.cpp220
-rw-r--r--servers/physics_2d/collision_object_2d_sw.h123
-rw-r--r--servers/physics_2d/collision_solver_2d_sat.cpp1034
-rw-r--r--servers/physics_2d/collision_solver_2d_sat.h37
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.cpp309
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.h52
-rw-r--r--servers/physics_2d/constraint_2d_sw.cpp30
-rw-r--r--servers/physics_2d/constraint_2d_sw.h72
-rw-r--r--servers/physics_2d/joints_2d_sw.cpp574
-rw-r--r--servers/physics_2d/joints_2d_sw.h210
-rw-r--r--servers/physics_2d/physics_2d_server_sw.cpp1046
-rw-r--r--servers/physics_2d/physics_2d_server_sw.h215
-rw-r--r--servers/physics_2d/shape_2d_sw.cpp1085
-rw-r--r--servers/physics_2d/shape_2d_sw.h442
-rw-r--r--servers/physics_2d/space_2d_sw.cpp432
-rw-r--r--servers/physics_2d/space_2d_sw.h160
-rw-r--r--servers/physics_2d/step_2d_sw.cpp239
-rw-r--r--servers/physics_2d/step_2d_sw.h48
-rw-r--r--servers/physics_2d_server.cpp417
-rw-r--r--servers/physics_2d_server.h426
-rw-r--r--servers/physics_server.cpp420
-rw-r--r--servers/physics_server.h429
-rw-r--r--servers/register_server_types.cpp68
-rw-r--r--servers/register_server_types.h35
-rw-r--r--servers/spatial_sound/SCsub7
-rw-r--r--servers/spatial_sound/spatial_sound_server_sw.cpp1049
-rw-r--r--servers/spatial_sound/spatial_sound_server_sw.h249
-rw-r--r--servers/spatial_sound_2d/SCsub7
-rw-r--r--servers/spatial_sound_2d/spatial_sound_2d_server_sw.cpp1059
-rw-r--r--servers/spatial_sound_2d/spatial_sound_2d_server_sw.h265
-rw-r--r--servers/spatial_sound_2d_server.cpp44
-rw-r--r--servers/spatial_sound_2d_server.h164
-rw-r--r--servers/spatial_sound_server.cpp44
-rw-r--r--servers/spatial_sound_server.h168
-rw-r--r--servers/visual/SCsub7
-rw-r--r--servers/visual/default_mouse_cursor.xpm23
-rw-r--r--servers/visual/particle_system_sw.cpp412
-rw-r--r--servers/visual/particle_system_sw.h131
-rw-r--r--servers/visual/rasterizer.cpp573
-rw-r--r--servers/visual/rasterizer.h562
-rw-r--r--servers/visual/rasterizer_dummy.cpp1786
-rw-r--r--servers/visual/rasterizer_dummy.h725
-rw-r--r--servers/visual/shader_compiler.cpp30
-rw-r--r--servers/visual/shader_compiler.h142
-rw-r--r--servers/visual/shader_graph.cpp455
-rw-r--r--servers/visual/shader_graph.h112
-rw-r--r--servers/visual/shader_language.cpp2385
-rw-r--r--servers/visual/shader_language.h403
-rw-r--r--servers/visual/visual_server_raster.cpp5717
-rw-r--r--servers/visual/visual_server_raster.h1112
-rw-r--r--servers/visual/visual_server_wrap_mt.cpp199
-rw-r--r--servers/visual/visual_server_wrap_mt.h1057
-rw-r--r--servers/visual_server.cpp751
-rw-r--r--servers/visual_server.h971
124 files changed, 47598 insertions, 0 deletions
diff --git a/servers/SCsub b/servers/SCsub
new file mode 100644
index 0000000000..3871c30cfa
--- /dev/null
+++ b/servers/SCsub
@@ -0,0 +1,19 @@
+Import('env')
+
+env.servers_sources=[]
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+SConscript('physics/SCsub');
+SConscript('physics_2d/SCsub');
+SConscript('visual/SCsub');
+SConscript('audio/SCsub');
+SConscript('spatial_sound/SCsub');
+SConscript('spatial_sound_2d/SCsub');
+
+lib = env.Library("servers",env.servers_sources)
+
+env.Prepend(LIBS=[lib])
+
+
diff --git a/servers/audio/SCsub b/servers/audio/SCsub
new file mode 100644
index 0000000000..16fe3a59ac
--- /dev/null
+++ b/servers/audio/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
new file mode 100644
index 0000000000..14d89cc216
--- /dev/null
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* audio_driver_dummy.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "globals.h"
+#include "os/os.h"
+
+
+
+Error AudioDriverDummy::init() {
+
+ active=false;
+ thread_exited=false;
+ exit_thread=false;
+ pcm_open = false;
+ samples_in = NULL;
+
+
+ mix_rate = 44100;
+ output_format = OUTPUT_STEREO;
+ channels = 2;
+
+ int latency = GLOBAL_DEF("audio/output_latency",25);
+ buffer_size = nearest_power_of_2( latency * mix_rate / 1000 );
+
+ samples_in = memnew_arr(int32_t, buffer_size*channels);
+
+ mutex=Mutex::create();
+ thread = Thread::create(AudioDriverDummy::thread_func, this);
+
+ return OK;
+};
+
+void AudioDriverDummy::thread_func(void* p_udata) {
+
+ AudioDriverDummy* ad = (AudioDriverDummy*)p_udata;
+
+ uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate))*1000000;
+
+
+ while (!ad->exit_thread) {
+
+
+ if (!ad->active) {
+
+ } else {
+
+ ad->lock();
+
+ ad->audio_server_process(ad->buffer_size, ad->samples_in);
+
+ ad->unlock();
+
+ };
+
+ OS::get_singleton()->delay_usec(usdelay);
+
+ };
+
+ ad->thread_exited=true;
+
+};
+
+void AudioDriverDummy::start() {
+
+ active = true;
+};
+
+int AudioDriverDummy::get_mix_rate() const {
+
+ return mix_rate;
+};
+
+AudioDriverSW::OutputFormat AudioDriverDummy::get_output_format() const {
+
+ return output_format;
+};
+void AudioDriverDummy::lock() {
+
+ if (!thread || !mutex)
+ return;
+ mutex->lock();
+};
+void AudioDriverDummy::unlock() {
+
+ if (!thread || !mutex)
+ return;
+ mutex->unlock();
+};
+
+void AudioDriverDummy::finish() {
+
+ if (!thread)
+ return;
+
+ exit_thread = true;
+ Thread::wait_to_finish(thread);
+
+ if (samples_in) {
+ memdelete_arr(samples_in);
+ };
+
+ memdelete(thread);
+ if (mutex)
+ memdelete(mutex);
+ thread = NULL;
+};
+
+AudioDriverDummy::AudioDriverDummy() {
+
+ mutex = NULL;
+ thread=NULL;
+
+};
+
+AudioDriverDummy::~AudioDriverDummy() {
+
+};
+
+
diff --git a/servers/audio/audio_driver_dummy.h b/servers/audio/audio_driver_dummy.h
new file mode 100644
index 0000000000..6784edf594
--- /dev/null
+++ b/servers/audio/audio_driver_dummy.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* audio_driver_dummy.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_DRIVER_DUMMY_H
+#define AUDIO_DRIVER_DUMMY_H
+
+#include "servers/audio/audio_server_sw.h"
+
+#include "core/os/thread.h"
+#include "core/os/mutex.h"
+
+
+class AudioDriverDummy : public AudioDriverSW {
+
+ Thread* thread;
+ Mutex* mutex;
+
+ int32_t* samples_in;
+
+ static void thread_func(void* p_udata);
+ int buffer_size;
+
+ unsigned int mix_rate;
+ OutputFormat output_format;
+
+ int channels;
+
+ bool active;
+ bool thread_exited;
+ mutable bool exit_thread;
+ bool pcm_open;
+
+public:
+
+ const char* get_name() const {
+ return "Dummy";
+ };
+
+ virtual Error init();
+ virtual void start();
+ virtual int get_mix_rate() const;
+ virtual OutputFormat get_output_format() const;
+ virtual void lock();
+ virtual void unlock();
+ virtual void finish();
+
+ AudioDriverDummy();
+ ~AudioDriverDummy();
+};
+
+#endif
diff --git a/servers/audio/audio_filter_sw.cpp b/servers/audio/audio_filter_sw.cpp
new file mode 100644
index 0000000000..5d8750ffdb
--- /dev/null
+++ b/servers/audio/audio_filter_sw.cpp
@@ -0,0 +1,286 @@
+/*************************************************************************/
+/* audio_filter_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_filter_sw.h"
+
+void AudioFilterSW::set_mode(Mode p_mode) {
+
+ mode = p_mode;
+}
+void AudioFilterSW::set_cutoff(float p_cutoff) {
+
+ cutoff=p_cutoff;
+}
+void AudioFilterSW::set_resonance(float p_resonance) {
+
+ resonance=p_resonance;
+}
+
+void AudioFilterSW::set_gain(float p_gain) {
+
+ gain=p_gain;
+}
+
+void AudioFilterSW::set_sampling_rate(float p_srate) {
+
+ sampling_rate=p_srate;
+}
+
+
+void AudioFilterSW::prepare_coefficients(Coeffs *p_coeffs) {
+
+ int sr_limit = (sampling_rate/2)+512;
+
+
+ double final_cutoff=(cutoff>sr_limit)?sr_limit:cutoff;
+ if (final_cutoff<1) //avoid crapness
+ final_cutoff=1; //dont allow less than this
+
+
+
+ double omega=2.0*Math_PI*final_cutoff/sampling_rate;
+
+ double sin_v=Math::sin(omega);
+ double cos_v=Math::cos(omega);
+
+ double Q=resonance;
+ if (Q<=0.0) {
+ Q=0.0001;
+ }
+
+
+ if (mode==BANDPASS)
+ Q*=2.0;
+ else if (mode==PEAK)
+ Q*=3.0;
+
+ double tmpgain=gain;
+
+ if (tmpgain<0.001)
+ tmpgain=0.001;
+
+ if (stages>1) {
+
+ Q=(Q>1.0 ? Math::pow(Q,1.0/stages) : Q);
+ tmpgain = Math::pow(tmpgain,1.0/(stages+1));
+ }
+ double alpha = sin_v/(2*Q);
+
+ double a0 = 1.0 + alpha;
+
+ switch (mode) {
+
+ case LOWPASS: {
+
+ p_coeffs->b0= (1.0 - cos_v)/2.0 ;
+ p_coeffs->b1= 1.0 - cos_v ;
+ p_coeffs->b2= (1.0 - cos_v)/2.0 ;
+ p_coeffs->a1= -2.0*cos_v;
+ p_coeffs->a2= 1.0 - alpha ;
+ } break;
+
+
+ case HIGHPASS: {
+
+ p_coeffs->b0 = (1.0 + cos_v)/2.0;
+ p_coeffs->b1 = -(1.0 + cos_v);
+ p_coeffs->b2 = (1.0 + cos_v)/2.0;
+ p_coeffs->a1 = -2.0*cos_v;
+ p_coeffs->a2 = 1.0 - alpha;
+ } break;
+
+ case BANDPASS: {
+
+ p_coeffs->b0 = alpha*sqrt(Q+1);
+ p_coeffs->b1 = 0.0 ;
+ p_coeffs->b2 = -alpha*sqrt(Q+1);
+ p_coeffs->a1 = -2.0*cos_v;
+ p_coeffs->a2 = 1.0 - alpha;
+ } break;
+
+ case NOTCH: {
+
+ p_coeffs->b0 = 1.0;
+ p_coeffs->b1 = -2.0*cos_v;
+ p_coeffs->b2 = 1.0;
+ p_coeffs->a1 = -2.0*cos_v;
+ p_coeffs->a2 = 1.0 - alpha;
+ } break;
+ case PEAK: {
+ p_coeffs->b0 = (1.0+alpha*tmpgain);
+ p_coeffs->b1 = (-2.0*cos_v);
+ p_coeffs->b2 = (1.0-alpha*tmpgain);
+ p_coeffs->a1 = -2*cos_v;
+ p_coeffs->a2 = (1-alpha/tmpgain);
+ } break;
+ case BANDLIMIT: {
+ //this one is extra tricky
+ double hicutoff=resonance;
+ double centercutoff = (cutoff+resonance)/2.0;
+ double bandwidth=(Math::log(centercutoff)-Math::log(hicutoff))/Math::log(2);
+ omega=2.0*Math_PI*centercutoff/sampling_rate;
+ alpha = Math::sin(omega)*Math::sinh( Math::log(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;
+ alpha = sin_v / (2 * tmpq);
+ 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;
+ alpha = sin_v / (2 * tmpq);
+ 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;
+
+ //undenormalise
+/* p_coeffs->b0=undenormalise(p_coeffs->b0);
+ p_coeffs->b1=undenormalise(p_coeffs->b1);
+ p_coeffs->b2=undenormalise(p_coeffs->b2);
+ p_coeffs->a1=undenormalise(p_coeffs->a1);
+ p_coeffs->a2=undenormalise(p_coeffs->a2);*/
+
+}
+
+void AudioFilterSW::set_stages(int p_stages) { //adjust for multiple stages
+
+ stages=p_stages;
+}
+
+/* Fouriertransform kernel to obtain response */
+
+float AudioFilterSW::get_response(float p_freq,Coeffs *p_coeffs) {
+
+ float freq=p_freq / sampling_rate * Math_PI * 2.0f;
+
+ float cx=p_coeffs->b0,cy=0.0;
+
+ cx += cos(freq) * p_coeffs->b1;
+ cy -= sin(freq) * p_coeffs->b1;
+ cx += cos(2*freq) * p_coeffs->b2;
+ cy -= sin(2*freq) * p_coeffs->b2;
+
+
+ float H=cx*cx+cy*cy;
+ cx=1.0;
+ cy=0.0;
+
+
+ cx -= cos(freq) * p_coeffs->a1;
+ cy += sin(freq) * p_coeffs->a1;
+ cx -= cos(2*freq) * p_coeffs->a2;
+ cy += sin(2*freq) * p_coeffs->a2;
+
+
+ H=H/(cx*cx+cy*cy);
+ return H;
+}
+
+
+AudioFilterSW::AudioFilterSW() {
+
+
+ sampling_rate=44100;
+ resonance=0.5;
+ cutoff=5000;
+ gain=1.0;
+ mode=LOWPASS;
+ stages=1;
+}
+
+AudioFilterSW::Processor::Processor() {
+
+ set_filter(NULL);
+
+}
+
+void AudioFilterSW::Processor::set_filter(AudioFilterSW * p_filter) {
+
+ ha1=ha2=hb1=hb2=0;
+ filter=p_filter;
+}
+
+void AudioFilterSW::Processor::update_coeffs() {
+
+ if (!filter)
+ return;
+
+ filter->prepare_coefficients(&coeffs);
+
+}
+
+void AudioFilterSW::Processor::process(float *p_samples,int p_amount, int p_stride) {
+
+ if (!filter)
+ return;
+
+ for (int i=0;i<p_amount;i++) {
+
+ process_one(*p_samples);
+ p_samples+=p_stride;
+ }
+
+
+}
diff --git a/servers/audio/audio_filter_sw.h b/servers/audio/audio_filter_sw.h
new file mode 100644
index 0000000000..52315af686
--- /dev/null
+++ b/servers/audio/audio_filter_sw.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* audio_filter_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_FILTER_SW_H
+#define AUDIO_FILTER_SW_H
+
+
+#include "math_funcs.h"
+
+class AudioFilterSW {
+public:
+
+ struct Coeffs {
+
+ float a1,a2;
+ float b0,b1,b2;
+
+ //bool operator==(const Coeffs &p_rv) { return (FLOATS_EQ(a1,p_rv.a1) && FLOATS_EQ(a2,p_rv.a2) && FLOATS_EQ(b1,p_rv.b1) && FLOATS_EQ(b2,p_rv.b2) && FLOATS_EQ(b0,p_rv.b0) ); }
+ Coeffs() { a1=a2=b0=b1=b2=0.0; }
+ };
+
+ enum Mode {
+ BANDPASS,
+ HIGHPASS,
+ LOWPASS,
+ NOTCH,
+ PEAK,
+ BANDLIMIT,
+ LOWSHELF,
+ HIGHSHELF
+
+ };
+
+ class Processor { // simple filter processor
+
+ AudioFilterSW * filter;
+ Coeffs coeffs;
+ float ha1,ha2,hb1,hb2; //history
+ public:
+ void set_filter(AudioFilterSW * p_filter);
+ void process(float *p_samples,int p_amount, int p_stride=1);
+ void update_coeffs();
+ inline void process_one(float& p_sample);
+
+ Processor();
+ };
+
+private:
+
+
+ float cutoff;
+ float resonance;
+ float gain;
+ float sampling_rate;
+ int stages;
+ Mode mode;
+
+
+
+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_val) {
+
+ float pre=p_val;
+ p_val = (p_val * coeffs.b0 + hb1 * coeffs.b1 + hb2 * coeffs.b2 + ha1 * coeffs.a1 + ha2 * coeffs.a2);
+ ha2=ha1;
+ hb2=hb1;
+ hb1=pre;
+ ha1=p_val;
+}
+
+
+#endif // AUDIO_FILTER_SW_H
diff --git a/servers/audio/audio_mixer_sw.cpp b/servers/audio/audio_mixer_sw.cpp
new file mode 100644
index 0000000000..2ca0c5e93a
--- /dev/null
+++ b/servers/audio/audio_mixer_sw.cpp
@@ -0,0 +1,1085 @@
+/*************************************************************************/
+/* audio_mixer_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_mixer_sw.h"
+#include "print_string.h"
+#include "os/os.h"
+//TODO implement FAST_AUDIO macro
+
+#ifdef FAST_AUDIO
+#define NO_REVERB
+#endif
+
+template<class Depth,bool is_stereo,bool use_filter,bool use_fx,AudioMixerSW::InterpolationType type,AudioMixerSW::MixChannels mix_mode>
+void AudioMixerSW::do_resample(const Depth* p_src, int32_t *p_dst, ResamplerState *p_state) {
+
+ // this function will be compiled branchless by any decent compiler
+
+ int32_t final,final_r,next,next_r;
+ int32_t *reverb_dst = p_state->reverb_buffer;
+ while (p_state->amount--) {
+
+ int32_t pos=p_state->pos >> MIX_FRAC_BITS;
+ if (is_stereo)
+ pos<<=1;
+
+ final=p_src[pos];
+ if (is_stereo)
+ final_r=p_src[pos+1];
+
+ if (sizeof(Depth)==1) { /* conditions will not exist anymore when compiled! */
+ final<<=8;
+ if (is_stereo)
+ final_r<<=8;
+ }
+
+ if (type==INTERPOLATION_LINEAR) {
+
+ if (is_stereo) {
+
+ next=p_src[pos+2];
+ next_r=p_src[pos+3];
+ } else {
+ next=p_src[pos+1];
+ }
+
+ if (sizeof(Depth)==1) {
+ next<<=8;
+ if (is_stereo)
+ next_r<<=8;
+ }
+
+ int32_t frac=int32_t(p_state->pos&MIX_FRAC_MASK);
+
+ final=final+((next-final)*frac >> MIX_FRAC_BITS);
+ if (is_stereo)
+ final_r=final_r+((next_r-final_r)*frac >> MIX_FRAC_BITS);
+ }
+
+ if (use_filter) {
+
+ Channel::Mix::Filter *f = p_state->filter_l;
+ float finalf=final;
+ float pre = finalf;
+ finalf = ((finalf*p_state->coefs.b0) + (f->hb[0]*p_state->coefs.b1) + (f->hb[1]*p_state->coefs.b2) + (f->ha[0]*p_state->coefs.a1) + (f->ha[1]*p_state->coefs.a2)
+ );
+
+ f->ha[1]=f->ha[0];
+ f->hb[1]=f->hb[0];
+ f->hb[0]=pre;
+ f->ha[0]=finalf;
+
+ final=Math::fast_ftoi(finalf);
+
+ if (is_stereo) {
+
+ f = p_state->filter_r;
+ finalf=final_r;
+ pre = finalf;
+ finalf = ((finalf*p_state->coefs.b0) + (f->hb[0]*p_state->coefs.b1) + (f->hb[1]*p_state->coefs.b2) + (f->ha[0]*p_state->coefs.a1) + (f->ha[1]*p_state->coefs.a2)
+ );
+ f->ha[1]=f->ha[0];
+ f->hb[1]=f->hb[0];
+ f->hb[0]=pre;
+ f->ha[0]=finalf;
+
+ final_r=Math::fast_ftoi(finalf);
+
+ }
+
+ p_state->coefs.b0+=p_state->coefs_inc.b0;
+ p_state->coefs.b1+=p_state->coefs_inc.b1;
+ p_state->coefs.b2+=p_state->coefs_inc.b2;
+ p_state->coefs.a1+=p_state->coefs_inc.a1;
+ p_state->coefs.a2+=p_state->coefs_inc.a2;
+ }
+
+ if (!is_stereo) {
+ final_r=final; //copy to right channel if stereo
+ }
+
+ //convert back to 24 bits and mix to buffers
+
+ if (mix_mode==MIX_STEREO) {
+ *p_dst++ +=(final*(p_state->vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *p_dst++ +=(final_r*(p_state->vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+
+ p_state->vol[0]+=p_state->vol_inc[0];
+ p_state->vol[1]+=p_state->vol_inc[1];
+
+ if (use_fx) {
+ *reverb_dst++ +=(final*(p_state->reverb_vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final_r*(p_state->reverb_vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ p_state->reverb_vol[0]+=p_state->reverb_vol_inc[0];
+ p_state->reverb_vol[1]+=p_state->reverb_vol_inc[1];
+ }
+
+
+ } else if (mix_mode==MIX_QUAD) {
+
+ *p_dst++ +=(final*(p_state->vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *p_dst++ +=(final_r*(p_state->vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+
+ *p_dst++ +=(final*(p_state->vol[2]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *p_dst++ +=(final_r*(p_state->vol[3]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+
+ p_state->vol[0]+=p_state->vol_inc[0];
+ p_state->vol[1]+=p_state->vol_inc[1];
+ p_state->vol[2]+=p_state->vol_inc[2];
+ p_state->vol[3]+=p_state->vol_inc[3];
+
+ if (use_fx) {
+ *reverb_dst++ +=(final*(p_state->reverb_vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final_r*(p_state->reverb_vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final*(p_state->reverb_vol[2]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final_r*(p_state->reverb_vol[3]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ p_state->reverb_vol[0]+=p_state->reverb_vol_inc[0];
+ p_state->reverb_vol[1]+=p_state->reverb_vol_inc[1];
+ p_state->reverb_vol[2]+=p_state->reverb_vol_inc[2];
+ p_state->reverb_vol[3]+=p_state->reverb_vol_inc[3];
+ }
+ }
+
+ p_state->pos+=p_state->increment;
+ }
+}
+
+
+void AudioMixerSW::mix_channel(Channel& c) {
+
+
+ if (!sample_manager->is_sample(c.sample)) {
+ // sample is gone!
+ c.active=false;
+ return;
+ }
+
+
+ /* some 64-bit fixed point precaches */
+
+ int64_t loop_begin_fp=((int64_t)sample_manager->sample_get_loop_begin(c.sample) << MIX_FRAC_BITS);
+ int64_t loop_end_fp=((int64_t)sample_manager->sample_get_loop_end(c.sample) << MIX_FRAC_BITS);
+ int64_t length_fp=((int64_t)sample_manager->sample_get_length(c.sample) << MIX_FRAC_BITS);
+ int64_t begin_limit=(sample_manager->sample_get_loop_format(c.sample)!=AS::SAMPLE_LOOP_NONE)?loop_begin_fp:0;
+ int64_t end_limit=(sample_manager->sample_get_loop_format(c.sample)!=AS::SAMPLE_LOOP_NONE)?loop_end_fp:length_fp;
+ bool is_stereo=sample_manager->sample_is_stereo(c.sample);
+
+ int32_t todo=mix_chunk_size;
+// int mixed=0;
+ bool use_filter=false;
+
+ ResamplerState rstate;
+
+ /* compute voume ramps, increment, etc */
+
+
+
+ for(int i=0;i<mix_channels;i++) {
+ c.mix.old_vol[i]=c.mix.vol[i];
+ c.mix.old_reverb_vol[i]=c.mix.reverb_vol[i];
+ c.mix.old_chorus_vol[i]=c.mix.chorus_vol[i];
+ }
+
+ float vol = c.vol*channel_nrg;
+
+ float reverb_vol = c.reverb_send*channel_nrg;
+ float chorus_vol = c.chorus_send*channel_nrg;
+
+ if (mix_channels==2) {
+ //stereo pan
+ float pan = c.pan * 0.5 + 0.5;
+ float panv[2]={
+ (1.0 - pan)*(1<<MIX_VOL_FRAC_BITS),
+ (pan)*(1<<MIX_VOL_FRAC_BITS)
+ };
+
+ for(int i=0;i<2;i++) {
+
+ c.mix.vol[i]=Math::fast_ftoi(vol*panv[i]);
+ c.mix.reverb_vol[i]=Math::fast_ftoi(reverb_vol*panv[i]);
+ c.mix.chorus_vol[i]=Math::fast_ftoi(chorus_vol*panv[i]);
+ }
+
+ } else {
+ //qudra pan
+ float panx = c.pan * 0.5 + 0.5;
+ float pany = c.depth * 0.5 + 0.5;
+ // with this model every speaker plays at 0.25 energy at the center.. i'm not sure if it's correct but it seems to be balanced
+ float panv[4]={
+ (1.0-pany)*(1.0-panx)*(1<<MIX_VOL_FRAC_BITS),
+ (1.0-pany)*( panx)*(1<<MIX_VOL_FRAC_BITS),
+ ( pany)*(1.0-panx)*(1<<MIX_VOL_FRAC_BITS),
+ ( pany)*( panx)*(1<<MIX_VOL_FRAC_BITS)
+ };
+
+ for(int i=0;i<4;i++) {
+
+ c.mix.vol[i]=Math::fast_ftoi(vol*panv[i]);
+ c.mix.reverb_vol[i]=Math::fast_ftoi(reverb_vol*panv[i]);
+ c.mix.chorus_vol[i]=Math::fast_ftoi(chorus_vol*panv[i]);
+ }
+
+ }
+
+ if (c.first_mix) { // avoid ramp up
+
+ for(int i=0;i<mix_channels;i++) {
+ c.mix.old_vol[i]=c.mix.vol[i];
+ c.mix.old_reverb_vol[i]=c.mix.reverb_vol[i];
+ c.mix.old_chorus_vol[i]=c.mix.chorus_vol[i];
+ }
+
+ c.first_mix=false;
+ }
+
+
+
+ Channel::Filter::Coefs filter_coefs;
+ Channel::Filter::Coefs filter_inc;
+
+ if (c.filter.type!=AudioMixer::FILTER_NONE) {
+
+ filter_coefs=c.filter.old_coefs;
+ filter_inc.b0=(c.filter.coefs.b0-filter_coefs.b0)/(1<<mix_chunk_bits);
+ filter_inc.b1=(c.filter.coefs.b1-filter_coefs.b1)/(1<<mix_chunk_bits);
+ filter_inc.b2=(c.filter.coefs.b2-filter_coefs.b2)/(1<<mix_chunk_bits);
+ filter_inc.a1=(c.filter.coefs.a1-filter_coefs.a1)/(1<<mix_chunk_bits);
+ filter_inc.a2=(c.filter.coefs.a2-filter_coefs.a2)/(1<<mix_chunk_bits);
+ use_filter=true;
+ }
+
+ if (c.mix.increment>0)
+ c.mix.increment=((int64_t)c.speed<<MIX_FRAC_BITS)/mix_rate;
+ else
+ c.mix.increment=-((int64_t)c.speed<<MIX_FRAC_BITS)/mix_rate;
+
+ //volume ramp
+
+
+ for(int i=0;i<mix_channels;i++) {
+ rstate.vol_inc[i]=((c.mix.vol[i]-c.mix.old_vol[i])<<MIX_VOLRAMP_FRAC_BITS)>>mix_chunk_bits;
+ rstate.vol[i]=c.mix.old_vol[i]<<MIX_VOLRAMP_FRAC_BITS;
+ rstate.reverb_vol_inc[i]=((c.mix.reverb_vol[i]-c.mix.old_reverb_vol[i])<<MIX_VOLRAMP_FRAC_BITS)>>mix_chunk_bits;
+ rstate.reverb_vol[i]=c.mix.old_reverb_vol[i]<<MIX_VOLRAMP_FRAC_BITS;
+ rstate.chorus_vol_inc[i]=((c.mix.chorus_vol[i]-c.mix.old_chorus_vol[i])<<MIX_VOLRAMP_FRAC_BITS)>>mix_chunk_bits;
+ rstate.chorus_vol[i]=c.mix.old_chorus_vol[i]<<MIX_VOLRAMP_FRAC_BITS;
+ }
+
+
+ //looping
+
+ AS::SampleLoopFormat loop_format=sample_manager->sample_get_loop_format(c.sample);
+ AS::SampleFormat format=sample_manager->sample_get_format(c.sample);
+
+ bool use_fx=fx_enabled && (c.mix.old_reverb_vol || c.mix.reverb_vol || c.mix.old_chorus_vol || c.mix.chorus_vol );
+
+ /* audio data */
+
+ const void *data=sample_manager->sample_get_data_ptr(c.sample);
+ int32_t *dst_buff=mix_buffer;
+
+#ifndef NO_REVERB
+ rstate.reverb_buffer=reverb_state[c.reverb_room].buffer;
+#endif
+
+ /* @TODO validar loops al registrar? */
+
+ rstate.coefs=filter_coefs;
+ rstate.coefs_inc=filter_inc;
+ rstate.filter_l=&c.mix.filter_l;
+ rstate.filter_r=&c.mix.filter_r;
+
+ while (todo>0) {
+
+ int64_t limit=0;
+ int32_t target=0,aux=0;
+
+ /** LOOP CHECKING **/
+
+ if ( c.mix.increment < 0 ) {
+ /* going backwards */
+
+ if ( loop_format!=AS::SAMPLE_LOOP_NONE && c.mix.offset < loop_begin_fp ) {
+ /* loopstart reached */
+ if ( loop_format==AS::SAMPLE_LOOP_PING_PONG ) {
+ /* bounce ping pong */
+ c.mix.offset= loop_begin_fp + ( loop_begin_fp-c.mix.offset );
+ c.mix.increment=-c.mix.increment;
+ } else {
+ /* go to loop-end */
+ c.mix.offset=loop_end_fp-(loop_begin_fp-c.mix.offset);
+ }
+ } else {
+ /* check for sample not reaching begining */
+ if(c.mix.offset < 0) {
+
+ c.active=false;
+ break;
+ }
+ }
+ } else {
+ /* going forward */
+ if( loop_format!=AS::SAMPLE_LOOP_NONE && c.mix.offset >= loop_end_fp ) {
+ /* loopend reached */
+
+ if ( loop_format==AS::SAMPLE_LOOP_PING_PONG ) {
+ /* bounce ping pong */
+ c.mix.offset=loop_end_fp-(c.mix.offset-loop_end_fp);
+ c.mix.increment=-c.mix.increment;
+ } else {
+ /* go to loop-begin */
+
+ c.mix.offset=loop_begin_fp+(c.mix.offset-loop_end_fp);
+
+ }
+ } else {
+ /* no loop, check for end of sample */
+ if(c.mix.offset >= length_fp) {
+
+ c.active=false;
+ break;
+ }
+ }
+ }
+
+ /** MIXCOUNT COMPUTING **/
+
+ /* next possible limit (looppoints or sample begin/end */
+ limit=(c.mix.increment < 0) ?begin_limit:end_limit;
+
+ /* compute what is shorter, the todo or the limit? */
+ aux=(limit-c.mix.offset)/c.mix.increment+1;
+ target=(aux<todo)?aux:todo; /* mix target is the shorter buffer */
+
+ /* check just in case */
+ if ( target<=0 ) {
+ c.active=false;
+ break;
+ }
+
+ todo-=target;
+
+ int32_t offset=c.mix.offset&mix_chunk_mask; /* strip integer */
+ c.mix.offset-=offset;
+
+ rstate.increment=c.mix.increment;
+ rstate.amount=target;
+ rstate.pos=offset;
+
+/* Macros to call the resample function for all possibilities, creating a dedicated-non branchy function call for each thanks to template magic*/
+
+#define CALL_RESAMPLE_FUNC( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ do_resample<m_depth,m_stereo,m_use_filter,m_use_fx,m_interp, m_mode>(\
+ src_ptr,\
+ dst_buff,&rstate);
+
+
+#define CALL_RESAMPLE_INTERP( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_interp==INTERPOLATION_RAW) {\
+ CALL_RESAMPLE_FUNC(m_depth,m_stereo,m_use_filter,m_use_fx,INTERPOLATION_RAW,m_mode);\
+ } else if(m_interp==INTERPOLATION_LINEAR) {\
+ CALL_RESAMPLE_FUNC(m_depth,m_stereo,m_use_filter,m_use_fx,INTERPOLATION_LINEAR,m_mode);\
+ } else if(m_interp==INTERPOLATION_CUBIC) {\
+ CALL_RESAMPLE_FUNC(m_depth,m_stereo,m_use_filter,m_use_fx,INTERPOLATION_CUBIC,m_mode);\
+ }\
+
+#define CALL_RESAMPLE_FX( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_use_fx) {\
+ CALL_RESAMPLE_INTERP(m_depth,m_stereo,m_use_filter,true,m_interp, m_mode);\
+ } else {\
+ CALL_RESAMPLE_INTERP(m_depth,m_stereo,m_use_filter,false,m_interp, m_mode);\
+ }\
+
+
+#define CALL_RESAMPLE_FILTER( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_use_filter) {\
+ CALL_RESAMPLE_FX(m_depth,m_stereo,true,m_use_fx,m_interp, m_mode);\
+ } else {\
+ CALL_RESAMPLE_FX(m_depth,m_stereo,false,m_use_fx,m_interp, m_mode);\
+ }\
+
+#define CALL_RESAMPLE_STEREO( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_stereo) {\
+ CALL_RESAMPLE_FILTER(m_depth,true,m_use_filter,m_use_fx,m_interp, m_mode);\
+ } else {\
+ CALL_RESAMPLE_FILTER(m_depth,false,m_use_filter,m_use_fx,m_interp, m_mode);\
+ }\
+
+#define CALL_RESAMPLE_MODE( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_mode==MIX_STEREO) {\
+ CALL_RESAMPLE_STEREO(m_depth,m_stereo,m_use_filter,m_use_fx,m_interp, MIX_STEREO);\
+ } else {\
+ CALL_RESAMPLE_STEREO(m_depth,m_stereo,m_use_filter,m_use_fx,m_interp, MIX_QUAD);\
+ }\
+
+
+
+
+ if (format==AS::SAMPLE_FORMAT_PCM8) {
+
+ int8_t *src_ptr = &((int8_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ];
+ CALL_RESAMPLE_MODE(int8_t,is_stereo,use_filter,use_fx,interpolation_type,mix_channels);
+
+ } else if (format==AS::SAMPLE_FORMAT_PCM16) {
+ int16_t *src_ptr = &((int16_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ];
+ CALL_RESAMPLE_MODE(int16_t,is_stereo,use_filter,use_fx,interpolation_type,mix_channels);
+
+ }
+
+ c.mix.offset+=rstate.pos;
+ dst_buff+=target*2;
+
+ }
+
+ c.filter.old_coefs=c.filter.coefs;
+}
+
+void AudioMixerSW::mix_chunk() {
+
+ ERR_FAIL_COND(mix_chunk_left);
+
+ inside_mix=true;
+
+ // emit tick in usecs
+ for (int i=0;i<mix_chunk_size*mix_channels;i++) {
+
+ mix_buffer[i]=0;
+ }
+#ifndef NO_REVERB
+ for(int i=0;i<max_reverbs;i++)
+ reverb_state[i].used_in_chunk=false;
+#endif
+
+
+ audio_mixer_chunk_call(mix_chunk_size);
+
+ int ac=0;
+ for (int i=0;i<MAX_CHANNELS;i++) {
+
+ if (!channels[i].active)
+ continue;
+ ac++;
+
+ /* process volume */
+ Channel&c=channels[i];
+#ifndef NO_REVERB
+ bool has_reverb = c.reverb_send>CMP_EPSILON && fx_enabled;
+ if (has_reverb || c.had_prev_reverb) {
+
+ if (!reverb_state[c.reverb_room].used_in_chunk) {
+ //zero the room
+ int32_t *buff = reverb_state[c.reverb_room].buffer;
+ int len = mix_chunk_size*mix_channels;
+ for (int j=0;j<len;j++) {
+
+ buff[j]=0; // buffer in use, clear it for appending
+ }
+ reverb_state[c.reverb_room].used_in_chunk=true;
+ }
+ }
+#else
+ bool has_reverb = false;
+#endif
+ bool has_chorus = c.chorus_send>CMP_EPSILON && fx_enabled;
+
+
+ mix_channel(c);
+
+ c.had_prev_reverb=has_reverb;
+ c.had_prev_chorus=has_chorus;
+
+ }
+
+ //process reverb
+#ifndef NO_REVERB
+ if (fx_enabled) {
+
+
+ for(int i=0;i<max_reverbs;i++) {
+
+ if (!reverb_state[i].enabled && !reverb_state[i].used_in_chunk)
+ continue; //this reverb is not in use
+
+ int32_t *src=NULL;
+
+ if (reverb_state[i].used_in_chunk)
+ src=reverb_state[i].buffer;
+ else
+ src=zero_buffer;
+
+ bool in_use=false;
+
+ int passes=mix_channels/2;
+
+ for(int j=0;j<passes;j++) {
+
+ if (reverb_state[i].reverb[j].process((int*)&src[j*2],(int*)&mix_buffer[j*2],mix_chunk_size,passes))
+ in_use=true;
+ }
+
+ if (in_use) {
+ reverb_state[i].enabled=true;
+ reverb_state[i].frames_idle=0;
+ //copy data over
+
+ } else {
+ reverb_state[i].frames_idle+=mix_chunk_size;
+ if (false) { // go idle because too many frames passed
+ //disable this reverb, as nothing important happened on it
+ reverb_state[i].enabled=false;
+ reverb_state[i].frames_idle=0;
+ }
+ }
+
+ }
+ }
+#endif
+ mix_chunk_left=mix_chunk_size;
+ inside_mix=false;
+}
+
+int AudioMixerSW::mix(int32_t *p_buffer,int p_frames) {
+
+ int todo=p_frames;
+ int mixes=0;
+
+ while(todo) {
+
+
+ if (!mix_chunk_left) {
+
+ if (step_callback)
+ step_callback(step_udata);
+ mix_chunk();
+ mixes++;
+ }
+
+ int to_mix=MIN(mix_chunk_left,todo);
+ int from=mix_chunk_size-mix_chunk_left;
+
+ for (int i=0;i<to_mix*2;i++) {
+
+ (*p_buffer++)=mix_buffer[from*2+i];
+ }
+
+ mix_chunk_left-=to_mix;
+ todo-=to_mix;
+ }
+
+ return mixes;
+}
+
+uint64_t AudioMixerSW::get_step_usecs() const {
+
+ double mct = (1<<mix_chunk_bits)/double(mix_rate);
+ return mct*1000000.0;
+}
+
+int AudioMixerSW::_get_channel(ChannelID p_channel) const {
+
+ if (p_channel<0) {
+ return -1;
+ }
+
+ int idx=p_channel%MAX_CHANNELS;
+ int check=p_channel/MAX_CHANNELS;
+ ERR_FAIL_INDEX_V(idx,MAX_CHANNELS,-1);
+ if (channels[idx].check!=check) {
+ return -1;
+ }
+ if (!channels[idx].active) {
+ return -1;
+ }
+
+ return idx;
+}
+
+AudioMixer::ChannelID AudioMixerSW::channel_alloc(RID p_sample) {
+
+ ERR_FAIL_COND_V( !sample_manager->is_sample(p_sample), INVALID_CHANNEL );
+
+
+ int index=-1;
+ for (int i=0;i<MAX_CHANNELS;i++) {
+
+ if (!channels[i].active) {
+ index=i;
+ break;
+ }
+ }
+
+ if (index==-1)
+ return INVALID_CHANNEL;
+
+ Channel &c=channels[index];
+
+ // init variables
+ c.sample=p_sample;
+ c.vol=1;
+ c.pan=0;
+ c.depth=0;
+ c.height=0;
+ c.chorus_send=0;
+ c.reverb_send=0;
+ c.reverb_room=REVERB_HALL;
+ c.positional=false;
+ c.filter.type=FILTER_NONE;
+ c.speed=sample_manager->sample_get_mix_rate(p_sample);
+ c.active=true;
+ c.check=channel_id_count++;
+ c.first_mix=true;
+
+ // init mix variables
+
+ c.mix.offset=0;
+ c.mix.increment=1;
+ //zero everything when this errors
+ for(int i=0;i<4;i++) {
+ c.mix.old_vol[i]=0;
+ c.mix.old_reverb_vol[i]=0;
+ c.mix.old_chorus_vol[i]=0;
+ }
+
+ c.had_prev_chorus=false;
+ c.had_prev_reverb=false;
+ c.had_prev_vol=false;
+
+ ChannelID ret_id = index+c.check*MAX_CHANNELS;
+
+ return ret_id;
+
+}
+
+void AudioMixerSW::channel_set_volume(ChannelID p_channel, float p_gain) {
+
+ if (p_gain>3) // avoid gain going too high
+ p_gain=3;
+ if (p_gain<0)
+ p_gain=0;
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+ Channel &c = channels[chan];
+
+ //Math::exp( p_db * 0.11512925464970228420089957273422 );
+ c.vol=p_gain;
+
+}
+
+void AudioMixerSW::channel_set_pan(ChannelID p_channel, float p_pan, float p_depth,float p_height) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+ Channel &c = channels[chan];
+
+ c.pan=p_pan;
+ c.depth=p_depth;
+ c.height=p_height;
+
+}
+void AudioMixerSW::channel_set_filter(ChannelID p_channel, FilterType p_type, float p_cutoff, float p_resonance, float p_gain) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+
+ if (c.filter.type==p_type && c.filter.cutoff==p_cutoff && c.filter.resonance==p_resonance && c.filter.gain==p_gain)
+ return; //bye
+
+
+ bool type_changed = p_type!=c.filter.type;
+
+ c.filter.type=p_type;
+ c.filter.cutoff=p_cutoff;
+ c.filter.resonance=p_resonance;
+ c.filter.gain=p_gain;
+
+
+ AudioFilterSW filter;
+ switch(p_type) {
+ case FILTER_NONE: {
+
+ return; //do nothing else
+ } break;
+ case FILTER_LOWPASS: {
+ filter.set_mode(AudioFilterSW::LOWPASS);
+ } break;
+ case FILTER_BANDPASS: {
+ filter.set_mode(AudioFilterSW::BANDPASS);
+ } break;
+ case FILTER_HIPASS: {
+ filter.set_mode(AudioFilterSW::HIGHPASS);
+ } break;
+ case FILTER_NOTCH: {
+ filter.set_mode(AudioFilterSW::NOTCH);
+ } break;
+ case FILTER_PEAK: {
+ filter.set_mode(AudioFilterSW::PEAK);
+ } break;
+ case FILTER_BANDLIMIT: {
+ filter.set_mode(AudioFilterSW::BANDLIMIT);
+ } break;
+ case FILTER_LOW_SHELF: {
+ filter.set_mode(AudioFilterSW::LOWSHELF);
+ } break;
+ case FILTER_HIGH_SHELF: {
+ filter.set_mode(AudioFilterSW::HIGHSHELF);
+ } break;
+ }
+
+ filter.set_cutoff(p_cutoff);
+ filter.set_resonance(p_resonance);
+ filter.set_gain(p_gain);
+ filter.set_sampling_rate(mix_rate);
+ filter.set_stages(1);
+
+ AudioFilterSW::Coeffs coefs;
+ filter.prepare_coefficients(&coefs);
+
+ if (!type_changed)
+ c.filter.old_coefs=c.filter.coefs;
+
+ c.filter.coefs.b0=coefs.b0;
+ c.filter.coefs.b1=coefs.b1;
+ c.filter.coefs.b2=coefs.b2;
+ c.filter.coefs.a1=coefs.a1;
+ c.filter.coefs.a2=coefs.a2;
+
+
+ if (type_changed) {
+ //type changed reset filter
+ c.filter.old_coefs=c.filter.coefs;
+ c.mix.filter_l.ha[0]=0;
+ c.mix.filter_l.ha[1]=0;
+ c.mix.filter_l.hb[0]=0;
+ c.mix.filter_l.hb[1]=0;
+ c.mix.filter_r.ha[0]=0;
+ c.mix.filter_r.ha[1]=0;
+ c.mix.filter_r.hb[0]=0;
+ c.mix.filter_r.hb[1]=0;
+ }
+
+
+}
+void AudioMixerSW::channel_set_chorus(ChannelID p_channel, float p_chorus ) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.chorus_send=p_chorus;
+
+}
+void AudioMixerSW::channel_set_reverb(ChannelID p_channel, ReverbRoomType p_room_type, float p_reverb) {
+
+ ERR_FAIL_INDEX(p_room_type,MAX_REVERBS);
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.reverb_room=p_room_type;
+ c.reverb_send=p_reverb;
+
+}
+
+void AudioMixerSW::channel_set_mix_rate(ChannelID p_channel, int p_mix_rate) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.speed=p_mix_rate;
+
+}
+void AudioMixerSW::channel_set_positional(ChannelID p_channel, bool p_positional) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.positional=p_positional;
+}
+
+float AudioMixerSW::channel_get_volume(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ //Math::log( c.vol ) * 8.6858896380650365530225783783321;
+ return c.vol;
+}
+
+float AudioMixerSW::channel_get_pan(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.pan;
+}
+float AudioMixerSW::channel_get_pan_depth(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.depth;
+}
+float AudioMixerSW::channel_get_pan_height(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.height;
+
+}
+AudioMixer::FilterType AudioMixerSW::channel_get_filter_type(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return FILTER_NONE;
+
+ const Channel &c = channels[chan];
+ return c.filter.type;
+}
+float AudioMixerSW::channel_get_filter_cutoff(ChannelID p_channel) const {
+
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.filter.cutoff;
+
+}
+float AudioMixerSW::channel_get_filter_resonance(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.filter.resonance;
+
+}
+
+float AudioMixerSW::channel_get_filter_gain(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.filter.gain;
+}
+
+
+float AudioMixerSW::channel_get_chorus(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.chorus_send;
+
+}
+AudioMixer::ReverbRoomType AudioMixerSW::channel_get_reverb_type(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return REVERB_HALL;
+
+ const Channel &c = channels[chan];
+ return c.reverb_room;
+
+}
+float AudioMixerSW::channel_get_reverb(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.reverb_send;
+}
+
+int AudioMixerSW::channel_get_mix_rate(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.speed;
+}
+bool AudioMixerSW::channel_is_positional(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return false;
+
+ const Channel &c = channels[chan];
+ return c.positional;
+}
+
+bool AudioMixerSW::channel_is_valid(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return false;
+ return channels[chan].active;
+}
+
+
+void AudioMixerSW::channel_free(ChannelID p_channel) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c=channels[chan];
+
+ if (!c.active)
+ return;
+
+ bool has_vol=false;
+
+ for(int i=0;i<mix_channels;i++) {
+
+ if (c.mix.vol[i])
+ has_vol=true;
+ if (c.mix.reverb_vol[i])
+ has_vol=true;
+ if (c.mix.chorus_vol[i])
+ has_vol=true;
+ }
+ if (c.active && has_vol && inside_mix) {
+ // drive voice to zero, and run a chunk, the VRAMP will fade it good
+ c.vol=0;
+ c.reverb_send=0;
+ c.chorus_send=0;
+ mix_channel(c);
+ }
+ /* @TODO RAMP DOWN ON STOP */
+ c.active=false;
+}
+
+
+
+AudioMixerSW::AudioMixerSW(SampleManagerSW *p_sample_manager,int p_desired_latency_ms,int p_mix_rate,MixChannels p_mix_channels,bool p_use_fx,InterpolationType p_interp,MixStepCallback p_step_callback,void *p_step_udata) {
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_line("AudioServerSW Params: ");
+ print_line(" -mix chans: "+itos(p_mix_channels));
+ print_line(" -mix rate: "+itos(p_mix_rate));
+ print_line(" -latency: "+itos(p_desired_latency_ms));
+ print_line(" -fx: "+itos(p_use_fx));
+ print_line(" -interp: "+itos(p_interp));
+ }
+ sample_manager=p_sample_manager;
+ mix_channels=p_mix_channels;
+ mix_rate=p_mix_rate;
+ step_callback=p_step_callback;
+ step_udata=p_step_udata;
+
+
+ mix_chunk_bits=nearest_shift( p_desired_latency_ms * p_mix_rate / 1000 );
+
+ mix_chunk_size=(1<<mix_chunk_bits);
+ mix_chunk_mask=mix_chunk_size-1;
+ mix_buffer = memnew_arr(int32_t,mix_chunk_size*mix_channels);
+#ifndef NO_REVERB
+ zero_buffer = memnew_arr(int32_t,mix_chunk_size*mix_channels);
+ for(int i=0;i<mix_chunk_size*mix_channels;i++)
+ zero_buffer[i]=0; //zero buffer is zero...
+
+ max_reverbs=MAX_REVERBS;
+ int reverberators=mix_channels/2;
+
+ reverb_state = memnew_arr(ReverbState,max_reverbs);
+ for(int i=0;i<max_reverbs;i++) {
+ reverb_state[i].enabled=false;
+ reverb_state[i].reverb = memnew_arr(ReverbSW,reverberators);
+ reverb_state[i].buffer = memnew_arr(int32_t,mix_chunk_size*mix_channels);
+ reverb_state[i].frames_idle=0;
+ for(int j=0;j<reverberators;j++) {
+ static ReverbSW::ReverbMode modes[MAX_REVERBS]={ReverbSW::REVERB_MODE_STUDIO_SMALL,ReverbSW::REVERB_MODE_STUDIO_MEDIUM,ReverbSW::REVERB_MODE_STUDIO_LARGE,ReverbSW::REVERB_MODE_HALL};
+ reverb_state[i].reverb[j].set_mix_rate(p_mix_rate);
+ reverb_state[i].reverb[j].set_mode(modes[i]);
+ }
+
+ }
+ fx_enabled=p_use_fx;
+#else
+ fx_enabled=false;
+#endif
+ mix_chunk_left=0;
+
+ interpolation_type=p_interp;
+ channel_id_count=1;
+ inside_mix=false;
+ channel_nrg=1.0;
+
+}
+
+void AudioMixerSW::set_mixer_volume(float p_volume) {
+
+ channel_nrg=p_volume;
+}
+
+AudioMixerSW::~AudioMixerSW() {
+
+ memdelete_arr(mix_buffer);
+
+#ifndef NO_REVERB
+ memdelete_arr(zero_buffer);
+ for(int i=0;i<max_reverbs;i++) {
+ memdelete_arr(reverb_state[i].reverb);
+ memdelete_arr(reverb_state[i].buffer);
+ }
+ memdelete_arr(reverb_state);
+#endif
+
+
+}
diff --git a/servers/audio/audio_mixer_sw.h b/servers/audio/audio_mixer_sw.h
new file mode 100644
index 0000000000..eb3feee1c8
--- /dev/null
+++ b/servers/audio/audio_mixer_sw.h
@@ -0,0 +1,248 @@
+/*************************************************************************/
+/* audio_mixer_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_MIXER_SW_H
+#define AUDIO_MIXER_SW_H
+
+#include "servers/audio_server.h"
+#include "servers/audio/sample_manager_sw.h"
+#include "servers/audio/audio_filter_sw.h"
+#include "servers/audio/reverb_sw.h"
+
+class AudioMixerSW : public AudioMixer {
+public:
+
+ enum InterpolationType {
+
+ INTERPOLATION_RAW,
+ INTERPOLATION_LINEAR,
+ INTERPOLATION_CUBIC
+ };
+
+ enum MixChannels {
+
+ MIX_STEREO=2,
+ MIX_QUAD=4
+ };
+
+ typedef void (*MixStepCallback)(void*);
+
+private:
+ SampleManagerSW *sample_manager;
+
+ enum {
+
+ MAX_CHANNELS=64,
+ // fixed point defs
+
+ MIX_FRAC_BITS=13,
+ MIX_FRAC_LEN=(1<<MIX_FRAC_BITS),
+ MIX_FRAC_MASK=MIX_FRAC_LEN-1,
+ MIX_VOL_FRAC_BITS=12,
+ MIX_VOLRAMP_FRAC_BITS=16,
+ MIX_VOLRAMP_FRAC_LEN=(1<<MIX_VOLRAMP_FRAC_BITS),
+ MIX_VOLRAMP_FRAC_MASK=MIX_VOLRAMP_FRAC_LEN-1,
+ MIX_FILTER_FRAC_BITS=16,
+ MIX_FILTER_RAMP_FRAC_BITS=8,
+ MIX_VOL_MOVE_TO_24=4,
+ MAX_REVERBS=4
+ };
+
+ struct Channel {
+
+ RID sample;
+ struct Mix {
+ int64_t offset;
+ int32_t increment;
+
+ int32_t vol[4];
+ int32_t reverb_vol[4];
+ int32_t chorus_vol[4];
+
+ int32_t old_vol[4];
+ int32_t old_reverb_vol[4];
+ int32_t old_chorus_vol[4];
+
+
+ struct Filter { //history (stereo)
+ float ha[2],hb[2];
+ } filter_l,filter_r;
+
+ } mix;
+
+ float vol;
+ float pan;
+ float depth;
+ float height;
+
+ float chorus_send;
+ ReverbRoomType reverb_room;
+ float reverb_send;
+ int speed;
+ int check;
+ bool positional;
+
+ bool had_prev_reverb;
+ bool had_prev_chorus;
+ bool had_prev_vol;
+
+ struct Filter {
+
+ bool dirty;
+
+ FilterType type;
+ float cutoff;
+ float resonance;
+ float gain;
+
+ struct Coefs {
+
+ float a1,a2,b0,b1,b2; // fixed point coefficients
+ } coefs,old_coefs;
+
+ } filter;
+
+ bool first_mix;
+ bool active;
+ Channel() { active=false; check=-1; first_mix=false; filter.dirty=true; filter.type=FILTER_NONE; filter.cutoff=8000; filter.resonance=0; filter.gain=0; }
+ };
+
+ Channel channels[MAX_CHANNELS];
+
+ uint32_t mix_rate;
+ bool fx_enabled;
+ InterpolationType interpolation_type;
+
+ int mix_chunk_bits;
+ int mix_chunk_size;
+ int mix_chunk_mask;
+
+ int32_t *mix_buffer;
+ int32_t *zero_buffer; // fx feed when no input was mixed
+
+ struct ResamplerState {
+
+ uint32_t amount;
+ int32_t increment;
+
+
+ int32_t pos;
+
+
+ int32_t vol[4];
+ int32_t reverb_vol[4];
+ int32_t chorus_vol[4];
+
+ int32_t vol_inc[4];
+ int32_t reverb_vol_inc[4];
+ int32_t chorus_vol_inc[4];
+
+
+ Channel::Mix::Filter *filter_l;
+ Channel::Mix::Filter *filter_r;
+ Channel::Filter::Coefs coefs;
+ Channel::Filter::Coefs coefs_inc;
+
+ int32_t *reverb_buffer;
+ };
+
+
+
+ template<class Depth,bool is_stereo,bool use_filter,bool use_fx,InterpolationType type,MixChannels>
+ _FORCE_INLINE_ void do_resample(const Depth* p_src, int32_t *p_dst, ResamplerState *p_state);
+
+ MixChannels mix_channels;
+
+ void mix_channel(Channel& p_channel);
+ int mix_chunk_left;
+ void mix_chunk();
+
+ float channel_nrg;
+ int channel_id_count;
+ bool inside_mix;
+ MixStepCallback step_callback;
+ void *step_udata;
+ _FORCE_INLINE_ int _get_channel(ChannelID p_channel) const;
+
+ int max_reverbs;
+ struct ReverbState {
+
+ bool used_in_chunk;
+ bool enabled;
+ ReverbSW *reverb;
+ int frames_idle;
+ int32_t *buffer; //reverb is sent here
+ ReverbState() { enabled=false; frames_idle=0; used_in_chunk=false; }
+ };
+
+ ReverbState *reverb_state;
+
+
+public:
+
+
+ virtual ChannelID channel_alloc(RID p_sample);
+
+ virtual void channel_set_volume(ChannelID p_channel, float p_gain);
+ virtual void channel_set_pan(ChannelID p_channel, float p_pan, float p_depth=0,float height=0); //pan and depth go from -1 to 1
+ virtual void channel_set_filter(ChannelID p_channel, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=1.0);
+ virtual void channel_set_chorus(ChannelID p_channel, float p_chorus );
+ virtual void channel_set_reverb(ChannelID p_channel, ReverbRoomType p_room_type, float p_reverb);
+ virtual void channel_set_mix_rate(ChannelID p_channel, int p_mix_rate);
+ virtual void channel_set_positional(ChannelID p_channel, bool p_positional);
+
+ virtual float channel_get_volume(ChannelID p_channel) const;
+ virtual float channel_get_pan(ChannelID p_channel) const; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_depth(ChannelID p_channel) const; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_height(ChannelID p_channel) const; //pan and depth go from -1 to 1
+ virtual FilterType channel_get_filter_type(ChannelID p_channel) const;
+ virtual float channel_get_filter_cutoff(ChannelID p_channel) const;
+ virtual float channel_get_filter_resonance(ChannelID p_channel) const;
+ virtual float channel_get_filter_gain(ChannelID p_channel) const;
+
+ virtual float channel_get_chorus(ChannelID p_channel) const;
+ virtual ReverbRoomType channel_get_reverb_type(ChannelID p_channel) const;
+ virtual float channel_get_reverb(ChannelID p_channel) const;
+
+ virtual int channel_get_mix_rate(ChannelID p_channel) const;
+ virtual bool channel_is_positional(ChannelID p_channel) const;
+
+ virtual bool channel_is_valid(ChannelID p_channel) const;
+
+ virtual void channel_free(ChannelID p_channel);
+
+ int mix(int32_t *p_buffer,int p_frames); //return amount of mixsteps
+ uint64_t get_step_usecs() const;
+
+ virtual void set_mixer_volume(float p_volume);
+
+ AudioMixerSW(SampleManagerSW *p_sample_manager,int p_desired_latency_ms,int p_mix_rate,MixChannels p_mix_channels,bool p_use_fx=true,InterpolationType p_interp=INTERPOLATION_LINEAR,MixStepCallback p_step_callback=NULL,void *p_callback_udata=NULL);
+ ~AudioMixerSW();
+};
+
+#endif // AUDIO_MIXER_SW_H
diff --git a/servers/audio/audio_server_sw.cpp b/servers/audio/audio_server_sw.cpp
new file mode 100644
index 0000000000..069bc414f9
--- /dev/null
+++ b/servers/audio/audio_server_sw.cpp
@@ -0,0 +1,1012 @@
+/*************************************************************************/
+/* audio_server_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_server_sw.h"
+#include "globals.h"
+#include "os/os.h"
+
+struct _AudioDriverLock {
+
+ _AudioDriverLock() { if (AudioDriverSW::get_singleton()) AudioDriverSW::get_singleton()->lock(); }
+ ~_AudioDriverLock() { if (AudioDriverSW::get_singleton()) AudioDriverSW::get_singleton()->unlock(); }
+
+};
+
+#define AUDIO_LOCK _AudioDriverLock _adlock;
+
+AudioMixer *AudioServerSW::get_mixer() {
+
+ return mixer;
+}
+
+/* CALLBACKS */
+
+void AudioServerSW::audio_mixer_chunk_callback(int p_frames) {
+/*
+ for(List<Stream*>::Element *E=event_streams.front();E;E=E->next()) {
+
+ if (E->get()->active)
+ E->get()->audio_stream->mix(NULL,p_frames);
+ }
+*/
+}
+
+void AudioServerSW::_mixer_callback(void *p_udata) {
+
+ AudioServerSW *self = (AudioServerSW*)p_udata;
+ for(List<Stream*>::Element *E=self->active_audio_streams.front();E;E=E->next()) {
+
+ if (!E->get()->active)
+ continue;
+
+ EventStream *es=E->get()->event_stream;
+ if (!es)
+ continue;
+
+ es->update(self->mixer_step_usecs);
+ }
+
+}
+
+void AudioServerSW::driver_process_chunk(int p_frames,int32_t *p_buffer) {
+
+
+
+ int samples=p_frames*internal_buffer_channels;
+
+ for(int i=0;i<samples;i++) {
+ internal_buffer[i]=0;
+ }
+
+ while(voice_rb.commands_left()) {
+
+ VoiceRBSW::Command cmd = voice_rb.pop_command();
+
+ if (cmd.type==VoiceRBSW::Command::CMD_CHANGE_ALL_FX_VOLUMES) {
+
+ SelfList<Voice>*al = active_list.first();
+ while(al) {
+
+ Voice *v=al->self();
+ if (v->channel!=AudioMixer::INVALID_CHANNEL) {
+ mixer->channel_set_volume(v->channel,v->volume*fx_volume_scale);
+ }
+ al=al->next();
+ }
+
+ continue;
+ }
+ if (!voice_owner.owns(cmd.voice))
+ continue;
+
+
+ Voice *v = voice_owner.get(cmd.voice);
+
+ switch(cmd.type) {
+ case VoiceRBSW::Command::CMD_NONE: {
+
+
+ } break;
+ case VoiceRBSW::Command::CMD_PLAY: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_free(v->channel);
+
+ RID sample = cmd.play.sample;
+ if (!sample_manager->is_sample(sample))
+ continue;
+
+ v->channel=mixer->channel_alloc(sample);
+ v->volume=1.0;
+ mixer->channel_set_volume(v->channel,fx_volume_scale);
+ if (v->channel==AudioMixer::INVALID_CHANNEL) {
+#ifdef AUDIO_DEBUG
+ WARN_PRINT("AUDIO: all channels used, failed to allocate voice");
+#endif
+ v->active=false;
+ break; // no voices left?
+ }
+
+ v->active=true; // this kind of ensures it works
+ if (!v->active_item.in_list())
+ active_list.add(&v->active_item);
+
+ } break;
+ case VoiceRBSW::Command::CMD_STOP: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL) {
+ mixer->channel_free(v->channel);
+ if (v->active_item.in_list()) {
+ active_list.remove(&v->active_item);
+ }
+ }
+ v->active=false;
+ } break;
+ case VoiceRBSW::Command::CMD_SET_VOLUME: {
+
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL) {
+ v->volume=cmd.volume.volume;
+ mixer->channel_set_volume(v->channel,cmd.volume.volume*fx_volume_scale);
+ }
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_PAN: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_pan(v->channel,cmd.pan.pan,cmd.pan.depth,cmd.pan.height);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_FILTER: {
+
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_filter(v->channel,(AudioMixer::FilterType)cmd.filter.type,cmd.filter.cutoff,cmd.filter.resonance,cmd.filter.gain);
+ } break;
+ case VoiceRBSW::Command::CMD_SET_CHORUS: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_chorus(v->channel,cmd.chorus.send);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_REVERB: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_reverb(v->channel,(AudioMixer::ReverbRoomType)cmd.reverb.room,cmd.reverb.send);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_MIX_RATE: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_mix_rate(v->channel,cmd.mix_rate.mix_rate);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_POSITIONAL: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_positional(v->channel,cmd.positional.positional);
+
+ } break;
+ default: {}
+
+ }
+ }
+
+ mixer->mix(internal_buffer,p_frames);
+ //uint64_t stepsize=mixer->get_step_usecs();
+
+
+ for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) {
+
+ ERR_CONTINUE(!E->get()->active); // bug?
+
+
+ AudioStream *as=E->get()->audio_stream;
+ if (!as)
+ continue;
+
+ int channels=as->get_channel_count();
+ if (channels==0)
+ continue; // does not want mix
+ if (!as->mix(stream_buffer,p_frames))
+ continue; //nothing was mixed!!
+
+ int32_t stream_vol_scale=(stream_volume*stream_volume_scale*E->get()->volume_scale)*(1<<STREAM_SCALE_BITS);
+
+#define STRSCALE(m_val) (((m_val>>STREAM_SCALE_BITS)*stream_vol_scale)>>8)
+ switch(internal_buffer_channels) {
+
+ case 2: {
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<p_frames;i++) {
+
+ internal_buffer[(i<<1)+0]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<1)+1]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<p_frames*2;i++) {
+
+ internal_buffer[i]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<p_frames;i++) {
+
+ internal_buffer[(i<<2)+0]+=STRSCALE((stream_buffer[(i<<2)+0]+stream_buffer[(i<<2)+2])>>1);
+ internal_buffer[(i<<2)+1]+=STRSCALE((stream_buffer[(i<<2)+1]+stream_buffer[(i<<2)+3])>>1);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 4: {
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<p_frames;i++) {
+
+ internal_buffer[(i<<2)+0]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<2)+1]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<2)+2]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<2)+3]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<p_frames*2;i++) {
+
+ internal_buffer[(i<<2)+0]+=STRSCALE(stream_buffer[(i<<1)+0]);
+ internal_buffer[(i<<2)+1]+=STRSCALE(stream_buffer[(i<<1)+1]);
+ internal_buffer[(i<<2)+2]+=STRSCALE(stream_buffer[(i<<1)+0]);
+ internal_buffer[(i<<2)+3]+=STRSCALE(stream_buffer[(i<<1)+1]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<p_frames*4;i++) {
+ internal_buffer[i]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 6: {
+
+
+ } break;
+ }
+
+#undef STRSCALE
+ }
+
+ SelfList<Voice> *activeE=active_list.first();
+ while(activeE) {
+
+ SelfList<Voice> *activeN=activeE->next();
+ if (activeE->self()->channel==AudioMixer::INVALID_CHANNEL || !mixer->channel_is_valid(activeE->self()->channel)) {
+
+ active_list.remove(activeE);
+ activeE->self()->active=false;
+
+ }
+ activeE=activeN;
+ }
+
+ uint32_t peak=0;
+ for(int i=0;i<samples;i++) {
+ //clamp to (1<<24) using branchless code
+ int32_t in = internal_buffer[i];
+#ifdef DEBUG_ENABLED
+ {
+ int mask = (in >> (32 - 1));
+ uint32_t p = (in + mask) ^ mask;
+ if (p>peak)
+ peak=p;
+ }
+#endif
+ int32_t lo = -0x800000, hi=0x7FFFFF;
+ lo-=in;
+ hi-=in;
+ in += (lo & ((lo < 0) - 1)) + (hi & ((hi > 0) - 1));
+ p_buffer[i]=in<<8;
+ }
+
+ if (peak>max_peak)
+ max_peak=peak;
+}
+
+void AudioServerSW::driver_process(int p_frames,int32_t *p_buffer) {
+
+
+ //process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE
+ int todo=p_frames;
+ while(todo) {
+
+ int tomix=MIN(todo,INTERNAL_BUFFER_SIZE);
+ driver_process_chunk(tomix,p_buffer);
+ p_buffer+=tomix;
+ todo-=tomix;
+ }
+
+
+}
+
+/* SAMPLE API */
+
+RID AudioServerSW::sample_create(SampleFormat p_format, bool p_stereo, int p_length) {
+
+ AUDIO_LOCK
+
+ return sample_manager->sample_create(p_format,p_stereo,p_length);
+}
+
+void AudioServerSW::sample_set_description(RID p_sample, const String& p_description) {
+
+ AUDIO_LOCK
+ sample_manager->sample_set_description(p_sample,p_description);
+}
+String AudioServerSW::sample_get_description(RID p_sample, const String& p_description) const {
+
+ AUDIO_LOCK
+ return sample_manager->sample_get_description(p_sample);
+}
+
+AS::SampleFormat AudioServerSW::sample_get_format(RID p_sample) const {
+ //AUDIO_LOCK
+ return sample_manager->sample_get_format(p_sample);
+}
+bool AudioServerSW::sample_is_stereo(RID p_sample) const {
+ //AUDIO_LOCK
+ return sample_manager->sample_is_stereo(p_sample);
+}
+int AudioServerSW::sample_get_length(RID p_sample) const {
+ ///AUDIO_LOCK
+ return sample_manager->sample_get_length(p_sample);
+}
+
+const void* AudioServerSW::sample_get_data_ptr(RID p_sample) const {
+ ///AUDIO_LOCK
+ return sample_manager->sample_get_data_ptr(p_sample);
+}
+
+void AudioServerSW::sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer) {
+ AUDIO_LOCK
+ sample_manager->sample_set_data(p_sample,p_buffer);
+}
+const DVector<uint8_t> AudioServerSW::sample_get_data(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_data(p_sample);
+}
+
+void AudioServerSW::sample_set_mix_rate(RID p_sample,int p_rate) {
+ AUDIO_LOCK
+ sample_manager->sample_set_mix_rate(p_sample,p_rate);
+}
+int AudioServerSW::sample_get_mix_rate(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_mix_rate(p_sample);
+}
+
+void AudioServerSW::sample_set_loop_format(RID p_sample,SampleLoopFormat p_format) {
+ AUDIO_LOCK
+ sample_manager->sample_set_loop_format(p_sample,p_format);
+}
+AS::SampleLoopFormat AudioServerSW::sample_get_loop_format(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_loop_format(p_sample);
+}
+
+void AudioServerSW::sample_set_loop_begin(RID p_sample,int p_pos) {
+ AUDIO_LOCK
+ sample_manager->sample_set_loop_begin(p_sample,p_pos);
+}
+int AudioServerSW::sample_get_loop_begin(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_loop_begin(p_sample);
+}
+
+void AudioServerSW::sample_set_loop_end(RID p_sample,int p_pos) {
+ AUDIO_LOCK
+ sample_manager->sample_set_loop_end(p_sample,p_pos);
+}
+int AudioServerSW::sample_get_loop_end(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_loop_end(p_sample);
+}
+
+/* VOICE API */
+
+RID AudioServerSW::voice_create() {
+
+ Voice * v = memnew( Voice );
+ v->channel=AudioMixer::INVALID_CHANNEL;
+
+ AUDIO_LOCK
+ return voice_owner.make_rid(v);
+
+}
+void AudioServerSW::voice_play(RID p_voice, RID p_sample) {
+
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND(!v);
+ v->active=true; // force actvive (will be disabled later i gues..)
+
+ //stop old, start new
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_PLAY;
+ cmd.voice=p_voice;
+ cmd.play.sample=p_sample;
+ voice_rb.push_command(cmd);
+
+}
+
+void AudioServerSW::voice_set_volume(RID p_voice, float p_db) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_VOLUME;
+ cmd.voice=p_voice;
+ cmd.volume.volume=p_db;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_pan(RID p_voice, float p_pan, float p_depth,float p_height) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_PAN;
+ cmd.voice=p_voice;
+ cmd.pan.pan=p_pan;
+ cmd.pan.depth=p_depth;
+ cmd.pan.height=p_height;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance,float p_gain) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_FILTER;
+ cmd.voice=p_voice;
+ cmd.filter.type=p_type;
+ cmd.filter.cutoff=p_cutoff;
+ cmd.filter.resonance=p_resonance;
+ cmd.filter.gain=p_gain;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_chorus(RID p_voice, float p_chorus ) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_CHORUS;
+ cmd.voice=p_voice;
+ cmd.chorus.send=p_chorus;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_REVERB;
+ cmd.voice=p_voice;
+ cmd.reverb.room=p_room_type;
+ cmd.reverb.send=p_reverb;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_mix_rate(RID p_voice, int p_mix_rate) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_MIX_RATE;
+ cmd.voice=p_voice;
+ cmd.mix_rate.mix_rate=p_mix_rate;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_positional(RID p_voice, bool p_positional) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_POSITIONAL;
+ cmd.voice=p_voice;
+ cmd.positional.positional=p_positional;
+ voice_rb.push_command(cmd);
+
+}
+
+float AudioServerSW::voice_get_volume(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_volume( v->channel );
+
+}
+float AudioServerSW::voice_get_pan(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_pan( v->channel );
+
+}
+float AudioServerSW::voice_get_pan_depth(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_pan_depth( v->channel );
+
+}
+float AudioServerSW::voice_get_pan_height(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_pan_height( v->channel );
+
+}
+AS::FilterType AudioServerSW::voice_get_filter_type(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, AS::FILTER_NONE);
+
+ return (AS::FilterType)mixer->channel_get_filter_type(v->channel);
+
+}
+float AudioServerSW::voice_get_filter_cutoff(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_filter_cutoff( v->channel );
+
+}
+float AudioServerSW::voice_get_filter_resonance(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_filter_resonance( v->channel );
+
+}
+float AudioServerSW::voice_get_chorus(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_chorus( v->channel );
+
+}
+AS::ReverbRoomType AudioServerSW::voice_get_reverb_type(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, REVERB_SMALL);
+
+ return (AS::ReverbRoomType)mixer->channel_get_reverb_type( v->channel );
+
+}
+float AudioServerSW::voice_get_reverb(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_reverb( v->channel );
+
+}
+
+int AudioServerSW::voice_get_mix_rate(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_mix_rate( v->channel );
+
+}
+bool AudioServerSW::voice_is_positional(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_is_positional( v->channel );
+
+}
+
+void AudioServerSW::voice_stop(RID p_voice) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_STOP;
+ cmd.voice=p_voice;
+ voice_rb.push_command(cmd);
+
+ //return mixer->channel_free( v->channel );
+
+}
+
+bool AudioServerSW::voice_is_active(RID p_voice) const {
+
+ Voice *v = voice_owner.get(p_voice);
+ ERR_FAIL_COND_V(!v,false);
+ return v->active;
+
+}
+
+/* STREAM API */
+
+RID AudioServerSW::audio_stream_create(AudioStream *p_stream) {
+
+ AUDIO_LOCK
+ Stream *s = memnew(Stream);
+ s->audio_stream=p_stream;
+ s->event_stream=NULL;
+ s->active=false;
+ s->E=NULL;
+ s->volume_scale=1.0;
+ p_stream->set_mix_rate(AudioDriverSW::get_singleton()->get_mix_rate());
+
+ return stream_owner.make_rid(s);
+}
+
+RID AudioServerSW::event_stream_create(EventStream *p_stream) {
+
+ AUDIO_LOCK
+ Stream *s = memnew(Stream);
+ s->audio_stream=NULL;
+ s->event_stream=p_stream;
+ s->active=false;
+ s->E=NULL;
+ s->volume_scale=1.0;
+ //p_stream->set_mix_rate(AudioDriverSW::get_singleton()->get_mix_rate());
+
+ return stream_owner.make_rid(s);
+
+
+}
+
+
+void AudioServerSW::stream_set_active(RID p_stream, bool p_active) {
+
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND(!s);
+
+ if (s->active==p_active)
+ return;
+ AUDIO_LOCK;
+ _THREAD_SAFE_METHOD_
+ s->active=p_active;
+ if (p_active)
+ s->E=active_audio_streams.push_back(s);
+ else {
+ active_audio_streams.erase(s->E);
+ s->E=NULL;
+ }
+}
+
+bool AudioServerSW::stream_is_active(RID p_stream) const {
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND_V(!s,false);
+ return s->active;
+}
+
+void AudioServerSW::stream_set_volume_scale(RID p_stream, float p_scale) {
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND(!s);
+ s->volume_scale=p_scale;
+
+}
+
+float AudioServerSW::stream_set_volume_scale(RID p_stream) const {
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND_V(!s,0);
+ return s->volume_scale;
+
+}
+
+
+void AudioServerSW::free(RID p_id) {
+
+ if(voice_owner.owns(p_id)) {
+
+ Voice *v = voice_owner.get(p_id);
+ AUDIO_LOCK
+ mixer->channel_free( v->channel );
+ voice_owner.free(p_id);
+ memdelete(v);
+
+ } else if (stream_owner.owns(p_id)) {
+
+
+ Stream *s=stream_owner.get(p_id);
+
+ if (s->active) {
+ stream_set_active(p_id,false);
+ }
+
+ memdelete(s);
+ stream_owner.free(p_id);
+
+ } else if (sample_manager->is_sample(p_id)) {
+
+ AUDIO_LOCK
+ sample_manager->free(p_id);
+ }
+
+}
+
+void AudioServerSW::_thread_func(void *self) {
+
+
+ AudioServerSW *as=(AudioServerSW *)self;
+
+ while (!as->exit_update_thread) {
+ as->_update_streams(true);
+ OS::get_singleton()->delay_usec(5000);
+ }
+
+}
+
+void AudioServerSW::init() {
+
+ int latency = GLOBAL_DEF("audio/mixer_latency",10);
+ internal_buffer_channels=2; // read from driver
+ internal_buffer = memnew_arr(int32_t,INTERNAL_BUFFER_SIZE*internal_buffer_channels);
+ stream_buffer = memnew_arr(int32_t,INTERNAL_BUFFER_SIZE*4); //max 4 channels
+ AudioMixerSW::MixChannels mix_chans = AudioMixerSW::MIX_STEREO;
+
+ switch(AudioDriverSW::get_singleton()->get_output_format()) {
+
+ case AudioDriverSW::OUTPUT_MONO:
+ case AudioDriverSW::OUTPUT_STEREO:
+ mix_chans=AudioMixerSW::MIX_STEREO;
+ break;
+ case AudioDriverSW::OUTPUT_QUAD:
+ case AudioDriverSW::OUTPUT_5_1:
+ mix_chans=AudioMixerSW::MIX_QUAD;
+ break;
+ }
+
+ mixer = memnew( AudioMixerSW( sample_manager, latency, AudioDriverSW::get_singleton()->get_mix_rate(),mix_chans,mixer_use_fx,mixer_interp,_mixer_callback,this ) );
+ mixer_step_usecs=mixer->get_step_usecs();
+
+ stream_volume=0.3;
+ // start the audio driver
+ if (AudioDriverSW::get_singleton())
+ AudioDriverSW::get_singleton()->start();
+
+#ifndef NO_THREADS
+ exit_update_thread=false;
+ thread = Thread::create(_thread_func,this);
+#endif
+
+}
+
+void AudioServerSW::finish() {
+
+#ifndef NO_THREADS
+ exit_update_thread=true;
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+#endif
+
+ if (AudioDriverSW::get_singleton())
+ AudioDriverSW::get_singleton()->finish();
+
+ memdelete_arr(internal_buffer);
+ memdelete_arr(stream_buffer);
+ memdelete(mixer);
+
+}
+
+void AudioServerSW::_update_streams(bool p_thread) {
+
+ _THREAD_SAFE_METHOD_
+ for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) {
+
+ if (E->get()->audio_stream && p_thread == E->get()->audio_stream->can_update_mt())
+ E->get()->audio_stream->update();
+ }
+
+}
+
+void AudioServerSW::update() {
+
+ _update_streams(false);
+#ifdef NO_THREADS
+
+ _update_streams(true);
+#endif
+}
+
+
+void AudioServerSW::lock() {
+
+ AudioDriverSW::get_singleton()->lock();
+}
+
+void AudioServerSW::unlock() {
+ AudioDriverSW::get_singleton()->unlock();
+
+}
+
+int AudioServerSW::get_default_mix_rate() const {
+
+ return AudioDriverSW::get_singleton()->get_mix_rate();
+}
+int AudioServerSW::get_default_channel_count() const {
+ return internal_buffer_channels;
+}
+
+void AudioServerSW::set_mixer_params(AudioMixerSW::InterpolationType p_interp, bool p_use_fx) {
+
+ mixer_interp=p_interp;
+ mixer_use_fx=p_use_fx;
+}
+
+void AudioServerSW::set_stream_global_volume_scale(float p_volume) {
+
+ stream_volume_scale=p_volume;
+}
+
+float AudioServerSW::get_stream_global_volume_scale() const {
+
+ return stream_volume_scale;
+
+
+}
+
+void AudioServerSW::set_fx_global_volume_scale(float p_volume) {
+
+ fx_volume_scale=p_volume;
+ //mixer->set_mixer_volume(fx_volume_scale);
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_CHANGE_ALL_FX_VOLUMES;
+ cmd.voice=RID();
+ cmd.volume.volume=p_volume;
+ voice_rb.push_command(cmd);
+
+}
+
+
+float AudioServerSW::get_fx_global_volume_scale() const {
+
+ return fx_volume_scale;
+}
+
+void AudioServerSW::set_event_voice_global_volume_scale(float p_volume) {
+
+ event_voice_volume_scale=p_volume;
+ //mixer->set_mixer_volume(event_voice_volume_scale);
+}
+
+
+float AudioServerSW::get_event_voice_global_volume_scale() const {
+
+ return event_voice_volume_scale;
+}
+
+double AudioServerSW::get_mix_time() const {
+
+ return AudioDriverSW::get_singleton()->get_mix_time();
+}
+
+uint32_t AudioServerSW::read_output_peak() const {
+
+ uint32_t val = max_peak;
+ uint32_t *p = (uint32_t*)&max_peak;
+ *p=0;
+ return val;
+}
+
+AudioServerSW::AudioServerSW(SampleManagerSW *p_sample_manager) {
+
+ sample_manager=p_sample_manager;
+ String interp = GLOBAL_DEF("audio/mixer_interp","linear");
+ Globals::get_singleton()->set_custom_property_info("audio/mixer",PropertyInfo(Variant::STRING,"audio/mixer",PROPERTY_HINT_ENUM,"raw,linear,cubic"));
+ if (interp=="raw")
+ mixer_interp=AudioMixerSW::INTERPOLATION_RAW;
+ else if (interp=="cubic")
+ mixer_interp=AudioMixerSW::INTERPOLATION_CUBIC;
+ else
+ mixer_interp=AudioMixerSW::INTERPOLATION_LINEAR;
+ mixer_use_fx = GLOBAL_DEF("audio/use_chorus_reverb",true);
+ stream_volume_scale=GLOBAL_DEF("audio/stream_volume_scale",1.0);
+ fx_volume_scale=GLOBAL_DEF("audio/fx_volume_scale",1.0);
+ event_voice_volume_scale=GLOBAL_DEF("audio/event_voice_volume_scale",0.5);
+ max_peak=0;
+
+
+}
+
+AudioServerSW::~AudioServerSW() {
+
+}
+
+
+AudioDriverSW *AudioDriverSW::singleton=NULL;
+AudioDriverSW *AudioDriverSW::get_singleton() {
+
+ return singleton;
+}
+
+void AudioDriverSW::set_singleton() {
+
+ singleton=this;
+}
+
+void AudioDriverSW::audio_server_process(int p_frames,int32_t *p_buffer,bool p_update_mix_time) {
+
+ AudioServerSW * audio_server = static_cast<AudioServerSW*>(AudioServer::get_singleton());
+ if (p_update_mix_time)
+ update_mix_time(p_frames);
+ audio_server->driver_process(p_frames,p_buffer);
+}
+
+void AudioDriverSW::update_mix_time(int p_frames) {
+
+ _mix_amount+=p_frames;
+ _last_mix_time=OS::get_singleton()->get_ticks_usec();
+}
+
+double AudioDriverSW::get_mix_time() const {
+
+ double total = (OS::get_singleton()->get_ticks_usec() - _last_mix_time) / 1000000.0;
+ total+=_mix_amount/(double)get_mix_rate();
+ return total;
+
+}
+
+
+AudioDriverSW::AudioDriverSW() {
+
+ _last_mix_time=0;
+ _mix_amount=0;
+}
+
+
+AudioDriverSW *AudioDriverManagerSW::drivers[MAX_DRIVERS];
+int AudioDriverManagerSW::driver_count=0;
+
+
+
+void AudioDriverManagerSW::add_driver(AudioDriverSW *p_driver) {
+
+ ERR_FAIL_COND(driver_count>=MAX_DRIVERS);
+ drivers[driver_count++]=p_driver;
+}
+
+int AudioDriverManagerSW::get_driver_count() {
+
+ return driver_count;
+}
+AudioDriverSW *AudioDriverManagerSW::get_driver(int p_driver) {
+
+ ERR_FAIL_INDEX_V(p_driver,driver_count,NULL);
+ return drivers[p_driver];
+}
+
diff --git a/servers/audio/audio_server_sw.h b/servers/audio/audio_server_sw.h
new file mode 100644
index 0000000000..d137c15633
--- /dev/null
+++ b/servers/audio/audio_server_sw.h
@@ -0,0 +1,279 @@
+/*************************************************************************/
+/* audio_server_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_SERVER_SW_H
+#define AUDIO_SERVER_SW_H
+
+#include "servers/audio_server.h"
+#include "servers/audio/audio_mixer_sw.h"
+#include "servers/audio/voice_rb_sw.h"
+#include "self_list.h"
+#include "os/thread_safe.h"
+#include "os/thread.h"
+class AudioServerSW : public AudioServer {
+
+ OBJ_TYPE( AudioServerSW, AudioServer );
+
+ _THREAD_SAFE_CLASS_
+
+ enum {
+ INTERNAL_BUFFER_SIZE=4096,
+ STREAM_SCALE_BITS=12
+
+ };
+
+ SampleManagerSW *sample_manager;
+ AudioMixerSW *mixer;
+
+ virtual AudioMixer *get_mixer();
+ virtual void audio_mixer_chunk_callback(int p_frames);
+
+ struct Voice {
+
+ float volume;
+ volatile bool active;
+ SelfList<Voice> active_item;
+ AudioMixer::ChannelID channel;
+
+
+ Voice () : active_item(this) { channel=AudioMixer::INVALID_CHANNEL; active=false;}
+ };
+
+ mutable RID_Owner<Voice> voice_owner;
+ SelfList<Voice>::List active_list;
+
+ struct Stream {
+ bool active;
+ List<Stream*>::Element *E;
+ AudioStream *audio_stream;
+ EventStream *event_stream;
+ float volume_scale;
+ };
+
+ List<Stream*> active_audio_streams;
+
+ //List<Stream*> event_streams;
+
+ int32_t * internal_buffer;
+ int internal_buffer_channels;
+ int32_t * stream_buffer;
+
+ mutable RID_Owner<Stream> stream_owner;
+
+ float stream_volume;
+ float stream_volume_scale;
+ float fx_volume_scale;
+ float event_voice_volume_scale;
+ float peak_left,peak_right;
+ uint32_t max_peak;
+
+ VoiceRBSW voice_rb;
+
+ bool exit_update_thread;
+ Thread *thread;
+ static void _thread_func(void *self);
+
+ void _update_streams(bool p_thread);
+ void driver_process_chunk(int p_frames,int32_t *p_buffer);
+
+ AudioMixerSW::InterpolationType mixer_interp;
+ bool mixer_use_fx;
+ uint64_t mixer_step_usecs;
+
+ static void _mixer_callback(void *p_udata);
+friend class AudioDriverSW;
+ void driver_process(int p_frames,int32_t *p_buffer);
+public:
+
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(SampleFormat p_format, bool p_stereo, int p_length);
+
+ virtual void sample_set_description(RID p_sample, const String& p_description);
+ virtual String sample_get_description(RID p_sample, const String& p_description) const;
+
+ virtual SampleFormat sample_get_format(RID p_sample) const;
+ virtual bool sample_is_stereo(RID p_sample) const;
+ virtual int sample_get_length(RID p_sample) const;
+ const void* sample_get_data_ptr(RID p_sample) const;
+
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer);
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate);
+ virtual int sample_get_mix_rate(RID p_sample) const;
+
+ virtual void sample_set_loop_format(RID p_sample,SampleLoopFormat p_format);
+ virtual SampleLoopFormat sample_get_loop_format(RID p_sample) const;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos);
+ virtual int sample_get_loop_begin(RID p_sample) const;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos);
+ virtual int sample_get_loop_end(RID p_sample) const;
+
+ /* VOICE API */
+
+ virtual RID voice_create();
+
+ virtual void voice_play(RID p_voice, RID p_sample);
+
+ virtual void voice_set_volume(RID p_voice, float p_db);
+ virtual void voice_set_pan(RID p_voice, float p_pan, float p_depth=0,float height=0); //pan and depth go from -1 to 1
+ virtual void voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance,float p_gain=0);
+ virtual void voice_set_chorus(RID p_voice, float p_chorus );
+ virtual void voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb);
+ virtual void voice_set_mix_rate(RID p_voice, int p_mix_rate);
+ virtual void voice_set_positional(RID p_voice, bool p_positional);
+
+ virtual float voice_get_volume(RID p_voice) const;
+ virtual float voice_get_pan(RID p_voice) const; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_depth(RID p_voice) const; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_height(RID p_voice) const; //pan and depth go from -1 to 1
+ virtual FilterType voice_get_filter_type(RID p_voice) const;
+ virtual float voice_get_filter_cutoff(RID p_voice) const;
+ virtual float voice_get_filter_resonance(RID p_voice) const;
+ virtual float voice_get_chorus(RID p_voice) const;
+ virtual ReverbRoomType voice_get_reverb_type(RID p_voice) const;
+ virtual float voice_get_reverb(RID p_voice) const;
+
+ virtual int voice_get_mix_rate(RID p_voice) const;
+ virtual bool voice_is_positional(RID p_voice) const;
+
+ virtual void voice_stop(RID p_voice);
+ virtual bool voice_is_active(RID p_voice) const;
+
+ /* STREAM API */
+
+ virtual RID audio_stream_create(AudioStream *p_stream);
+ virtual RID event_stream_create(EventStream *p_stream);
+
+ virtual void stream_set_active(RID p_stream, bool p_active);
+ virtual bool stream_is_active(RID p_stream) const;
+
+ virtual void stream_set_volume_scale(RID p_stream, float p_scale);
+ virtual float stream_set_volume_scale(RID p_stream) const;
+
+ virtual void free(RID p_id);
+
+ virtual void init();
+ virtual void finish();
+ virtual void update();
+
+ virtual void lock();
+ virtual void unlock();
+ virtual int get_default_channel_count() const;
+ virtual int get_default_mix_rate() const;
+
+ void set_mixer_params(AudioMixerSW::InterpolationType p_interp, bool p_use_fx);
+
+ virtual void set_stream_global_volume_scale(float p_volume);
+ virtual void set_fx_global_volume_scale(float p_volume);
+ virtual void set_event_voice_global_volume_scale(float p_volume);
+
+
+ virtual float get_stream_global_volume_scale() const;
+ virtual float get_fx_global_volume_scale() const;
+ virtual float get_event_voice_global_volume_scale() const;
+
+ virtual uint32_t read_output_peak() const;
+
+ virtual double get_mix_time() const; //useful for video -> audio sync
+
+ AudioServerSW(SampleManagerSW *p_sample_manager);
+ ~AudioServerSW();
+
+};
+
+
+class AudioDriverSW {
+
+
+ static AudioDriverSW *singleton;
+ uint64_t _last_mix_time;
+ uint64_t _mix_amount;
+
+
+protected:
+
+ void audio_server_process(int p_frames,int32_t *p_buffer,bool p_update_mix_time=true);
+ void update_mix_time(int p_frames);
+
+public:
+
+
+ double get_mix_time() const; //useful for video -> audio sync
+
+ enum OutputFormat {
+
+ OUTPUT_MONO,
+ OUTPUT_STEREO,
+ OUTPUT_QUAD,
+ OUTPUT_5_1
+ };
+
+ static AudioDriverSW *get_singleton();
+ void set_singleton();
+
+ virtual const char* get_name() const=0;
+
+ virtual Error init()=0;
+ virtual void start()=0;
+ virtual int get_mix_rate() const =0;
+ virtual OutputFormat get_output_format() const=0;
+ virtual void lock()=0;
+ virtual void unlock()=0;
+ virtual void finish()=0;
+
+
+
+
+ AudioDriverSW();
+ virtual ~AudioDriverSW() {};
+};
+
+
+
+class AudioDriverManagerSW {
+
+ enum {
+
+ MAX_DRIVERS=10
+ };
+
+ static AudioDriverSW *drivers[MAX_DRIVERS];
+ static int driver_count;
+public:
+
+ static void add_driver(AudioDriverSW *p_driver);
+ static int get_driver_count();
+ static AudioDriverSW *get_driver(int p_driver);
+};
+
+#endif // AUDIO_SERVER_SW_H
diff --git a/servers/audio/reverb_buffers_sw.cpp b/servers/audio/reverb_buffers_sw.cpp
new file mode 100644
index 0000000000..6d09be9a3d
--- /dev/null
+++ b/servers/audio/reverb_buffers_sw.cpp
@@ -0,0 +1,33 @@
+/*************************************************************************/
+/* reverb_buffers_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_buffers_sw.h"
+
+ReverbBuffersSW::ReverbBuffersSW()
+{
+}
diff --git a/servers/audio/reverb_buffers_sw.h b/servers/audio/reverb_buffers_sw.h
new file mode 100644
index 0000000000..64a9e4fe7d
--- /dev/null
+++ b/servers/audio/reverb_buffers_sw.h
@@ -0,0 +1,38 @@
+/*************************************************************************/
+/* reverb_buffers_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef REVERB_BUFFERS_SW_H
+#define REVERB_BUFFERS_SW_H
+
+class ReverbBuffersSW
+{
+public:
+ ReverbBuffersSW();
+};
+
+#endif // REVERB_BUFFERS_SW_H
diff --git a/servers/audio/reverb_sw.cpp b/servers/audio/reverb_sw.cpp
new file mode 100644
index 0000000000..df36886db8
--- /dev/null
+++ b/servers/audio/reverb_sw.cpp
@@ -0,0 +1,569 @@
+/*************************************************************************/
+/* reverb_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_sw.h"
+#include "stdlib.h"
+#include "print_string.h"
+#define SETMIN( x, y ) (x) = MIN ( (x), (y) )
+#define rangeloop( c, min, max ) \
+ for ( (c) = (min) ; (c) < (max) ; (c)++ )
+
+#define ABSDIFF(x, y)\
+ ( ((x) < (y)) ? ((y) - (x)) : ((x) - (y)) )
+
+
+#ifdef bleh_MSC_VER
+
+#if _MSC_VER >= 1400
+ _FORCE_INLINE_ int32_tMULSHIFT_S32 (
+ int32_t Factor1,
+ int32_t Factor2,
+ uint8_t Bits
+ ) {
+
+ return __ll_rshift (
+ __emul ( Factor1, Factor2 ),
+ Bits
+ );
+ }
+#endif
+
+#else
+#define MULSHIFT_S32( Factor1, Factor2, Bits )\
+ ( (int) (( (int64_t)(Factor1) * (Factor2) ) >> (Bits)) )
+#endif
+
+
+
+struct ReverbParamsSW {
+ unsigned int BufferSize; // Required buffer size
+ int gLPF; // Coefficient
+ int gEcho0; // Coefficient
+ int gEcho1; // Coefficient
+ int gEcho2; // Coefficient
+ int gEcho3; // Coefficient
+ int gWall; // Coefficient
+ int gReva; // Coefficient
+ int gRevb; // Coefficient
+ int gInputL; // Coefficient
+ int gInputR; // Coefficient
+ unsigned int nRevaOldL; // Offset
+ unsigned int nRevaOldR; // Offset
+ unsigned int nRevbOldL; // Offset
+ unsigned int nRevbOldR; // Offset
+ unsigned int nLwlNew; // Offset
+ unsigned int nRwrNew; // Offset
+ unsigned int nEcho0L; // Offset
+ unsigned int nEcho0R; // Offset
+ unsigned int nEcho1L; // Offset
+ unsigned int nEcho1R; // Offset
+ unsigned int nLwlOld; // Offset
+ unsigned int nRwrOld; // Offset
+ unsigned int nLwrNew; // Offset
+ unsigned int nRwlNew; // Offset
+ unsigned int nEcho2L; // Offset
+ unsigned int nEcho2R; // Offset
+ unsigned int nEcho3L; // Offset
+ unsigned int nEcho3R; // Offset
+ unsigned int nLwrOld; // Offset
+ unsigned int nRwlOld; // Offset
+ unsigned int nRevaNewL; // Offset
+ unsigned int nRevaNewR; // Offset
+ unsigned int nRevbNewL; // Offset
+ unsigned int nRevbNewR; // Offset
+};
+
+static ReverbParamsSW reverb_params_Room = {
+ 0x26C0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x6D80, 0x54B8, -0x4130, 0x0000, 0x0000, -0x4580,
+// gReva gRevb gInputL gInputR
+ 0x5800, 0x5300, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x01B4 - 0x007D, 0x0136 - 0x007D, 0x00B8 - 0x005B, 0x005C - 0x005B,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x04D6, 0x0333, 0x03F0, 0x0227, 0x0374, 0x01EF,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x0334, 0x01B5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x0000, 0x0000, 0x01B4, 0x0136, 0x00B8, 0x005C
+};
+
+static ReverbParamsSW reverb_params_StudioSmall = {
+ 0x1F40/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x70F0, 0x4FA8, -0x4320, 0x4410, -0x3F10, -0x6400,
+// gReva gRevb gInputL gInputR
+ 0x5280, 0x4EC0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x00B4 - 0x0033, 0x0080 - 0x0033, 0x004C - 0x0025, 0x0026 - 0x0025,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x03E4, 0x031B, 0x03A4, 0x02AF, 0x0372, 0x0266,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x031C, 0x025D, 0x025C, 0x018E, 0x022F, 0x0135, 0x01D2, 0x00B7,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x018F, 0x00B5, 0x00B4, 0x0080, 0x004C, 0x0026
+};
+
+static ReverbParamsSW reverb_params_StudioMedium = {
+ 0x4840/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x70F0, 0x4FA8, -0x4320, 0x4510, -0x4110, -0x4B40,
+// gReva gRevb gInputL gInputR
+ 0x5280, 0x4EC0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x0264 - 0x00B1, 0x01B2 - 0x00B1, 0x0100 - 0x007F, 0x0080 - 0x007F,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x0904, 0x076B, 0x0824, 0x065F, 0x07A2, 0x0616,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x076C, 0x05ED, 0x05EC, 0x042E, 0x050F, 0x0305, 0x0462, 0x02B7,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x042F, 0x0265, 0x0264, 0x01B2, 0x0100, 0x0080
+};
+
+static ReverbParamsSW reverb_params_StudioLarge = {
+ 0x6FE0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x6F60, 0x4FA8, -0x4320, 0x4510, -0x4110, -0x5980,
+// gReva gRevb gInputL gInputR
+ 0x5680, 0x52C0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x031C - 0x00E3, 0x0238 - 0x00E3, 0x0154 - 0x00A9, 0x00AA - 0x00A9,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x0DFB, 0x0B58, 0x0D09, 0x0A3C, 0x0BD9, 0x0973,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x0B59, 0x08DA, 0x08D9, 0x05E9, 0x07EC, 0x04B0, 0x06EF, 0x03D2,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x05EA, 0x031D, 0x031C, 0x0238, 0x0154, 0x00AA
+};
+
+static ReverbParamsSW reverb_params_Hall = {
+ 0xADE0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x6000, 0x5000, 0x4C00, -0x4800, -0x4400, -0x4000,
+// gReva gRevb gInputL gInputR
+ 0x6000, 0x5C00, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x05C0 - 0x01A5, 0x041A - 0x01A5, 0x0274 - 0x0139, 0x013A - 0x0139,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x15BA, 0x11BB, 0x14C2, 0x10BD, 0x11BC, 0x0DC1,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x11C0, 0x0DC3, 0x0DC0, 0x09C1, 0x0BC4, 0x07C1, 0x0A00, 0x06CD,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x09C2, 0x05C1, 0x05C0, 0x041A, 0x0274, 0x013A
+};
+
+static ReverbParamsSW reverb_params_SpaceEcho = {
+ 0xF6C0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x7E00, 0x5000, -0x4C00, -0x5000, 0x4C00, -0x5000,
+// gReva gRevb gInputL gInputR
+ 0x6000, 0x5400, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x0AE0 - 0x033D, 0x07A2 - 0x033D, 0x0464 - 0x0231, 0x0232 - 0x0231,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x1ED6, 0x1A31, 0x1D14, 0x183B, 0x1BC2, 0x16B2,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x1A32, 0x15EF, 0x15EE, 0x1055, 0x1334, 0x0F2D, 0x11F6, 0x0C5D,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x1056, 0x0AE1, 0x0AE0, 0x07A2, 0x0464, 0x0232
+};
+
+static ReverbParamsSW reverb_params_Echo = {
+ 0x18040/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x7FFF, 0x7FFF, 0x0000, 0x0000, 0x0000, -0x7F00,
+// gReva gRevb gInputL gInputR
+ 0x0000, 0x0000, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x1004 - 0x0001, 0x1002 - 0x0001, 0x0004 - 0x0001, 0x0002 - 0x0001,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x1FFF, 0x0FFF, 0x1005, 0x0005, 0x0000, 0x0000,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x1005, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x0000, 0x0000, 0x1004, 0x1002, 0x0004, 0x0002
+};
+
+static ReverbParamsSW reverb_params_Delay = {
+ 0x18040/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x7FFF, 0x7FFF, 0x0000, 0x0000, 0x0000, 0x0000,
+// gReva gRevb gInputL gInputR
+ 0x0000, 0x0000, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x1004 - 0x0001, 0x1002 - 0x0001, 0x0004 - 0x0001, 0x0002 - 0x0001,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x1FFF, 0x0FFF, 0x1005, 0x0005, 0x0000, 0x0000,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x1005, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x0000, 0x0000, 0x1004, 0x1002, 0x0004, 0x0002
+};
+
+static ReverbParamsSW reverb_params_HalfEcho = {
+ 0x3C00/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x70F0, 0x4FA8, -0x4320, 0x4510, -0x4110, -0x7B00,
+// gReva gRevb gInputL gInputR
+ 0x5F80, 0x54C0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x0058 - 0x0017, 0x0040 - 0x0017, 0x0028 - 0x0013, 0x0014 - 0x0013,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x0371, 0x02AF, 0x02E5, 0x01DF, 0x02B0, 0x01D7,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x0358, 0x026A, 0x01D6, 0x011E, 0x012D, 0x00B1, 0x011F, 0x0059,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x01A0, 0x00E3, 0x0058, 0x0040, 0x0028, 0x0014
+};
+
+
+static ReverbParamsSW * reverb_param_modes[] = {
+ &reverb_params_Room,
+ &reverb_params_StudioSmall,
+ &reverb_params_StudioMedium,
+ &reverb_params_StudioLarge,
+ &reverb_params_Hall,
+ &reverb_params_SpaceEcho,
+ &reverb_params_Echo,
+ &reverb_params_Delay,
+ &reverb_params_HalfEcho,
+};
+
+bool ReverbSW::process(int *p_input,int *p_output,int p_frames,int p_stereo_stride) {
+
+ if (!reverb_buffer)
+ return false;
+
+
+ //
+ // p_input must point to a non-looping buffer.
+ // BOTH p_input and p_output must be touched (use ClearModuleBuffer).
+
+ // �������LOCAL MACROS ������۲
+
+#undef LM_SETSRCOFFSET
+#define LM_SETSRCOFFSET(x) \
+ (x) = current_params->x + Offset; \
+ if ( (x) >= reverb_buffer_size ) { \
+ (x) -= reverb_buffer_size; \
+ } \
+ SETMIN ( aSample, reverb_buffer_size - (x) );
+
+/*
+#undef LM_SETSRCOFFSET2
+#define LM_SETSRCOFFSET2(x,y) \
+ (x) = ((y) << 3) >> HZShift; \
+ (x) += Offset; \
+ if ( (x) >= reverb_buffer_size ) { \
+ (x) -= reverb_buffer_size; \
+ } \
+ SETMIN ( aSample, reverb_buffer_size - (x) );
+*/
+#undef LM_SRCADVANCE
+#define LM_SRCADVANCE(x) \
+ (x) += aSample;
+
+#undef LM_MUL
+#define LM_MUL(x,y) \
+MULSHIFT_S32 ( x, current_params->y, 15 )
+
+#undef LM_REVERB
+#define LM_REVERB(x) reverb_buffer[ (x) + cSample ]
+
+ // �������LOCAL VARIABLES ������۲
+
+ unsigned int Offset;
+
+ int lwl, lwr, rwl, rwr;
+// unsigned char HZShift;
+
+ // �������CODE ������۲
+
+
+ lwl = state.lwl;
+ lwr = state.lwr;
+ rwl = state.rwl;
+ rwr = state.rwr;
+ Offset = state.Offset;
+
+ int max=0;
+
+ while ( p_frames ) {
+
+ // Offsets
+
+ unsigned int nLwlOld;
+ unsigned int nRwrOld;
+ unsigned int nLwlNew;
+ unsigned int nRwrNew;
+
+ unsigned int nLwrOld;
+ unsigned int nRwlOld;
+ unsigned int nLwrNew;
+ unsigned int nRwlNew;
+
+ unsigned int nEcho0L;
+ unsigned int nEcho1L;
+ unsigned int nEcho2L;
+ unsigned int nEcho3L;
+
+ unsigned int nEcho0R;
+ unsigned int nEcho1R;
+ unsigned int nEcho2R;
+ unsigned int nEcho3R;
+
+ unsigned int nRevaOldL;
+ unsigned int nRevaOldR;
+ unsigned int nRevbOldL;
+ unsigned int nRevbOldR;
+
+ unsigned int nRevaNewL;
+ unsigned int nRevaNewR;
+ unsigned int nRevbNewL;
+ unsigned int nRevbNewR;
+
+ // Other variables
+
+ unsigned int aSample = p_frames;
+
+ // Set initial offsets
+
+ LM_SETSRCOFFSET ( nLwlOld );
+ LM_SETSRCOFFSET ( nRwrOld );
+ LM_SETSRCOFFSET ( nLwlNew );
+ LM_SETSRCOFFSET ( nRwrNew );
+ LM_SETSRCOFFSET ( nLwrOld );
+ LM_SETSRCOFFSET ( nRwlOld );
+ LM_SETSRCOFFSET ( nLwrNew );
+ LM_SETSRCOFFSET ( nRwlNew );
+ LM_SETSRCOFFSET ( nEcho0L );
+ LM_SETSRCOFFSET ( nEcho1L );
+ LM_SETSRCOFFSET ( nEcho2L );
+ LM_SETSRCOFFSET ( nEcho3L );
+ LM_SETSRCOFFSET ( nEcho0R );
+ LM_SETSRCOFFSET ( nEcho1R );
+ LM_SETSRCOFFSET ( nEcho2R );
+ LM_SETSRCOFFSET ( nEcho3R );
+ LM_SETSRCOFFSET ( nRevaOldL );
+ LM_SETSRCOFFSET ( nRevaOldR );
+ LM_SETSRCOFFSET ( nRevbOldL );
+ LM_SETSRCOFFSET ( nRevbOldR );
+ LM_SETSRCOFFSET ( nRevaNewL );
+ LM_SETSRCOFFSET ( nRevaNewR );
+ LM_SETSRCOFFSET ( nRevbNewL );
+ LM_SETSRCOFFSET ( nRevbNewR );
+
+ //SETMIN ( aSample, p_output.Size - p_output.Offset );
+
+ for (unsigned int cSample=0;cSample<aSample;cSample++) {
+
+ int tempL0, tempL1, tempR0, tempR1;
+
+ tempL1 = p_input[(cSample<<p_stereo_stride)]>>8;
+ tempR1 = p_input[(cSample<<p_stereo_stride) + 1]>>8;
+
+ tempL0 = LM_MUL ( tempL1, gInputL );
+ tempR0 = LM_MUL ( tempR1, gInputR );
+
+ /*
+ Left -> Wall -> Left Reflection
+ */
+ tempL1 = tempL0 + LM_MUL ( LM_REVERB( nLwlOld ), gWall );
+ tempR1 = tempR0 + LM_MUL ( LM_REVERB( nRwrOld ), gWall );
+ lwl += LM_MUL ( tempL1 - lwl, gLPF );
+ rwr += LM_MUL ( tempR1 - rwr, gLPF );
+ LM_REVERB( nLwlNew ) = lwl;
+ LM_REVERB( nRwrNew ) = rwr;
+ /*
+ Left -> Wall -> Right Reflection
+ */
+ tempL1 = tempL0 + LM_MUL ( LM_REVERB( nRwlOld ), gWall );
+ tempR1 = tempR0 + LM_MUL ( LM_REVERB( nLwrOld ), gWall );
+ lwr += LM_MUL ( tempL1 - lwr, gLPF );
+ rwl += LM_MUL ( tempR1 - rwl, gLPF );
+ LM_REVERB( nLwrNew ) = lwr;
+ LM_REVERB( nRwlNew ) = rwl;
+ /*
+ Early Echo(Early Reflection)
+ */
+ tempL0 =
+ LM_MUL ( LM_REVERB( nEcho0L ), gEcho0 ) +
+ LM_MUL ( LM_REVERB( nEcho1L ), gEcho1 ) +
+ LM_MUL ( LM_REVERB( nEcho2L ), gEcho2 ) +
+ LM_MUL ( LM_REVERB( nEcho3L ), gEcho3 );
+ tempR0 =
+ LM_MUL ( LM_REVERB( nEcho0R ), gEcho0 ) +
+ LM_MUL ( LM_REVERB( nEcho1R ), gEcho1 ) +
+ LM_MUL ( LM_REVERB( nEcho2R ), gEcho2 ) +
+ LM_MUL ( LM_REVERB( nEcho3R ), gEcho3 );
+ /*
+ Late Reverb
+ */
+ tempL1 = LM_REVERB( nRevaOldL );
+ tempR1 = LM_REVERB( nRevaOldR );
+ tempL0 -= LM_MUL ( tempL1, gReva );
+ tempR0 -= LM_MUL ( tempR1, gReva );
+ LM_REVERB( nRevaNewL ) = tempL0;
+ LM_REVERB( nRevaNewR ) = tempR0;
+ tempL0 = LM_MUL ( tempL0, gReva ) + tempL1;
+ tempR0 = LM_MUL ( tempR0, gReva ) + tempR1;
+ tempL1 = LM_REVERB( nRevbOldL );
+ tempR1 = LM_REVERB( nRevbOldR );
+ tempL0 -= LM_MUL ( tempL1, gRevb );
+ tempR0 -= LM_MUL ( tempR1, gRevb );
+ LM_REVERB( nRevbNewL ) = tempL0;
+ LM_REVERB( nRevbNewR ) = tempR0;
+ tempL0 = LM_MUL ( tempL0, gRevb ) + tempL1;
+ tempR0 = LM_MUL ( tempR0, gRevb ) + tempR1;
+ /*
+ Output
+ */
+
+ max|=abs(tempL0);
+ max|=abs(tempR0);
+
+ p_output[(cSample<<p_stereo_stride)] += tempL0<<8;
+ p_output[(cSample<<p_stereo_stride) + 1] += tempR0<<8;
+
+ }
+
+ // Advance offsets
+
+ Offset += aSample;
+ if ( Offset >= reverb_buffer_size ) { Offset -= reverb_buffer_size; }
+
+ p_input += aSample << p_stereo_stride;
+ p_output += aSample << p_stereo_stride;
+
+ p_frames -= aSample;
+ }
+
+ state.lwl = lwl;
+ state.lwr = lwr;
+ state.rwl = rwl;
+ state.rwr = rwr;
+ state.Offset = Offset;
+
+ return (max&0x7FFFFF00)!=0; // audio was mixed?
+}
+
+void ReverbSW::adjust_current_params() {
+
+ *current_params=*reverb_param_modes[mode];
+
+ uint32_t maxofs=0;
+
+#define LM_CONFIG_PARAM( x )\
+ current_params->x=(int)( ( (int64_t)current_params->x*(int64_t)mix_rate*8L)/(int64_t)44100);\
+ if (current_params->x>maxofs)\
+ maxofs=current_params->x;
+
+
+ LM_CONFIG_PARAM ( nLwlOld );
+ LM_CONFIG_PARAM ( nRwrOld );
+ LM_CONFIG_PARAM ( nLwlNew );
+ LM_CONFIG_PARAM ( nRwrNew );
+ LM_CONFIG_PARAM ( nLwrOld );
+ LM_CONFIG_PARAM ( nRwlOld );
+ LM_CONFIG_PARAM ( nLwrNew );
+ LM_CONFIG_PARAM ( nRwlNew );
+ LM_CONFIG_PARAM ( nEcho0L );
+ LM_CONFIG_PARAM ( nEcho1L );
+ LM_CONFIG_PARAM ( nEcho2L );
+ LM_CONFIG_PARAM ( nEcho3L );
+ LM_CONFIG_PARAM ( nEcho0R );
+ LM_CONFIG_PARAM ( nEcho1R );
+ LM_CONFIG_PARAM ( nEcho2R );
+ LM_CONFIG_PARAM ( nEcho3R );
+ LM_CONFIG_PARAM ( nRevaOldL );
+ LM_CONFIG_PARAM ( nRevaOldR );
+ LM_CONFIG_PARAM ( nRevbOldL );
+ LM_CONFIG_PARAM ( nRevbOldR );
+ LM_CONFIG_PARAM ( nRevaNewL );
+ LM_CONFIG_PARAM ( nRevaNewR );
+ LM_CONFIG_PARAM ( nRevbNewL );
+ LM_CONFIG_PARAM ( nRevbNewR );
+
+ int needed_buffer_size=maxofs+1;
+ if (reverb_buffer)
+ memdelete_arr(reverb_buffer);
+
+ reverb_buffer = memnew_arr(int,needed_buffer_size);
+ reverb_buffer_size=needed_buffer_size;
+
+ for (uint32_t i=0;i<reverb_buffer_size;i++)
+ reverb_buffer[i]=0;
+
+ state.reset();
+
+
+}
+
+void ReverbSW::set_mode(ReverbMode p_mode) {
+
+ if (mode==p_mode)
+ return;
+
+ mode=p_mode;
+
+ adjust_current_params();
+}
+
+void ReverbSW::set_mix_rate(int p_mix_rate) {
+
+ if (p_mix_rate==mix_rate)
+ return;
+
+ mix_rate=p_mix_rate;
+
+ adjust_current_params();
+
+}
+
+
+ReverbSW::ReverbSW() {
+
+ reverb_buffer=0;
+ reverb_buffer_size=0;
+ mode=REVERB_MODE_ROOM;
+ mix_rate=1;
+ current_params = memnew(ReverbParamsSW);
+}
+
+
+ReverbSW::~ReverbSW() {
+
+
+ if (reverb_buffer)
+ memdelete_arr(reverb_buffer);
+
+ memdelete(current_params);
+
+}
+
diff --git a/servers/audio/reverb_sw.h b/servers/audio/reverb_sw.h
new file mode 100644
index 0000000000..acf22f01b4
--- /dev/null
+++ b/servers/audio/reverb_sw.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* reverb_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef REVERB_SW_H
+#define REVERB_SW_H
+
+#include "typedefs.h"
+#include "os/memory.h"
+
+class ReverbParamsSW;
+
+class ReverbSW {
+public:
+ enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_STUDIO_SMALL,
+ REVERB_MODE_STUDIO_MEDIUM,
+ REVERB_MODE_STUDIO_LARGE,
+ REVERB_MODE_HALL,
+ REVERB_MODE_SPACE_ECHO,
+ REVERB_MODE_ECHO,
+ REVERB_MODE_DELAY,
+ REVERB_MODE_HALF_ECHO
+ };
+
+private:
+ struct State {
+ int lwl;
+ int lwr;
+ int rwl;
+ int rwr;
+ unsigned int Offset;
+ void reset() { lwl=0; lwr=0; rwl=0; rwr=0; Offset=0; }
+ State() { reset(); }
+ } state;
+
+ ReverbParamsSW *current_params;
+
+
+ int *reverb_buffer;
+ unsigned int reverb_buffer_size;
+ ReverbMode mode;
+ int mix_rate;
+
+ void adjust_current_params();
+
+public:
+
+
+ void set_mode(ReverbMode p_mode);
+ bool process(int *p_input,int *p_output,int p_frames,int p_stereo_stride=1); // return tru if audio was created
+ void set_mix_rate(int p_mix_rate);
+
+ ReverbSW();
+ ~ReverbSW();
+
+};
+
+#endif
diff --git a/servers/audio/sample_manager_sw.cpp b/servers/audio/sample_manager_sw.cpp
new file mode 100644
index 0000000000..2c065a9375
--- /dev/null
+++ b/servers/audio/sample_manager_sw.cpp
@@ -0,0 +1,280 @@
+/*************************************************************************/
+/* sample_manager_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "sample_manager_sw.h"
+
+#include "print_string.h"
+
+SampleManagerSW::~SampleManagerSW()
+{
+}
+
+
+
+RID SampleManagerMallocSW::sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length) {
+
+ ERR_EXPLAIN("IMA-ADPCM and STEREO are not a valid combination for sample format.");
+ ERR_FAIL_COND_V( p_format == AS::SAMPLE_FORMAT_IMA_ADPCM && p_stereo,RID());
+ Sample *s = memnew( Sample );
+ int datalen = p_length;
+ if (p_stereo)
+ datalen*=2;
+ if (p_format==AS::SAMPLE_FORMAT_PCM16)
+ datalen*=2;
+ else if (p_format==AS::SAMPLE_FORMAT_IMA_ADPCM)
+ datalen/=2;
+#define SAMPLE_EXTRA 16
+
+ s->data = memalloc(datalen+SAMPLE_EXTRA); //help the interpolator by allocating a little more..
+ for(int i=0;i<SAMPLE_EXTRA;i++) {
+
+ uint8_t *data = (uint8_t*)s->data;
+ data[datalen+i]=0;
+ }
+ if (!s->data) {
+
+ memdelete(s);
+ ERR_EXPLAIN("Cannot allocate sample of requested size.");
+ ERR_FAIL_V(RID());
+ }
+
+ s->format=p_format;
+ s->length=p_length;
+ s->length_bytes=datalen;
+ s->stereo=p_stereo;
+ s->loop_begin=0;
+ s->loop_end=0;
+ s->loop_format=AS::SAMPLE_LOOP_NONE;
+ s->mix_rate=44100;
+
+ AudioServer::get_singleton()->lock();
+ RID rid = sample_owner.make_rid(s);
+ AudioServer::get_singleton()->unlock();
+
+ return rid;
+}
+
+void SampleManagerMallocSW::sample_set_description(RID p_sample, const String& p_description) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ s->description=p_description;
+}
+
+String SampleManagerMallocSW::sample_get_description(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,String());
+
+ return s->description;
+}
+
+
+AS::SampleFormat SampleManagerMallocSW::sample_get_format(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,AS::SAMPLE_FORMAT_PCM8);
+
+ return s->format;
+}
+
+bool SampleManagerMallocSW::sample_is_stereo(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,false);
+
+ return s->stereo;
+
+}
+int SampleManagerMallocSW::sample_get_length(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->length;
+}
+
+void SampleManagerMallocSW::sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ int buff_size=p_buffer.size();
+ ERR_FAIL_COND(buff_size==0);
+
+ ERR_EXPLAIN("Sample buffer size does not match sample size.");
+ ERR_FAIL_COND(s->length_bytes!=buff_size);
+ DVector<uint8_t>::Read buffer_r=p_buffer.read();
+ const uint8_t *src = buffer_r.ptr();
+ uint8_t *dst = (uint8_t*)s->data;
+
+ for(int i=0;i<s->length_bytes;i++) {
+
+ dst[i]=src[i];
+ }
+
+}
+
+const DVector<uint8_t> SampleManagerMallocSW::sample_get_data(RID p_sample) const {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,DVector<uint8_t>());
+
+ DVector<uint8_t> ret_buffer;
+ ret_buffer.resize(s->length_bytes);
+ DVector<uint8_t>::Write buffer_w=ret_buffer.write();
+ uint8_t *dst = buffer_w.ptr();
+ const uint8_t *src = (const uint8_t*)s->data;
+
+ for(int i=0;i<s->length_bytes;i++) {
+
+ dst[i]=src[i];
+ }
+
+ buffer_w = DVector<uint8_t>::Write(); //unlock
+
+ return ret_buffer;
+}
+
+void *SampleManagerMallocSW::sample_get_data_ptr(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,NULL);
+
+ return s->data;
+
+}
+
+void SampleManagerMallocSW::sample_set_mix_rate(RID p_sample,int p_rate) {
+
+ ERR_FAIL_COND(p_rate<1);
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ s->mix_rate=p_rate;
+
+
+}
+int SampleManagerMallocSW::sample_get_mix_rate(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->mix_rate;
+
+}
+void SampleManagerMallocSW::sample_set_loop_format(RID p_sample,AS::SampleLoopFormat p_format) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ s->loop_format=p_format;
+
+}
+AS::SampleLoopFormat SampleManagerMallocSW::sample_get_loop_format(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,AS::SAMPLE_LOOP_NONE);
+
+ return s->loop_format;
+}
+
+void SampleManagerMallocSW::sample_set_loop_begin(RID p_sample,int p_pos) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+ ERR_FAIL_INDEX(p_pos,s->length);
+
+ s->loop_begin=p_pos;
+}
+int SampleManagerMallocSW::sample_get_loop_begin(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->loop_begin;
+}
+
+void SampleManagerMallocSW::sample_set_loop_end(RID p_sample,int p_pos) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+ if (p_pos>s->length)
+ p_pos=s->length;
+ s->loop_end=p_pos;
+
+}
+int SampleManagerMallocSW::sample_get_loop_end(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->loop_end;
+}
+
+bool SampleManagerMallocSW::is_sample(RID p_sample) const {
+
+ return sample_owner.owns(p_sample);
+
+}
+void SampleManagerMallocSW::free(RID p_sample) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+ AudioServer::get_singleton()->lock();
+ sample_owner.free(p_sample);
+ AudioServer::get_singleton()->unlock();
+
+ memfree(s->data);
+ memdelete(s);
+
+}
+
+SampleManagerMallocSW::SampleManagerMallocSW() {
+
+
+}
+
+SampleManagerMallocSW::~SampleManagerMallocSW() {
+
+ // check for sample leakage
+ List<RID> owned_list;
+ sample_owner.get_owned_list(&owned_list);
+
+ while(owned_list.size()) {
+
+ Sample *s = sample_owner.get(owned_list.front()->get());
+ String err="Leaked sample of size: "+itos(s->length_bytes)+" description: "+s->description;
+ ERR_PRINT(err.utf8().get_data());
+ free(owned_list.front()->get());
+ owned_list.pop_front();
+ }
+
+}
diff --git a/servers/audio/sample_manager_sw.h b/servers/audio/sample_manager_sw.h
new file mode 100644
index 0000000000..5de1b40389
--- /dev/null
+++ b/servers/audio/sample_manager_sw.h
@@ -0,0 +1,129 @@
+/*************************************************************************/
+/* sample_manager_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SAMPLE_MANAGER_SW_H
+#define SAMPLE_MANAGER_SW_H
+
+#include "servers/audio_server.h"
+
+class SampleManagerSW {
+public:
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length)=0;
+
+ virtual void sample_set_description(RID p_sample, const String& p_description)=0;
+ virtual String sample_get_description(RID p_sample) const=0;
+
+ virtual AS::SampleFormat sample_get_format(RID p_sample) const=0;
+ virtual bool sample_is_stereo(RID p_sample) const=0;
+ virtual int sample_get_length(RID p_sample) const=0;
+
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer)=0;
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const=0;
+
+ virtual void *sample_get_data_ptr(RID p_sample) const=0;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate)=0;
+ virtual int sample_get_mix_rate(RID p_sample) const=0;
+
+ virtual void sample_set_loop_format(RID p_sample,AS::SampleLoopFormat p_format)=0;
+ virtual AS::SampleLoopFormat sample_get_loop_format(RID p_sample) const=0;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_begin(RID p_sample) const=0;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_end(RID p_sample) const=0;
+
+ virtual bool is_sample(RID) const=0;
+ virtual void free(RID p_sample)=0;
+
+
+
+ virtual ~SampleManagerSW();
+};
+
+
+class SampleManagerMallocSW : public SampleManagerSW {
+
+
+ struct Sample {
+
+ void *data;
+ int length;
+ int length_bytes;
+ AS::SampleFormat format;
+ bool stereo;
+ AS::SampleLoopFormat loop_format;
+ int loop_begin;
+ int loop_end;
+ int mix_rate;
+ String description;
+ };
+
+ mutable RID_Owner<Sample> sample_owner;
+public:
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length);
+
+ virtual void sample_set_description(RID p_sample, const String& p_description);
+ virtual String sample_get_description(RID p_sample) const;
+
+ virtual AS::SampleFormat sample_get_format(RID p_sample) const;
+ virtual bool sample_is_stereo(RID p_sample) const;
+ virtual int sample_get_length(RID p_sample) const;
+
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer);
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const;
+
+ virtual void *sample_get_data_ptr(RID p_sample) const;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate);
+ virtual int sample_get_mix_rate(RID p_sample) const;
+
+ virtual void sample_set_loop_format(RID p_sample,AS::SampleLoopFormat p_format);
+ virtual AS::SampleLoopFormat sample_get_loop_format(RID p_sample) const;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos);
+ virtual int sample_get_loop_begin(RID p_sample) const;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos);
+ virtual int sample_get_loop_end(RID p_sample) const;
+
+ virtual bool is_sample(RID) const;
+ virtual void free(RID p_sample);
+
+ SampleManagerMallocSW();
+ virtual ~SampleManagerMallocSW();
+};
+
+#endif // SAMPLE_MANAGER_SW_H
diff --git a/servers/audio/voice_rb_sw.cpp b/servers/audio/voice_rb_sw.cpp
new file mode 100644
index 0000000000..93f176b755
--- /dev/null
+++ b/servers/audio/voice_rb_sw.cpp
@@ -0,0 +1,34 @@
+/*************************************************************************/
+/* voice_rb_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "voice_rb_sw.h"
+/*
+VoiceRBSW::VoiceRBSW()
+{
+}
+*/
diff --git a/servers/audio/voice_rb_sw.h b/servers/audio/voice_rb_sw.h
new file mode 100644
index 0000000000..7fcdebaa1a
--- /dev/null
+++ b/servers/audio/voice_rb_sw.h
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* voice_rb_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 VOICE_RB_SW_H
+#define VOICE_RB_SW_H
+
+#include "servers/audio_server.h"
+#include "os/os.h"
+class VoiceRBSW {
+public:
+
+ enum {
+ VOICE_RB_SIZE=1024
+ };
+
+ struct Command {
+
+ enum Type {
+ CMD_NONE,
+ CMD_PLAY,
+ CMD_STOP,
+ CMD_SET_VOLUME,
+ CMD_SET_PAN,
+ CMD_SET_FILTER,
+ CMD_SET_CHORUS,
+ CMD_SET_REVERB,
+ CMD_SET_MIX_RATE,
+ CMD_SET_POSITIONAL,
+ CMD_CHANGE_ALL_FX_VOLUMES
+ };
+
+ Type type;
+ RID voice;
+
+ struct {
+
+ RID sample;
+
+ } play;
+
+ union {
+
+ struct {
+
+ float volume;
+ } volume;
+
+ struct {
+
+ float pan,depth,height;
+ } pan;
+
+ struct {
+
+ AS::FilterType type;
+ float cutoff;
+ float resonance;
+ float gain;
+ } filter;
+
+ struct {
+ float send;
+ } chorus;
+ struct {
+ float send;
+ AS::ReverbRoomType room;
+ } reverb;
+
+ struct {
+
+ int mix_rate;
+ } mix_rate;
+
+ struct {
+
+ bool positional;
+ } positional;
+
+ };
+
+ Command() { type=CMD_NONE; }
+
+ };
+private:
+
+ Command voice_cmd_rb[VOICE_RB_SIZE];
+ volatile int read_pos;
+ volatile int write_pos;
+
+public:
+
+ _FORCE_INLINE_ bool commands_left() const { return read_pos!=write_pos; }
+ _FORCE_INLINE_ Command pop_command() {
+ ERR_FAIL_COND_V( read_pos==write_pos, Command() );
+ Command cmd=voice_cmd_rb[read_pos];
+ read_pos=(read_pos+1)%VOICE_RB_SIZE;
+ return cmd;
+ }
+ _FORCE_INLINE_ void push_command(const Command& p_command) {
+
+ bool full = ((write_pos+1)%VOICE_RB_SIZE)==read_pos;
+ if (full) {
+#ifdef DEBUG_ENABLED
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ ERR_EXPLAIN("Audio Ring Buffer Full (too many commands");
+ ERR_FAIL_COND( ((write_pos+1)%VOICE_RB_SIZE)==read_pos);
+ }
+#endif
+ return;
+ }
+
+ voice_cmd_rb[write_pos]=p_command;
+ write_pos=(write_pos+1)%VOICE_RB_SIZE;
+
+ }
+
+ VoiceRBSW() { read_pos=write_pos=0; }
+
+};
+
+#endif // VOICE_RB_SW_H
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
new file mode 100644
index 0000000000..81dab367c1
--- /dev/null
+++ b/servers/audio_server.cpp
@@ -0,0 +1,178 @@
+/*************************************************************************/
+/* audio_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_server.h"
+#include "globals.h"
+
+void AudioMixer::audio_mixer_chunk_call(int p_frames) {
+
+ AudioServer::get_singleton()->audio_mixer_chunk_callback(p_frames);
+}
+
+AudioMixer *AudioServer::EventStream::get_mixer() const {
+
+ return AudioServer::get_singleton()->get_mixer();
+}
+
+AudioServer *AudioServer::singleton=NULL;
+
+AudioServer *AudioServer::get_singleton() {
+
+ return singleton;
+}
+
+void AudioServer::sample_set_signed_data(RID p_sample, const DVector<float>& p_buffer) {
+
+ int len = p_buffer.size();
+ ERR_FAIL_COND( len == 0 );
+
+ DVector<uint8_t> data;
+ data.resize(len*2);
+ DVector<uint8_t>::Write w=data.write();
+
+ int16_t *samples = (int16_t*)w.ptr();
+
+ DVector<float>::Read r = p_buffer.read();
+
+ for(int i=0;i<len;i++) {
+
+ float sample = r[i];
+ sample = Math::floor( sample * (1<<16) );
+ if (sample<-32768)
+ sample=-32768;
+ else if (sample>32767)
+ sample=32767;
+ samples[i]=sample;
+ }
+
+ w = DVector<uint8_t>::Write();
+
+ sample_set_data(p_sample,data);
+
+
+}
+
+void AudioServer::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("sample_create","format","stereo","length"), &AudioServer::sample_create );
+ ObjectTypeDB::bind_method(_MD("sample_set_description","sample","description"), &AudioServer::sample_set_description );
+ ObjectTypeDB::bind_method(_MD("sample_get_description","sample"), &AudioServer::sample_get_description );
+
+ ObjectTypeDB::bind_method(_MD("sample_get_format","sample"), &AudioServer::sample_get_format );
+ ObjectTypeDB::bind_method(_MD("sample_is_stereo","sample"), &AudioServer::sample_is_stereo );
+ ObjectTypeDB::bind_method(_MD("sample_get_length","sample"), &AudioServer::sample_get_length );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_signed_data","sample","data"), &AudioServer::sample_set_signed_data );
+ ObjectTypeDB::bind_method(_MD("sample_set_data","sample"), &AudioServer::sample_set_data );
+ ObjectTypeDB::bind_method(_MD("sample_get_data","sample"), &AudioServer::sample_get_data );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_mix_rate","sample","mix_rate"), &AudioServer::sample_set_mix_rate );
+ ObjectTypeDB::bind_method(_MD("sample_get_mix_rate","sample"), &AudioServer::sample_get_mix_rate );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_loop_format","sample","loop_format"), &AudioServer::sample_set_loop_format );
+ ObjectTypeDB::bind_method(_MD("sample_get_loop_format","sample"), &AudioServer::sample_get_loop_format );
+
+
+ ObjectTypeDB::bind_method(_MD("sample_set_loop_begin","sample","pos"), &AudioServer::sample_set_loop_begin );
+ ObjectTypeDB::bind_method(_MD("sample_get_loop_begin","sample"), &AudioServer::sample_get_loop_begin );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_loop_end","sample","pos"), &AudioServer::sample_set_loop_end );
+ ObjectTypeDB::bind_method(_MD("sample_get_loop_end","sample"), &AudioServer::sample_get_loop_end );
+
+
+
+ ObjectTypeDB::bind_method(_MD("voice_create"), &AudioServer::voice_create );
+ ObjectTypeDB::bind_method(_MD("voice_play","voice","sample"), &AudioServer::voice_play );
+ ObjectTypeDB::bind_method(_MD("voice_set_volume","voice","volume"), &AudioServer::voice_set_volume );
+ ObjectTypeDB::bind_method(_MD("voice_set_pan","voice","pan","depth","height"), &AudioServer::voice_set_pan,DEFVAL(0),DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("voice_set_filter","voice","type","cutoff","resonance","gain"), &AudioServer::voice_set_filter,DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("voice_set_chorus","voice","chorus"), &AudioServer::voice_set_chorus );
+ ObjectTypeDB::bind_method(_MD("voice_set_reverb","voice","room","reverb"), &AudioServer::voice_set_reverb );
+ ObjectTypeDB::bind_method(_MD("voice_set_mix_rate","voice","rate"), &AudioServer::voice_set_mix_rate );
+ ObjectTypeDB::bind_method(_MD("voice_set_positional","voice","enabled"), &AudioServer::voice_set_positional );
+
+
+ ObjectTypeDB::bind_method(_MD("voice_get_volume","voice"), &AudioServer::voice_get_volume );
+ ObjectTypeDB::bind_method(_MD("voice_get_pan","voice"), &AudioServer::voice_get_pan );
+ ObjectTypeDB::bind_method(_MD("voice_get_pan_height","voice"), &AudioServer::voice_get_pan_height );
+ ObjectTypeDB::bind_method(_MD("voice_get_pan_depth","voice"), &AudioServer::voice_get_pan_depth );
+ ObjectTypeDB::bind_method(_MD("voice_get_filter_type","voice"), &AudioServer::voice_get_filter_type );
+ ObjectTypeDB::bind_method(_MD("voice_get_filter_cutoff","voice"), &AudioServer::voice_get_filter_cutoff );
+ ObjectTypeDB::bind_method(_MD("voice_get_filter_resonance","voice"), &AudioServer::voice_get_filter_resonance );
+ ObjectTypeDB::bind_method(_MD("voice_get_chorus","voice"), &AudioServer::voice_get_chorus );
+ ObjectTypeDB::bind_method(_MD("voice_get_reverb_type","voice"), &AudioServer::voice_get_reverb_type );
+ ObjectTypeDB::bind_method(_MD("voice_get_reverb","voice"), &AudioServer::voice_get_reverb );
+ ObjectTypeDB::bind_method(_MD("voice_get_mix_rate","voice"), &AudioServer::voice_get_mix_rate );
+ ObjectTypeDB::bind_method(_MD("voice_is_positional","voice"), &AudioServer::voice_is_positional );
+
+ ObjectTypeDB::bind_method(_MD("voice_stop","voice"), &AudioServer::voice_stop );
+
+ ObjectTypeDB::bind_method(_MD("free","rid"), &AudioServer::free );
+
+ ObjectTypeDB::bind_method(_MD("set_stream_global_volume_scale","scale"), &AudioServer::set_stream_global_volume_scale );
+ ObjectTypeDB::bind_method(_MD("get_stream_global_volume_scale"), &AudioServer::get_stream_global_volume_scale );
+
+ ObjectTypeDB::bind_method(_MD("set_fx_global_volume_scale","scale"), &AudioServer::set_fx_global_volume_scale );
+ ObjectTypeDB::bind_method(_MD("get_fx_global_volume_scale"), &AudioServer::get_fx_global_volume_scale );
+
+ ObjectTypeDB::bind_method(_MD("set_event_voice_global_volume_scale","scale"), &AudioServer::set_event_voice_global_volume_scale );
+ ObjectTypeDB::bind_method(_MD("get_event_voice_global_volume_scale"), &AudioServer::get_event_voice_global_volume_scale );
+
+ BIND_CONSTANT( SAMPLE_FORMAT_PCM8 );
+ BIND_CONSTANT( SAMPLE_FORMAT_PCM16 );
+ BIND_CONSTANT( SAMPLE_FORMAT_IMA_ADPCM );
+
+ BIND_CONSTANT( SAMPLE_LOOP_NONE );
+ BIND_CONSTANT( SAMPLE_LOOP_FORWARD );
+ BIND_CONSTANT( SAMPLE_LOOP_PING_PONG );
+
+ BIND_CONSTANT( FILTER_NONE );
+ BIND_CONSTANT( FILTER_LOWPASS );
+ BIND_CONSTANT( FILTER_BANDPASS );
+ BIND_CONSTANT( FILTER_HIPASS );
+ BIND_CONSTANT( FILTER_NOTCH );
+ BIND_CONSTANT( FILTER_BANDLIMIT ); ///< cutoff is LP resonace is HP
+
+ BIND_CONSTANT( REVERB_SMALL );
+ BIND_CONSTANT( REVERB_MEDIUM );
+ BIND_CONSTANT( REVERB_LARGE );
+ BIND_CONSTANT( REVERB_HALL );
+
+ GLOBAL_DEF("audio/stream_buffering_ms",500);
+
+}
+
+AudioServer::AudioServer() {
+
+ singleton=this;
+}
+
+AudioServer::~AudioServer() {
+
+
+}
diff --git a/servers/audio_server.h b/servers/audio_server.h
new file mode 100644
index 0000000000..85289de58a
--- /dev/null
+++ b/servers/audio_server.h
@@ -0,0 +1,289 @@
+/*************************************************************************/
+/* audio_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_SERVER_H
+#define AUDIO_SERVER_H
+
+#include "variant.h"
+#include "object.h"
+
+class AudioMixer {
+protected:
+
+ void audio_mixer_chunk_call(int p_frames);
+public:
+
+ enum {
+
+ INVALID_CHANNEL=0xFFFFFFFF
+ };
+
+ typedef uint32_t ChannelID;
+
+ /* CHANNEL API */
+
+ enum FilterType {
+ FILTER_NONE,
+ FILTER_LOWPASS,
+ FILTER_BANDPASS,
+ FILTER_HIPASS,
+ FILTER_NOTCH,
+ FILTER_PEAK,
+ FILTER_BANDLIMIT, ///< cutoff is LP resonace is HP
+ FILTER_LOW_SHELF,
+ FILTER_HIGH_SHELF
+
+ };
+
+ enum ReverbRoomType {
+
+ REVERB_SMALL,
+ REVERB_MEDIUM,
+ REVERB_LARGE,
+ REVERB_HALL
+ };
+
+ virtual ChannelID channel_alloc(RID p_sample)=0;
+
+ virtual void channel_set_volume(ChannelID p_channel, float p_gain)=0;
+ virtual void channel_set_pan(ChannelID p_channel, float p_pan, float p_depth=0,float height=0)=0; //pan and depth go from -1 to 1
+ virtual void channel_set_filter(ChannelID p_channel, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=1.0)=0;
+ virtual void channel_set_chorus(ChannelID p_channel, float p_chorus )=0;
+ virtual void channel_set_reverb(ChannelID p_channel, ReverbRoomType p_room_type, float p_reverb)=0;
+ virtual void channel_set_mix_rate(ChannelID p_channel, int p_mix_rate)=0;
+ virtual void channel_set_positional(ChannelID p_channel, bool p_positional)=0;
+
+ virtual float channel_get_volume(ChannelID p_channel) const=0;
+ virtual float channel_get_pan(ChannelID p_channel) const=0; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_depth(ChannelID p_channel) const=0; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_height(ChannelID p_channel) const=0; //pan and depth go from -1 to 1
+ virtual FilterType channel_get_filter_type(ChannelID p_channel) const=0;
+ virtual float channel_get_filter_cutoff(ChannelID p_channel) const=0;
+ virtual float channel_get_filter_resonance(ChannelID p_channel) const=0;
+ virtual float channel_get_filter_gain(ChannelID p_channel) const=0;
+ virtual float channel_get_chorus(ChannelID p_channel) const=0;
+ virtual ReverbRoomType channel_get_reverb_type(ChannelID p_channel) const=0;
+ virtual float channel_get_reverb(ChannelID p_channel) const=0;
+
+ virtual int channel_get_mix_rate(ChannelID p_channel) const=0;
+ virtual bool channel_is_positional(ChannelID p_channel) const=0;
+ virtual bool channel_is_valid(ChannelID p_channel) const=0;
+
+
+ virtual void channel_free(ChannelID p_channel)=0;
+
+ virtual void set_mixer_volume(float p_volume)=0;
+
+
+ virtual ~AudioMixer() {}
+};
+
+
+class AudioServer : public Object {
+
+ OBJ_TYPE( AudioServer, Object );
+
+ static AudioServer *singleton;
+protected:
+friend class AudioStream;
+friend class EventStream;
+friend class AudioMixer;
+
+ virtual AudioMixer *get_mixer()=0;
+ virtual void audio_mixer_chunk_callback(int p_frames)=0;
+
+ static void _bind_methods();
+public:
+
+
+ class EventStream {
+ protected:
+ AudioMixer *get_mixer() const;
+ public:
+ virtual void update(uint64_t p_usec)=0;
+
+ virtual ~EventStream() {}
+ };
+
+ class AudioStream {
+ public:
+ virtual int get_channel_count() const=0;
+ virtual void set_mix_rate(int p_rate)=0; //notify the stream of the mix rate
+ virtual bool mix(int32_t *p_buffer,int p_frames)=0;
+ virtual void update()=0;
+ virtual bool can_update_mt() const { return true; }
+ virtual ~AudioStream() {}
+ };
+
+
+ enum SampleFormat {
+
+ SAMPLE_FORMAT_PCM8,
+ SAMPLE_FORMAT_PCM16,
+ SAMPLE_FORMAT_IMA_ADPCM
+ };
+
+ enum SampleLoopFormat {
+ SAMPLE_LOOP_NONE,
+ SAMPLE_LOOP_FORWARD,
+ SAMPLE_LOOP_PING_PONG // not supported in every platform
+
+ };
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(SampleFormat p_format, bool p_stereo, int p_length)=0;
+
+ virtual void sample_set_description(RID p_sample, const String& p_description)=0;
+ virtual String sample_get_description(RID p_sample, const String& p_description) const=0;
+
+ virtual SampleFormat sample_get_format(RID p_sample) const=0;
+ virtual bool sample_is_stereo(RID p_sample) const=0;
+ virtual int sample_get_length(RID p_sample) const=0;
+ virtual const void* sample_get_data_ptr(RID p_sample) const=0;
+
+ virtual void sample_set_signed_data(RID p_sample, const DVector<float>& p_buffer);
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer)=0;
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const=0;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate)=0;
+ virtual int sample_get_mix_rate(RID p_sample) const=0;
+
+ virtual void sample_set_loop_format(RID p_sample,SampleLoopFormat p_format)=0;
+ virtual SampleLoopFormat sample_get_loop_format(RID p_sample) const=0;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_begin(RID p_sample) const=0;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_end(RID p_sample) const=0;
+
+
+ /* VOICE API */
+
+ enum FilterType {
+ FILTER_NONE,
+ FILTER_LOWPASS,
+ FILTER_BANDPASS,
+ FILTER_HIPASS,
+ FILTER_NOTCH,
+ FILTER_PEAK,
+ FILTER_BANDLIMIT, ///< cutoff is LP resonace is HP
+ FILTER_LOW_SHELF,
+ FILTER_HIGH_SHELF
+ };
+
+ enum ReverbRoomType {
+
+ REVERB_SMALL,
+ REVERB_MEDIUM,
+ REVERB_LARGE,
+ REVERB_HALL
+ };
+
+ virtual RID voice_create()=0;
+
+ virtual void voice_play(RID p_voice, RID p_sample)=0;
+
+ virtual void voice_set_volume(RID p_voice, float p_gain)=0;
+ virtual void voice_set_pan(RID p_voice, float p_pan, float p_depth=0,float height=0)=0; //pan and depth go from -1 to 1
+ virtual void voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=0)=0;
+ virtual void voice_set_chorus(RID p_voice, float p_chorus )=0;
+ virtual void voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb)=0;
+ virtual void voice_set_mix_rate(RID p_voice, int p_mix_rate)=0;
+ virtual void voice_set_positional(RID p_voice, bool p_positional)=0;
+
+ virtual float voice_get_volume(RID p_voice) const=0;
+ virtual float voice_get_pan(RID p_voice) const=0; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_depth(RID p_voice) const=0; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_height(RID p_voice) const=0; //pan and depth go from -1 to 1
+ virtual FilterType voice_get_filter_type(RID p_voice) const=0;
+ virtual float voice_get_filter_cutoff(RID p_voice) const=0;
+ virtual float voice_get_filter_resonance(RID p_voice) const=0;
+ virtual float voice_get_chorus(RID p_voice) const=0;
+ virtual ReverbRoomType voice_get_reverb_type(RID p_voice) const=0;
+ virtual float voice_get_reverb(RID p_voice) const=0;
+
+ virtual int voice_get_mix_rate(RID p_voice) const=0;
+ virtual bool voice_is_positional(RID p_voice) const=0;
+
+ virtual void voice_stop(RID p_voice)=0;
+ virtual bool voice_is_active(RID p_voice) const=0;
+
+ /* STREAM API */
+
+ virtual RID audio_stream_create(AudioStream *p_stream)=0;
+ virtual RID event_stream_create(EventStream *p_stream)=0;
+
+ virtual void stream_set_active(RID p_stream, bool p_active)=0;
+ virtual bool stream_is_active(RID p_stream) const=0;
+
+ virtual void stream_set_volume_scale(RID p_stream, float p_scale)=0;
+ virtual float stream_set_volume_scale(RID p_stream) const=0;
+
+ /* Audio Physics API */
+
+ virtual void free(RID p_id)=0;
+
+ virtual void init()=0;
+ virtual void finish()=0;
+ virtual void update()=0;
+
+ /* MISC config */
+
+ virtual void lock()=0;
+ virtual void unlock()=0;
+ virtual int get_default_channel_count() const=0;
+ virtual int get_default_mix_rate() const=0;
+
+ virtual void set_stream_global_volume_scale(float p_volume)=0;
+ virtual void set_fx_global_volume_scale(float p_volume)=0;
+ virtual void set_event_voice_global_volume_scale(float p_volume)=0;
+
+ virtual float get_stream_global_volume_scale() const=0;
+ virtual float get_fx_global_volume_scale() const=0;
+ virtual float get_event_voice_global_volume_scale() const=0;
+
+ virtual uint32_t read_output_peak() const=0;
+
+ static AudioServer *get_singleton();
+
+ virtual double get_mix_time() const=0; //useful for video -> audio sync
+
+ AudioServer();
+ virtual ~AudioServer();
+};
+
+VARIANT_ENUM_CAST( AudioServer::SampleFormat );
+VARIANT_ENUM_CAST( AudioServer::SampleLoopFormat );
+VARIANT_ENUM_CAST( AudioServer::FilterType );
+VARIANT_ENUM_CAST( AudioServer::ReverbRoomType );
+
+typedef AudioServer AS;
+
+#endif // AUDIO_SERVER_H
diff --git a/servers/physics/SCsub b/servers/physics/SCsub
new file mode 100644
index 0000000000..16fe3a59ac
--- /dev/null
+++ b/servers/physics/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/servers/physics/area_pair_sw.cpp b/servers/physics/area_pair_sw.cpp
new file mode 100644
index 0000000000..4a303d3fdd
--- /dev/null
+++ b/servers/physics/area_pair_sw.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* area_pair_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "area_pair_sw.h"
+#include "collision_solver_sw.h"
+
+
+bool AreaPairSW::setup(float p_step) {
+
+ bool result = CollisionSolverSW::solve_static(body->get_shape(body_shape),body->get_transform() * body->get_shape_transform(body_shape),area->get_shape(area_shape),area->get_transform() * area->get_shape_transform(area_shape),NULL,this);
+
+ if (result!=colliding) {
+
+ if (result) {
+
+ if (area->get_space_override_mode()!=PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED)
+ body->add_area(area);
+ if (area->has_monitor_callback())
+ area->add_body_to_query(body,body_shape,area_shape);
+
+ } else {
+
+ if (area->get_space_override_mode()!=PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED)
+ body->remove_area(area);
+ if (area->has_monitor_callback())
+ area->remove_body_from_query(body,body_shape,area_shape);
+
+ }
+
+ colliding=result;
+
+ }
+
+ return false; //never do any post solving
+}
+
+void AreaPairSW::solve(float p_step) {
+
+
+}
+
+
+AreaPairSW::AreaPairSW(BodySW *p_body,int p_body_shape, AreaSW *p_area,int p_area_shape) {
+
+
+ body=p_body;
+ area=p_area;
+ body_shape=p_body_shape;
+ area_shape=p_area_shape;
+ colliding=false;
+ body->add_constraint(this,0);
+ area->add_constraint(this);
+
+}
+
+AreaPairSW::~AreaPairSW() {
+
+ if (colliding) {
+
+ if (area->get_space_override_mode()!=PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED)
+ body->remove_area(area);
+ if (area->has_monitor_callback())
+ area->remove_body_from_query(body,body_shape,area_shape);
+
+
+ }
+ body->remove_constraint(this);
+ area->remove_constraint(this);
+}
diff --git a/servers/physics/area_pair_sw.h b/servers/physics/area_pair_sw.h
new file mode 100644
index 0000000000..e5e2b5cf5e
--- /dev/null
+++ b/servers/physics/area_pair_sw.h
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* area_pair_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 AREA_PAIR_SW_H
+#define AREA_PAIR_SW_H
+
+#include "constraint_sw.h"
+#include "body_sw.h"
+#include "area_sw.h"
+
+class AreaPairSW : public ConstraintSW {
+
+ BodySW *body;
+ AreaSW *area;
+ int body_shape;
+ int area_shape;
+ bool colliding;
+public:
+
+ bool setup(float p_step);
+ void solve(float p_step);
+
+ AreaPairSW(BodySW *p_body,int p_body_shape, AreaSW *p_area,int p_area_shape);
+ ~AreaPairSW();
+};
+
+#endif // AREA_PAIR__SW_H
+
diff --git a/servers/physics/area_sw.cpp b/servers/physics/area_sw.cpp
new file mode 100644
index 0000000000..33962d993a
--- /dev/null
+++ b/servers/physics/area_sw.cpp
@@ -0,0 +1,192 @@
+/*************************************************************************/
+/* area_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "area_sw.h"
+#include "space_sw.h"
+#include "body_sw.h"
+
+AreaSW::BodyKey::BodyKey(BodySW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) { rid=p_body->get_self(); instance_id=p_body->get_instance_id(); body_shape=p_body_shape; area_shape=p_area_shape; }
+
+void AreaSW::_shapes_changed() {
+
+
+}
+
+void AreaSW::set_transform(const Transform& p_transform) {
+
+ if (!moved_list.in_list() && get_space())
+ get_space()->area_add_to_moved_list(&moved_list);
+
+ _set_transform(p_transform);
+}
+
+void AreaSW::set_space(SpaceSW *p_space) {
+
+ if (get_space()) {
+ if (monitor_query_list.in_list())
+ get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
+ if (moved_list.in_list())
+ get_space()->area_remove_from_moved_list(&moved_list);
+
+ }
+
+ monitored_bodies.clear();
+
+ _set_space(p_space);
+}
+
+
+void AreaSW::set_monitor_callback(ObjectID p_id, const StringName& p_method) {
+
+
+ if (p_id==monitor_callback_id) {
+ monitor_callback_method=p_method;
+ return;
+ }
+
+ _unregister_shapes();
+
+ monitor_callback_id=p_id;
+ monitor_callback_method=p_method;
+
+ monitored_bodies.clear();
+
+ _shape_changed();
+
+}
+
+
+
+void AreaSW::set_space_override_mode(PhysicsServer::AreaSpaceOverrideMode p_mode) {
+ bool do_override=p_mode!=PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED;
+ if (do_override==(space_override_mode!=PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED))
+ return;
+ _unregister_shapes();
+ space_override_mode=p_mode;
+ _shape_changed();
+}
+
+void AreaSW::set_param(PhysicsServer::AreaParameter p_param, const Variant& p_value) {
+
+ switch(p_param) {
+ case PhysicsServer::AREA_PARAM_GRAVITY: gravity=p_value; ; break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_VECTOR: gravity_vector=p_value; ; break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT: gravity_is_point=p_value; ; break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION: point_attenuation=p_value; ; break;
+ case PhysicsServer::AREA_PARAM_DENSITY: density=p_value; ; break;
+ case PhysicsServer::AREA_PARAM_PRIORITY: priority=p_value; ; break;
+ }
+
+
+}
+
+Variant AreaSW::get_param(PhysicsServer::AreaParameter p_param) const {
+
+
+ switch(p_param) {
+ case PhysicsServer::AREA_PARAM_GRAVITY: return gravity;
+ case PhysicsServer::AREA_PARAM_GRAVITY_VECTOR: return gravity_vector;
+ case PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT: return gravity_is_point;
+ case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION: return point_attenuation;
+ case PhysicsServer::AREA_PARAM_DENSITY: return density;
+ case PhysicsServer::AREA_PARAM_PRIORITY: return priority;
+ }
+
+ return Variant();
+}
+
+
+void AreaSW::_queue_monitor_update() {
+
+ ERR_FAIL_COND(!get_space());
+
+ if (!monitor_query_list.in_list())
+ get_space()->area_add_to_monitor_query_list(&monitor_query_list);
+
+
+}
+
+void AreaSW::call_queries() {
+
+ if (monitor_callback_id && !monitored_bodies.empty()) {
+
+ Variant res[5];
+ Variant *resptr[5];
+ for(int i=0;i<5;i++)
+ resptr[i]=&res[i];
+
+ Object *obj = ObjectDB::get_instance(monitor_callback_id);
+ if (!obj) {
+ monitored_bodies.clear();
+ monitor_callback_id=0;
+ return;
+ }
+
+
+
+ for (Map<BodyKey,BodyState>::Element *E=monitored_bodies.front();E;E=E->next()) {
+
+ if (E->get().state==0)
+ continue; //nothing happened
+
+ res[0]=E->get().state>0 ? PhysicsServer::AREA_BODY_ADDED : PhysicsServer::AREA_BODY_REMOVED;
+ res[1]=E->key().rid;
+ res[2]=E->key().instance_id;
+ res[3]=E->key().body_shape;
+ res[4]=E->key().area_shape;
+
+ Variant::CallError ce;
+ obj->call(monitor_callback_method,(const Variant**)resptr,5,ce);
+ }
+ }
+
+ monitored_bodies.clear();
+
+ //get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
+
+}
+
+AreaSW::AreaSW() : CollisionObjectSW(TYPE_AREA), monitor_query_list(this), moved_list(this) {
+
+ _set_static(true); //areas are never active
+ space_override_mode=PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED;
+ gravity=9.80665;
+ gravity_vector=Vector3(0,-1,0);
+ gravity_is_point=false;
+ point_attenuation=1;
+ density=0.1;
+ priority=0;
+
+
+}
+
+AreaSW::~AreaSW() {
+
+
+}
+
diff --git a/servers/physics/area_sw.h b/servers/physics/area_sw.h
new file mode 100644
index 0000000000..3e39dc3bb6
--- /dev/null
+++ b/servers/physics/area_sw.h
@@ -0,0 +1,172 @@
+/*************************************************************************/
+/* area_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 AREA_SW_H
+#define AREA_SW_H
+
+#include "servers/physics_server.h"
+#include "collision_object_sw.h"
+#include "self_list.h"
+//#include "servers/physics/query_sw.h"
+
+class SpaceSW;
+class BodySW;
+class ConstraintSW;
+
+class AreaSW : public CollisionObjectSW{
+
+
+ PhysicsServer::AreaSpaceOverrideMode space_override_mode;
+ float gravity;
+ Vector3 gravity_vector;
+ bool gravity_is_point;
+ float point_attenuation;
+ float density;
+ int priority;
+
+ ObjectID monitor_callback_id;
+ StringName monitor_callback_method;
+
+ SelfList<AreaSW> monitor_query_list;
+ SelfList<AreaSW> moved_list;
+
+ struct BodyKey {
+
+ RID rid;
+ ObjectID instance_id;
+ uint32_t body_shape;
+ uint32_t area_shape;
+
+ _FORCE_INLINE_ bool operator<( const BodyKey& p_key) const {
+
+ if (rid==p_key.rid) {
+
+ if (body_shape==p_key.body_shape) {
+
+ return area_shape < p_key.area_shape;
+ } else
+ return body_shape < p_key.area_shape;
+ } else
+ return rid < p_key.rid;
+
+ }
+
+ _FORCE_INLINE_ BodyKey() {}
+ BodyKey(BodySW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+ };
+
+ struct BodyState {
+
+ int state;
+ _FORCE_INLINE_ void inc() { state++; }
+ _FORCE_INLINE_ void dec() { state--; }
+ _FORCE_INLINE_ BodyState() { state=0; }
+ };
+
+ Map<BodyKey,BodyState> monitored_bodies;
+
+ //virtual void shape_changed_notify(ShapeSW *p_shape);
+ //virtual void shape_deleted_notify(ShapeSW *p_shape);
+
+ Set<ConstraintSW*> constraints;
+
+
+ virtual void _shapes_changed();
+ void _queue_monitor_update();
+
+public:
+
+ //_FORCE_INLINE_ const Transform& get_inverse_transform() const { return inverse_transform; }
+ //_FORCE_INLINE_ SpaceSW* get_owner() { return owner; }
+
+ void set_monitor_callback(ObjectID p_id, const StringName& p_method);
+ _FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback_id; }
+
+ _FORCE_INLINE_ void add_body_to_query(BodySW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+ _FORCE_INLINE_ void remove_body_from_query(BodySW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+
+ void set_param(PhysicsServer::AreaParameter p_param, const Variant& p_value);
+ Variant get_param(PhysicsServer::AreaParameter p_param) const;
+
+ void set_space_override_mode(PhysicsServer::AreaSpaceOverrideMode p_mode);
+ PhysicsServer::AreaSpaceOverrideMode get_space_override_mode() const { return space_override_mode; }
+
+ _FORCE_INLINE_ void set_gravity(float p_gravity) { gravity=p_gravity; }
+ _FORCE_INLINE_ float get_gravity() const { return gravity; }
+
+ _FORCE_INLINE_ void set_gravity_vector(const Vector3& p_gravity) { gravity_vector=p_gravity; }
+ _FORCE_INLINE_ Vector3 get_gravity_vector() const { return gravity_vector; }
+
+ _FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point=p_enable; }
+ _FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; }
+
+ _FORCE_INLINE_ void set_point_attenuation(float p_point_attenuation) { point_attenuation=p_point_attenuation; }
+ _FORCE_INLINE_ float get_point_attenuation() const { return point_attenuation; }
+
+ _FORCE_INLINE_ void set_density(float p_density) { density=p_density; }
+ _FORCE_INLINE_ float get_density() const { return density; }
+
+ _FORCE_INLINE_ void set_priority(int p_priority) { priority=p_priority; }
+ _FORCE_INLINE_ int get_priority() const { return priority; }
+
+ _FORCE_INLINE_ void add_constraint( ConstraintSW* p_constraint) { constraints.insert(p_constraint); }
+ _FORCE_INLINE_ void remove_constraint( ConstraintSW* p_constraint) { constraints.erase(p_constraint); }
+ _FORCE_INLINE_ const Set<ConstraintSW*>& get_constraints() const { return constraints; }
+
+ void set_transform(const Transform& p_transform);
+
+ void set_space(SpaceSW *p_space);
+
+
+ void call_queries();
+
+ AreaSW();
+ ~AreaSW();
+};
+
+void AreaSW::add_body_to_query(BodySW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) {
+
+ BodyKey bk(p_body,p_body_shape,p_area_shape);
+ monitored_bodies[bk].inc();
+ if (!monitor_query_list.in_list())
+ _queue_monitor_update();
+}
+void AreaSW::remove_body_from_query(BodySW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) {
+
+ BodyKey bk(p_body,p_body_shape,p_area_shape);
+ monitored_bodies[bk].dec();
+ if (!monitor_query_list.in_list())
+ _queue_monitor_update();
+}
+
+
+
+
+
+
+#endif // AREA__SW_H
diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp
new file mode 100644
index 0000000000..06ae34098c
--- /dev/null
+++ b/servers/physics/body_pair_sw.cpp
@@ -0,0 +1,442 @@
+/*************************************************************************/
+/* body_pair_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "body_pair_sw.h"
+#include "collision_solver_sw.h"
+#include "space_sw.h"
+
+
+/*
+#define NO_ACCUMULATE_IMPULSES
+#define NO_SPLIT_IMPULSES
+
+#define NO_FRICTION
+*/
+
+#define NO_TANGENTIALS
+/* BODY PAIR */
+
+
+//#define ALLOWED_PENETRATION 0.01
+#define RELAXATION_TIMESTEPS 3
+#define MIN_VELOCITY 0.0001
+
+void BodyPairSW::_contact_added_callback(const Vector3& p_point_A,const Vector3& p_point_B,void *p_userdata) {
+
+ BodyPairSW* pair = (BodyPairSW*)p_userdata;
+ pair->contact_added_callback(p_point_A,p_point_B);
+
+}
+
+void BodyPairSW::contact_added_callback(const Vector3& p_point_A,const Vector3& p_point_B) {
+
+ // check if we already have the contact
+
+ //Vector3 local_A = A->get_inv_transform().xform(p_point_A);
+ //Vector3 local_B = B->get_inv_transform().xform(p_point_B);
+
+ Vector3 local_A = A->get_inv_transform().basis.xform(p_point_A);
+ Vector3 local_B = B->get_inv_transform().basis.xform(p_point_B-offset_B);
+
+
+
+ int new_index = contact_count;
+
+ ERR_FAIL_COND( new_index >= (MAX_CONTACTS+1) );
+
+ Contact contact;
+
+ contact.acc_normal_impulse=0;
+ contact.acc_bias_impulse=0;
+ contact.acc_tangent_impulse=Vector3();
+ contact.local_A=local_A;
+ contact.local_B=local_B;
+ contact.normal=(p_point_A-p_point_B).normalized();
+
+
+
+ // attempt to determine if the contact will be reused
+ real_t contact_recycle_radius=space->get_contact_recycle_radius();
+
+ for (int i=0;i<contact_count;i++) {
+
+ Contact& c = contacts[i];
+ if (
+ c.local_A.distance_squared_to( local_A ) < (contact_recycle_radius*contact_recycle_radius) &&
+ c.local_B.distance_squared_to( local_B ) < (contact_recycle_radius*contact_recycle_radius) ) {
+
+ contact.acc_normal_impulse=c.acc_normal_impulse;
+ contact.acc_bias_impulse=c.acc_bias_impulse;
+ contact.acc_tangent_impulse=c.acc_tangent_impulse;
+ new_index=i;
+ break;
+ }
+ }
+
+ // figure out if the contact amount must be reduced to fit the new contact
+
+ if (new_index == MAX_CONTACTS) {
+
+ // remove the contact with the minimum depth
+
+ int least_deep=-1;
+ float min_depth=1e10;
+
+ for (int i=0;i<=contact_count;i++) {
+
+ Contact& c = (i==contact_count)?contact:contacts[i];
+ Vector3 global_A = A->get_transform().basis.xform(c.local_A);
+ Vector3 global_B = B->get_transform().basis.xform(c.local_B)+offset_B;
+
+ Vector3 axis = global_A - global_B;
+ float depth = axis.dot( c.normal );
+
+ if (depth<min_depth) {
+
+ min_depth=depth;
+ least_deep=i;
+ }
+ }
+
+ ERR_FAIL_COND(least_deep==-1);
+
+ if (least_deep < contact_count) { //replace the last deep contact by the new one
+
+ contacts[least_deep]=contact;
+ }
+
+ return;
+ }
+
+ contacts[new_index]=contact;
+
+ if (new_index==contact_count) {
+
+ contact_count++;
+ }
+
+}
+
+void BodyPairSW::validate_contacts() {
+
+ //make sure to erase contacts that are no longer valid
+
+ real_t contact_max_separation=space->get_contact_max_separation();
+ for (int i=0;i<contact_count;i++) {
+
+ Contact& c = contacts[i];
+
+ Vector3 global_A = A->get_transform().basis.xform(c.local_A);
+ Vector3 global_B = B->get_transform().basis.xform(c.local_B)+offset_B;
+ Vector3 axis = global_A - global_B;
+ float depth = axis.dot( c.normal );
+
+ if (depth < -contact_max_separation || (global_B + c.normal * depth - global_A).length() > contact_max_separation) {
+ // contact no longer needed, remove
+
+
+ if ((i+1) < contact_count) {
+ // swap with the last one
+ SWAP( contacts[i], contacts[ contact_count-1 ] );
+
+ }
+
+ i--;
+ contact_count--;
+ }
+ }
+}
+
+bool BodyPairSW::setup(float p_step) {
+
+
+ offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
+
+ validate_contacts();
+
+ Vector3 offset_A = A->get_transform().get_origin();
+ Transform xform_Au = Transform(A->get_transform().basis,Vector3());
+ Transform xform_A = xform_Au * A->get_shape_transform(shape_A);
+
+ Transform xform_Bu = B->get_transform();
+ xform_Bu.origin-=offset_A;
+ Transform xform_B = xform_Bu * B->get_shape_transform(shape_B);
+
+ ShapeSW *shape_A_ptr=A->get_shape(shape_A);
+ ShapeSW *shape_B_ptr=B->get_shape(shape_B);
+
+ bool collided = CollisionSolverSW::solve_static(shape_A_ptr,xform_A,shape_B_ptr,xform_B,_contact_added_callback,this,&sep_axis);
+ this->collided=collided;
+
+ if (!collided)
+ return false;
+
+
+ //cannot collide
+ if (A->has_exception(B->get_self()) || B->has_exception(A->get_self()) || (A->get_mode()<=PhysicsServer::BODY_MODE_STATIC_ACTIVE && B->get_mode()<=PhysicsServer::BODY_MODE_STATIC_ACTIVE)) {
+ return false;
+ }
+
+ real_t max_penetration = space->get_contact_max_allowed_penetration();
+
+ float bias = 0.3f;
+
+ if (shape_A_ptr->get_custom_bias() || shape_B_ptr->get_custom_bias()) {
+
+ if (shape_A_ptr->get_custom_bias()==0)
+ bias=shape_B_ptr->get_custom_bias();
+ else if (shape_B_ptr->get_custom_bias()==0)
+ bias=shape_A_ptr->get_custom_bias();
+ else
+ bias=(shape_B_ptr->get_custom_bias()+shape_A_ptr->get_custom_bias())*0.5;
+ }
+
+
+ real_t inv_dt = 1.0/p_step;
+
+ for(int i=0;i<contact_count;i++) {
+
+ Contact &c = contacts[i];
+ c.active=false;
+
+ Vector3 global_A = xform_Au.xform(c.local_A);
+ Vector3 global_B = xform_Bu.xform(c.local_B);
+
+ real_t depth = c.normal.dot(global_A - global_B);
+
+ if (depth<=0) {
+ c.active=false;
+ continue;
+ }
+
+ c.active=true;
+
+ int gather_A = A->can_report_contacts();
+ int gather_B = B->can_report_contacts();
+
+ c.rA = global_A;
+ c.rB = global_B-offset_B;
+
+ // contact query reporting...
+#if 0
+ if (A->get_body_type() == PhysicsServer::BODY_CHARACTER)
+ static_cast<CharacterBodySW*>(A)->report_character_contact( global_A, global_B, B );
+ if (B->get_body_type() == PhysicsServer::BODY_CHARACTER)
+ static_cast<CharacterBodySW*>(B)->report_character_contact( global_B, global_A, A );
+ if (A->has_contact_query())
+ A->report_contact( global_A, global_B, B );
+ if (B->has_contact_query())
+ B->report_contact( global_B, global_A, A );
+#endif
+
+ if (A->can_report_contacts()) {
+ Vector3 crB = A->get_angular_velocity().cross( c.rA ) + A->get_linear_velocity();
+ A->add_contact(global_A,-c.normal,depth,shape_A,global_B,shape_B,B->get_instance_id(),B->get_self(),crB);
+ }
+
+ if (B->can_report_contacts()) {
+ Vector3 crA = A->get_angular_velocity().cross( c.rB ) + A->get_linear_velocity();
+ B->add_contact(global_B,c.normal,depth,shape_B,global_A,shape_A,A->get_instance_id(),A->get_self(),crA);
+ }
+
+ c.active=true;
+
+ // Precompute normal mass, tangent mass, and bias.
+ Vector3 inertia_A = A->get_inv_inertia_tensor().xform( c.rA.cross( c.normal ) );
+ Vector3 inertia_B = B->get_inv_inertia_tensor().xform( c.rB.cross( c.normal ) );
+ real_t kNormal = A->get_inv_mass() + B->get_inv_mass();
+ kNormal += c.normal.dot( inertia_A.cross(c.rA ) ) + c.normal.dot( inertia_B.cross( c.rB ));
+ c.mass_normal = 1.0f / kNormal;
+
+#if 1
+ c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration);
+
+#else
+ if (depth > max_penetration) {
+ c.bias = (depth - max_penetration) * (1.0/(p_step*(1.0/RELAXATION_TIMESTEPS)));
+ } else {
+ float approach = -0.1f * (depth - max_penetration) / (CMP_EPSILON + max_penetration);
+ approach = CLAMP( approach, CMP_EPSILON, 1.0 );
+ c.bias = approach * (depth - max_penetration) * (1.0/p_step);
+ }
+#endif
+ c.depth=depth;
+
+ Vector3 j_vec = c.normal * c.acc_normal_impulse + c.acc_tangent_impulse;
+ A->apply_impulse( c.rA, -j_vec );
+ B->apply_impulse( c.rB, j_vec );
+ c.acc_bias_impulse=0;
+ Vector3 jb_vec = c.normal * c.acc_bias_impulse;
+ A->apply_bias_impulse( c.rA, -jb_vec );
+ B->apply_bias_impulse( c.rB, jb_vec );
+
+ }
+
+ return true;
+}
+
+void BodyPairSW::solve(float p_step) {
+
+ if (!collided)
+ return;
+
+
+ for(int i=0;i<contact_count;i++) {
+
+ Contact &c = contacts[i];
+ if (!c.active)
+ continue;
+
+ c.active=false; //try to deactivate, will activate itself if still needed
+
+ //bias impule
+
+ Vector3 crbA = A->get_biased_angular_velocity().cross( c.rA );
+ Vector3 crbB = B->get_biased_angular_velocity().cross( c.rB );
+ Vector3 dbv = B->get_biased_linear_velocity() + crbB - A->get_biased_linear_velocity() - crbA;
+
+ real_t vbn = dbv.dot(c.normal);
+
+ if (Math::abs(-vbn+c.bias)>MIN_VELOCITY) {
+
+ real_t jbn = (-vbn + c.bias)*c.mass_normal;
+ real_t jbnOld = c.acc_bias_impulse;
+ c.acc_bias_impulse = MAX(jbnOld + jbn, 0.0f);
+
+ Vector3 jb = c.normal * (c.acc_bias_impulse - jbnOld);
+
+
+ A->apply_bias_impulse(c.rA,-jb);
+ B->apply_bias_impulse(c.rB, jb);
+
+ c.active=true;
+ }
+
+
+ Vector3 crA = A->get_angular_velocity().cross( c.rA );
+ Vector3 crB = B->get_angular_velocity().cross( c.rB );
+ Vector3 dv = B->get_linear_velocity() + crB - A->get_linear_velocity() - crA;
+
+ //normal impule
+ real_t vn = dv.dot(c.normal);
+
+ if (Math::abs(vn)>MIN_VELOCITY) {
+
+ real_t bounce=0;
+ real_t jn = (-bounce -vn)*c.mass_normal;
+ real_t jnOld = c.acc_normal_impulse;
+ c.acc_normal_impulse = MAX(jnOld + jn, 0.0f);
+
+
+ Vector3 j =c.normal * (c.acc_normal_impulse - jnOld);
+
+
+ A->apply_impulse(c.rA,-j);
+ B->apply_impulse(c.rB, j);
+
+ c.active=true;
+ }
+
+ //friction impule
+
+ real_t friction = A->get_friction() * B->get_friction();
+
+ Vector3 lvA = A->get_linear_velocity() + A->get_angular_velocity().cross( c.rA );
+ Vector3 lvB = B->get_linear_velocity() + B->get_angular_velocity().cross( c.rB );
+
+ Vector3 dtv = lvB - lvA;
+ real_t tn = c.normal.dot(dtv);
+
+ // tangential velocity
+ Vector3 tv = dtv - c.normal * tn;
+ real_t tvl = tv.length();
+
+ if (tvl > MIN_VELOCITY) {
+
+ tv /= tvl;
+
+ Vector3 temp1 = A->get_inv_inertia_tensor().xform( c.rA.cross( tv ) );
+ Vector3 temp2 = B->get_inv_inertia_tensor().xform( c.rB.cross( tv ) );
+
+ real_t t = -tvl /
+ (A->get_inv_mass() + B->get_inv_mass() + tv.dot(temp1.cross(c.rA) + temp2.cross(c.rB)));
+
+ Vector3 jt = t * tv;
+
+
+ Vector3 jtOld = c.acc_tangent_impulse;
+ c.acc_tangent_impulse += jt;
+
+ real_t fi_len = c.acc_tangent_impulse.length();
+ real_t jtMax = c.acc_normal_impulse * friction;
+
+ if (fi_len > CMP_EPSILON && fi_len > jtMax) {
+
+ c.acc_tangent_impulse*=jtMax / fi_len;
+ }
+
+ jt = c.acc_tangent_impulse - jtOld;
+
+
+ A->apply_impulse( c.rA, -jt );
+ B->apply_impulse( c.rB, jt );
+
+ c.active=true;
+
+ }
+
+
+ }
+
+}
+
+
+
+
+
+BodyPairSW::BodyPairSW(BodySW *p_A, int p_shape_A,BodySW *p_B, int p_shape_B) : ConstraintSW(_arr,2) {
+
+ A=p_A;
+ B=p_B;
+ shape_A=p_shape_A;
+ shape_B=p_shape_B;
+ space=A->get_space();
+ A->add_constraint(this,0);
+ B->add_constraint(this,1);
+ contact_count=0;
+ collided=false;
+
+}
+
+
+BodyPairSW::~BodyPairSW() {
+
+ A->remove_constraint(this);
+ B->remove_constraint(this);
+
+}
diff --git a/servers/physics/body_pair_sw.h b/servers/physics/body_pair_sw.h
new file mode 100644
index 0000000000..ad66227b36
--- /dev/null
+++ b/servers/physics/body_pair_sw.h
@@ -0,0 +1,97 @@
+/*************************************************************************/
+/* body_pair_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef BODY_PAIR_SW_H
+#define BODY_PAIR_SW_H
+
+#include "body_sw.h"
+#include "constraint_sw.h"
+
+class BodyPairSW : public ConstraintSW {
+ enum {
+
+ MAX_CONTACTS=4
+ };
+
+ union {
+ struct {
+ BodySW *A;
+ BodySW *B;
+ };
+
+ BodySW *_arr[2];
+ };
+
+ int shape_A;
+ int shape_B;
+
+
+ struct Contact {
+
+ Vector3 position;
+ Vector3 normal;
+ Vector3 local_A, local_B;
+ real_t acc_normal_impulse; // accumulated normal impulse (Pn)
+ Vector3 acc_tangent_impulse; // accumulated tangent impulse (Pt)
+ real_t acc_bias_impulse; // accumulated normal impulse for position bias (Pnb)
+ real_t mass_normal;
+ real_t bias;
+
+ real_t depth;
+ bool active;
+ Vector3 rA,rB;
+ };
+
+ Vector3 offset_B; //use local A coordinates to avoid numerical issues on collision detection
+
+ Vector3 sep_axis;
+ Contact contacts[MAX_CONTACTS];
+ int contact_count;
+ bool collided;
+ int cc;
+
+
+ static void _contact_added_callback(const Vector3& p_point_A,const Vector3& p_point_B,void *p_userdata);
+
+ void contact_added_callback(const Vector3& p_point_A,const Vector3& p_point_B);
+
+ void validate_contacts();
+
+ SpaceSW *space;
+
+public:
+
+ bool setup(float p_step);
+ void solve(float p_step);
+
+ BodyPairSW(BodySW *p_A, int p_shape_A,BodySW *p_B, int p_shape_B);
+ ~BodyPairSW();
+
+};
+
+#endif // BODY_PAIR__SW_H
diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp
new file mode 100644
index 0000000000..b926a93773
--- /dev/null
+++ b/servers/physics/body_sw.cpp
@@ -0,0 +1,631 @@
+/*************************************************************************/
+/* body_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "body_sw.h"
+#include "space_sw.h"
+#include "area_sw.h"
+
+void BodySW::_update_inertia() {
+
+ if (get_space() && !inertia_update_list.in_list())
+ get_space()->body_add_to_inertia_update_list(&inertia_update_list);
+
+}
+
+
+void BodySW::_update_inertia_tensor() {
+
+ Matrix3 tb = get_transform().basis;
+ tb.scale(_inv_inertia);
+ _inv_inertia_tensor = tb * get_transform().basis.transposed();
+
+}
+
+void BodySW::update_inertias() {
+
+ //update shapes and motions
+
+ switch(mode) {
+
+ case PhysicsServer::BODY_MODE_RIGID: {
+
+ //update tensor for allshapes, not the best way but should be somehow OK. (inspired from bullet)
+ float total_area=0;
+
+ for (int i=0;i<get_shape_count();i++) {
+
+ total_area+=get_shape_aabb(i).get_area();
+ }
+
+ Vector3 _inertia;
+
+
+ for (int i=0;i<get_shape_count();i++) {
+
+ const ShapeSW* shape=get_shape(i);
+
+ float area=get_shape_aabb(i).get_area();
+
+ float mass = area * this->mass / total_area;
+
+ _inertia += shape->get_moment_of_inertia(mass) + mass * get_shape_transform(i).get_origin();
+
+ }
+
+ if (_inertia!=Vector3())
+ _inv_inertia=_inertia.inverse();
+ else
+ _inv_inertia=Vector3();
+
+ if (mass)
+ _inv_mass=1.0/mass;
+ else
+ _inv_mass=0;
+
+ } break;
+
+ case PhysicsServer::BODY_MODE_STATIC_ACTIVE:
+ case PhysicsServer::BODY_MODE_STATIC: {
+
+ _inv_inertia=Vector3();
+ _inv_mass=0;
+ } break;
+ case PhysicsServer::BODY_MODE_CHARACTER: {
+
+ _inv_inertia=Vector3();
+ _inv_mass=1.0/mass;
+
+ } break;
+ }
+ _update_inertia_tensor();
+
+ //_update_shapes();
+
+}
+
+
+
+void BodySW::set_active(bool p_active) {
+
+ if (active==p_active)
+ return;
+
+ active=p_active;
+ if (!p_active) {
+ if (get_space())
+ get_space()->body_remove_from_active_list(&active_list);
+ } else {
+ if (mode==PhysicsServer::BODY_MODE_STATIC)
+ return; //static bodies can't become active
+ if (get_space())
+ get_space()->body_add_to_active_list(&active_list);
+
+ //still_time=0;
+ }
+/*
+ if (!space)
+ return;
+
+ for(int i=0;i<get_shape_count();i++) {
+ Shape &s=shapes[i];
+ if (s.bpid>0) {
+ get_space()->get_broadphase()->set_active(s.bpid,active);
+ }
+ }
+*/
+}
+
+
+
+void BodySW::set_param(PhysicsServer::BodyParameter p_param, float p_value) {
+
+ switch(p_param) {
+ case PhysicsServer::BODY_PARAM_BOUNCE: {
+
+ bounce=p_value;
+ } break;
+ case PhysicsServer::BODY_PARAM_FRICTION: {
+
+ friction=p_value;
+ } break;
+ case PhysicsServer::BODY_PARAM_MASS: {
+ ERR_FAIL_COND(p_value<=0);
+ mass=p_value;
+ _update_inertia();
+
+ } break;
+ default:{}
+ }
+}
+
+float BodySW::get_param(PhysicsServer::BodyParameter p_param) const {
+
+ switch(p_param) {
+ case PhysicsServer::BODY_PARAM_BOUNCE: {
+
+ return bounce;
+ } break;
+ case PhysicsServer::BODY_PARAM_FRICTION: {
+
+ return friction;
+ } break;
+ case PhysicsServer::BODY_PARAM_MASS: {
+ return mass;
+ } break;
+ default:{}
+ }
+
+ return 0;
+}
+
+void BodySW::set_mode(PhysicsServer::BodyMode p_mode) {
+
+ mode=p_mode;
+
+ switch(p_mode) {
+ //CLEAR UP EVERYTHING IN CASE IT NOT WORKS!
+ case PhysicsServer::BODY_MODE_STATIC:
+ case PhysicsServer::BODY_MODE_STATIC_ACTIVE: {
+
+ _set_inv_transform(get_transform().affine_inverse());
+ _inv_mass=0;
+ _set_static(p_mode==PhysicsServer::BODY_MODE_STATIC);
+ set_active(p_mode==PhysicsServer::BODY_MODE_STATIC_ACTIVE);
+ linear_velocity=Vector3();
+ angular_velocity=Vector3();
+ } break;
+ case PhysicsServer::BODY_MODE_RIGID: {
+
+ _inv_mass=mass>0?(1.0/mass):0;
+ _set_static(false);
+ simulated_motion=false; //jic
+
+ } break;
+ case PhysicsServer::BODY_MODE_CHARACTER: {
+
+ _inv_mass=mass>0?(1.0/mass):0;
+ _set_static(false);
+ simulated_motion=false; //jic
+ } break;
+ }
+
+ _update_inertia();
+ //if (get_space())
+// _update_queries();
+
+}
+PhysicsServer::BodyMode BodySW::get_mode() const {
+
+ return mode;
+}
+
+void BodySW::_shapes_changed() {
+
+ _update_inertia();
+}
+
+void BodySW::set_state(PhysicsServer::BodyState p_state, const Variant& p_variant) {
+
+ switch(p_state) {
+ case PhysicsServer::BODY_STATE_TRANSFORM: {
+
+
+ if (mode==PhysicsServer::BODY_MODE_STATIC || mode==PhysicsServer::BODY_MODE_STATIC_ACTIVE) {
+ _set_transform(p_variant);
+ _set_inv_transform(get_transform().affine_inverse());
+ wakeup_neighbours();
+ } else {
+ Transform t = p_variant;
+ t.orthonormalize();
+ _set_transform(t);
+ _set_inv_transform(get_transform().inverse());
+
+ }
+
+ } break;
+ case PhysicsServer::BODY_STATE_LINEAR_VELOCITY: {
+
+ //if (mode==PhysicsServer::BODY_MODE_STATIC)
+ // break;
+ linear_velocity=p_variant;
+ } break;
+ case PhysicsServer::BODY_STATE_ANGULAR_VELOCITY: {
+ //if (mode!=PhysicsServer::BODY_MODE_RIGID)
+ // break;
+ angular_velocity=p_variant;
+
+ } break;
+ case PhysicsServer::BODY_STATE_SLEEPING: {
+ //?
+ if (mode==PhysicsServer::BODY_MODE_STATIC || mode==PhysicsServer::BODY_MODE_STATIC_ACTIVE)
+ break;
+ bool do_sleep=p_variant;
+ if (do_sleep) {
+ linear_velocity=Vector3();
+ //biased_linear_velocity=Vector3();
+ angular_velocity=Vector3();
+ //biased_angular_velocity=Vector3();
+ set_active(false);
+ } else {
+ if (mode!=PhysicsServer::BODY_MODE_STATIC)
+ set_active(true);
+ }
+ } break;
+ case PhysicsServer::BODY_STATE_CAN_SLEEP: {
+ can_sleep=p_variant;
+ if (mode==PhysicsServer::BODY_MODE_RIGID && !active && !can_sleep)
+ set_active(true);
+
+ } break;
+ }
+
+}
+Variant BodySW::get_state(PhysicsServer::BodyState p_state) const {
+
+ switch(p_state) {
+ case PhysicsServer::BODY_STATE_TRANSFORM: {
+ return get_transform();
+ } break;
+ case PhysicsServer::BODY_STATE_LINEAR_VELOCITY: {
+ return linear_velocity;
+ } break;
+ case PhysicsServer::BODY_STATE_ANGULAR_VELOCITY: {
+ return angular_velocity;
+ } break;
+ case PhysicsServer::BODY_STATE_SLEEPING: {
+ return !is_active();
+ } break;
+ case PhysicsServer::BODY_STATE_CAN_SLEEP: {
+ return can_sleep;
+ } break;
+ }
+
+ return Variant();
+}
+
+
+void BodySW::set_space(SpaceSW *p_space){
+
+ if (get_space()) {
+
+ if (inertia_update_list.in_list())
+ get_space()->body_remove_from_inertia_update_list(&inertia_update_list);
+ if (active_list.in_list())
+ get_space()->body_remove_from_active_list(&active_list);
+ if (direct_state_query_list.in_list())
+ get_space()->body_remove_from_state_query_list(&direct_state_query_list);
+
+ }
+
+ _set_space(p_space);
+
+ if (get_space()) {
+
+ _update_inertia();
+ if (active)
+ get_space()->body_add_to_active_list(&active_list);
+// _update_queries();
+ //if (is_active()) {
+ // active=false;
+ // set_active(true);
+ //}
+
+ }
+
+}
+
+void BodySW::_compute_area_gravity(const AreaSW *p_area) {
+
+ if (p_area->is_gravity_point()) {
+
+ gravity = (p_area->get_gravity_vector() - get_transform().get_origin()).normalized() * p_area->get_gravity();
+
+ } else {
+ gravity = p_area->get_gravity_vector() * p_area->get_gravity();
+ }
+}
+
+void BodySW::integrate_forces(real_t p_step) {
+
+
+ if (mode==PhysicsServer::BODY_MODE_STATIC || mode==PhysicsServer::BODY_MODE_STATIC_ACTIVE)
+ return;
+
+ AreaSW *current_area = get_space()->get_default_area();
+ ERR_FAIL_COND(!current_area);
+
+ int prio = current_area->get_priority();
+ int ac = areas.size();
+ if (ac) {
+ const AreaCMP *aa = &areas[0];
+ for(int i=0;i<ac;i++) {
+ if (aa[i].area->get_priority() > prio) {
+ current_area=aa[i].area;
+ prio=current_area->get_priority();
+ }
+ }
+ }
+
+ _compute_area_gravity(current_area);
+ density=current_area->get_density();
+
+ if (!omit_force_integration) {
+ //overriden by direct state query
+
+ Vector3 force=gravity*mass;
+ force+=applied_force;
+ Vector3 torque=applied_torque;
+
+ real_t damp = 1.0 - p_step * density;
+
+ if (damp<0) // reached zero in the given time
+ damp=0;
+
+ real_t angular_damp = 1.0 - p_step * density * get_space()->get_body_angular_velocity_damp_ratio();
+
+ if (angular_damp<0) // reached zero in the given time
+ angular_damp=0;
+
+ linear_velocity*=damp;
+ angular_velocity*=angular_damp;
+
+ linear_velocity+=_inv_mass * force * p_step;
+ angular_velocity+=_inv_inertia_tensor.xform(torque)*p_step;
+ }
+
+ applied_force=Vector3();
+ applied_torque=Vector3();
+
+ //motion=linear_velocity*p_step;
+
+ biased_angular_velocity=Vector3();
+ biased_linear_velocity=Vector3();
+
+ if (continuous_cd) //shapes temporarily extend for raycast
+ _update_shapes_with_motion(linear_velocity*p_step);
+
+ current_area=NULL; // clear the area, so it is set in the next frame
+ contact_count=0;
+
+}
+
+void BodySW::integrate_velocities(real_t p_step) {
+
+ if (mode==PhysicsServer::BODY_MODE_STATIC)
+ return;
+
+ if (mode==PhysicsServer::BODY_MODE_STATIC_ACTIVE) {
+ if (fi_callback)
+ get_space()->body_add_to_state_query_list(&direct_state_query_list);
+ return;
+ }
+
+ Vector3 total_angular_velocity = angular_velocity+biased_angular_velocity;
+
+
+
+ float ang_vel = total_angular_velocity.length();
+ Transform transform = get_transform();
+
+
+ if (ang_vel!=0.0) {
+ Vector3 ang_vel_axis = total_angular_velocity / ang_vel;
+ Matrix3 rot( ang_vel_axis, -ang_vel*p_step );
+ transform.basis = rot * transform.basis;
+ transform.orthonormalize();
+ }
+
+ Vector3 total_linear_velocity=linear_velocity+biased_linear_velocity;
+
+
+ transform.origin+=total_linear_velocity * p_step;
+
+ _set_transform(transform);
+ _set_inv_transform(get_transform().inverse());
+
+ _update_inertia_tensor();
+
+ if (fi_callback) {
+
+ get_space()->body_add_to_state_query_list(&direct_state_query_list);
+ }
+
+}
+
+
+void BodySW::simulate_motion(const Transform& p_xform,real_t p_step) {
+
+ Transform inv_xform = p_xform.affine_inverse();
+ if (!get_space()) {
+ _set_transform(p_xform);
+ _set_inv_transform(inv_xform);
+
+ return;
+ }
+
+ //compute a FAKE linear velocity - this is easy
+
+ linear_velocity=(p_xform.origin - get_transform().origin)/p_step;
+
+ //compute a FAKE angular velocity, not so easy
+ Matrix3 rot=get_transform().basis.orthonormalized().transposed() * p_xform.basis.orthonormalized();
+ Vector3 axis;
+ float angle;
+
+ rot.get_axis_and_angle(axis,angle);
+ axis.normalize();
+ angular_velocity=axis.normalized() * (angle/p_step);
+ linear_velocity = (p_xform.origin - get_transform().origin)/p_step;
+
+ if (!direct_state_query_list.in_list())// - callalways, so lv and av are cleared && (state_query || direct_state_query))
+ get_space()->body_add_to_state_query_list(&direct_state_query_list);
+ simulated_motion=true;
+ _set_transform(p_xform);
+
+
+}
+
+void BodySW::wakeup_neighbours() {
+
+ for(Map<ConstraintSW*,int>::Element *E=constraint_map.front();E;E=E->next()) {
+
+ const ConstraintSW *c=E->key();
+ BodySW **n = c->get_body_ptr();
+ int bc=c->get_body_count();
+
+ for(int i=0;i<bc;i++) {
+
+ if (i==E->get())
+ continue;
+ BodySW *b = n[i];
+ if (b->mode!=PhysicsServer::BODY_MODE_RIGID)
+ continue;
+
+ if (!b->is_active())
+ b->set_active(true);
+ }
+ }
+}
+
+void BodySW::call_queries() {
+
+
+ if (fi_callback) {
+
+ PhysicsDirectBodyStateSW *dbs = PhysicsDirectBodyStateSW::singleton;
+ dbs->body=this;
+
+ Variant v=dbs;
+
+ Object *obj = ObjectDB::get_instance(fi_callback->id);
+ if (!obj) {
+
+ set_force_integration_callback(0,StringName());
+ } else {
+ const Variant *vp[2]={&v,&fi_callback->udata};
+
+ Variant::CallError ce;
+ int argc=(fi_callback->udata.get_type()==Variant::NIL)?1:2;
+ obj->call(fi_callback->method,vp,argc,ce);
+ }
+
+
+ }
+
+ if (simulated_motion) {
+
+ // linear_velocity=Vector3();
+ // angular_velocity=0;
+ simulated_motion=false;
+ }
+}
+
+
+bool BodySW::sleep_test(real_t p_step) {
+
+ if (mode==PhysicsServer::BODY_MODE_STATIC || mode==PhysicsServer::BODY_MODE_STATIC_ACTIVE)
+ return true; //
+ else if (mode==PhysicsServer::BODY_MODE_CHARACTER)
+ return !active; // characters don't sleep unless asked to sleep
+ else if (!can_sleep)
+ return false;
+
+
+
+
+ if (Math::abs(angular_velocity.length())<get_space()->get_body_angular_velocity_sleep_treshold() && Math::abs(linear_velocity.length_squared()) < get_space()->get_body_linear_velocity_sleep_treshold()*get_space()->get_body_linear_velocity_sleep_treshold()) {
+
+ still_time+=p_step;
+
+ return still_time > get_space()->get_body_time_to_sleep();
+ } else {
+
+ still_time=0; //maybe this should be set to 0 on set_active?
+ return false;
+ }
+}
+
+
+void BodySW::set_force_integration_callback(ObjectID p_id,const StringName& p_method,const Variant& p_udata) {
+
+ if (fi_callback) {
+
+ memdelete(fi_callback);
+ fi_callback=NULL;
+ }
+
+
+ if (p_id!=0) {
+
+ fi_callback=memnew(ForceIntegrationCallback);
+ fi_callback->id=p_id;
+ fi_callback->method=p_method;
+ fi_callback->udata=p_udata;
+ }
+
+}
+
+BodySW::BodySW() : CollisionObjectSW(TYPE_BODY), active_list(this), inertia_update_list(this), direct_state_query_list(this) {
+
+
+ mode=PhysicsServer::BODY_MODE_RIGID;
+ active=true;
+
+ mass=1;
+// _inv_inertia=Transform();
+ _inv_mass=1;
+ bounce=0;
+ friction=1;
+ omit_force_integration=false;
+// applied_torque=0;
+ island_step=0;
+ island_next=NULL;
+ island_list_next=NULL;
+ _set_static(false);
+ density=0;
+ contact_count=0;
+ simulated_motion=false;
+ still_time=0;
+ continuous_cd=false;
+ can_sleep=false;
+ fi_callback=NULL;
+
+}
+
+BodySW::~BodySW() {
+
+ if (fi_callback)
+ memdelete(fi_callback);
+}
+
+PhysicsDirectBodyStateSW *PhysicsDirectBodyStateSW::singleton=NULL;
+
+PhysicsDirectSpaceState* PhysicsDirectBodyStateSW::get_space_state() {
+
+ return body->get_space()->get_direct_state();
+}
diff --git a/servers/physics/body_sw.h b/servers/physics/body_sw.h
new file mode 100644
index 0000000000..9f0bbc00cf
--- /dev/null
+++ b/servers/physics/body_sw.h
@@ -0,0 +1,348 @@
+/*************************************************************************/
+/* body_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef BODY_SW_H
+#define BODY_SW_H
+
+#include "collision_object_sw.h"
+#include "vset.h"
+#include "area_sw.h"
+
+class ConstraintSW;
+
+
+class BodySW : public CollisionObjectSW {
+
+
+ PhysicsServer::BodyMode mode;
+
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
+
+ Vector3 biased_linear_velocity;
+ Vector3 biased_angular_velocity;
+ real_t mass;
+ real_t bounce;
+ real_t friction;
+
+ real_t _inv_mass;
+ Vector3 _inv_inertia;
+ Matrix3 _inv_inertia_tensor;
+
+ Vector3 gravity;
+ real_t density;
+
+ real_t still_time;
+
+ Vector3 applied_force;
+ Vector3 applied_torque;
+
+ SelfList<BodySW> active_list;
+ SelfList<BodySW> inertia_update_list;
+ SelfList<BodySW> direct_state_query_list;
+
+ VSet<RID> exceptions;
+ bool omit_force_integration;
+ bool active;
+ bool simulated_motion;
+ bool continuous_cd;
+ bool can_sleep;
+ void _update_inertia();
+ virtual void _shapes_changed();
+
+ Map<ConstraintSW*,int> constraint_map;
+
+ struct AreaCMP {
+
+ AreaSW *area;
+ _FORCE_INLINE_ bool operator<(const AreaCMP& p_cmp) const { return area->get_self() < p_cmp.area->get_self() ; }
+ _FORCE_INLINE_ AreaCMP() {}
+ _FORCE_INLINE_ AreaCMP(AreaSW *p_area) { area=p_area;}
+ };
+
+
+ VSet<AreaCMP> areas;
+
+ struct Contact {
+
+
+ Vector3 local_pos;
+ Vector3 local_normal;
+ float depth;
+ int local_shape;
+ Vector3 collider_pos;
+ int collider_shape;
+ ObjectID collider_instance_id;
+ RID collider;
+ Vector3 collider_velocity_at_pos;
+ };
+
+ Vector<Contact> contacts; //no contacts by default
+ int contact_count;
+
+ struct ForceIntegrationCallback {
+
+ ObjectID id;
+ StringName method;
+ Variant udata;
+ };
+
+ ForceIntegrationCallback *fi_callback;
+
+
+ uint64_t island_step;
+ BodySW *island_next;
+ BodySW *island_list_next;
+
+ _FORCE_INLINE_ void _compute_area_gravity(const AreaSW *p_area);
+
+ _FORCE_INLINE_ void _update_inertia_tensor();
+
+friend class PhysicsDirectBodyStateSW; // i give up, too many functions to expose
+
+public:
+
+
+ void set_force_integration_callback(ObjectID p_id,const StringName& p_method,const Variant& p_udata=Variant());
+
+
+ _FORCE_INLINE_ void add_area(AreaSW *p_area) { areas.insert(AreaCMP(p_area)); }
+ _FORCE_INLINE_ void remove_area(AreaSW *p_area) { areas.erase(AreaCMP(p_area)); }
+
+ _FORCE_INLINE_ void set_max_contacts_reported(int p_size) { contacts.resize(p_size); contact_count=0; }
+ _FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); }
+
+ _FORCE_INLINE_ bool can_report_contacts() const { return !contacts.empty(); }
+ _FORCE_INLINE_ void add_contact(const Vector3& p_local_pos,const Vector3& p_local_normal, float p_depth, int p_local_shape, const Vector3& p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID& p_collider,const Vector3& p_collider_velocity_at_pos);
+
+
+ _FORCE_INLINE_ void add_exception(const RID& p_exception) { exceptions.insert(p_exception);}
+ _FORCE_INLINE_ void remove_exception(const RID& p_exception) { exceptions.erase(p_exception);}
+ _FORCE_INLINE_ bool has_exception(const RID& p_exception) const { return exceptions.has(p_exception);}
+ _FORCE_INLINE_ const VSet<RID>& get_exceptions() const { return exceptions;}
+
+ _FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
+ _FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step=p_step; }
+
+ _FORCE_INLINE_ BodySW* get_island_next() const { return island_next; }
+ _FORCE_INLINE_ void set_island_next(BodySW* p_next) { island_next=p_next; }
+
+ _FORCE_INLINE_ BodySW* get_island_list_next() const { return island_list_next; }
+ _FORCE_INLINE_ void set_island_list_next(BodySW* p_next) { island_list_next=p_next; }
+
+ _FORCE_INLINE_ void add_constraint(ConstraintSW* p_constraint, int p_pos) { constraint_map[p_constraint]=p_pos; }
+ _FORCE_INLINE_ void remove_constraint(ConstraintSW* p_constraint) { constraint_map.erase(p_constraint); }
+ const Map<ConstraintSW*,int>& get_constraint_map() const { return constraint_map; }
+
+ _FORCE_INLINE_ void set_omit_force_integration(bool p_omit_force_integration) { omit_force_integration=p_omit_force_integration; }
+ _FORCE_INLINE_ bool get_omit_force_integration() const { return omit_force_integration; }
+
+ _FORCE_INLINE_ void set_linear_velocity(const Vector3& p_velocity) {linear_velocity=p_velocity; }
+ _FORCE_INLINE_ Vector3 get_linear_velocity() const { return linear_velocity; }
+
+ _FORCE_INLINE_ void set_angular_velocity(const Vector3& p_velocity) { angular_velocity=p_velocity; }
+ _FORCE_INLINE_ Vector3 get_angular_velocity() const { return angular_velocity; }
+
+ _FORCE_INLINE_ const Vector3& get_biased_linear_velocity() const { return biased_linear_velocity; }
+ _FORCE_INLINE_ const Vector3& get_biased_angular_velocity() const { return biased_angular_velocity; }
+
+ _FORCE_INLINE_ void apply_impulse(const Vector3& p_pos, const Vector3& p_j) {
+
+ linear_velocity += p_j * _inv_mass;
+ angular_velocity += _inv_inertia_tensor.xform( p_pos.cross(p_j) );
+ }
+
+ _FORCE_INLINE_ void apply_bias_impulse(const Vector3& p_pos, const Vector3& p_j) {
+
+ biased_linear_velocity += p_j * _inv_mass;
+ biased_angular_velocity += _inv_inertia_tensor.xform( p_pos.cross(p_j) );
+ }
+
+ _FORCE_INLINE_ void apply_torque_impulse(const Vector3& p_j) {
+
+ angular_velocity += _inv_inertia_tensor.xform(p_j);
+ }
+
+ _FORCE_INLINE_ void add_force(const Vector3& p_force, const Vector3& p_pos) {
+
+ applied_force += p_force;
+ applied_torque += p_pos.cross(p_force);
+ }
+
+ void set_active(bool p_active);
+ _FORCE_INLINE_ bool is_active() const { return active; }
+
+ void set_param(PhysicsServer::BodyParameter p_param, float);
+ float get_param(PhysicsServer::BodyParameter p_param) const;
+
+ void set_mode(PhysicsServer::BodyMode p_mode);
+ PhysicsServer::BodyMode get_mode() const;
+
+ void set_state(PhysicsServer::BodyState p_state, const Variant& p_variant);
+ Variant get_state(PhysicsServer::BodyState p_state) const;
+
+ void set_applied_force(const Vector3& p_force) { applied_force=p_force; }
+ Vector3 get_applied_force() const { return applied_force; }
+
+ void set_applied_torque(const Vector3& p_torque) { applied_torque=p_torque; }
+ Vector3 get_applied_torque() const { return applied_torque; }
+
+ _FORCE_INLINE_ void set_continuous_collision_detection(bool p_enable) { continuous_cd=p_enable; }
+ _FORCE_INLINE_ bool is_continuous_collision_detection_enabled() const { return continuous_cd; }
+
+ void set_space(SpaceSW *p_space);
+
+ void update_inertias();
+
+ _FORCE_INLINE_ real_t get_inv_mass() const { return _inv_mass; }
+ _FORCE_INLINE_ Vector3 get_inv_inertia() const { return _inv_inertia; }
+ _FORCE_INLINE_ Matrix3 get_inv_inertia_tensor() const { return _inv_inertia_tensor; }
+ _FORCE_INLINE_ real_t get_friction() const { return friction; }
+ _FORCE_INLINE_ Vector3 get_gravity() const { return gravity; }
+ _FORCE_INLINE_ real_t get_density() const { return density; }
+ _FORCE_INLINE_ real_t get_bounce() const { return bounce; }
+
+ void integrate_forces(real_t p_step);
+ void integrate_velocities(real_t p_step);
+
+ void simulate_motion(const Transform& p_xform,real_t p_step);
+ void call_queries();
+ void wakeup_neighbours();
+
+ bool sleep_test(real_t p_step);
+
+ BodySW();
+ ~BodySW();
+
+};
+
+
+//add contact inline
+
+void BodySW::add_contact(const Vector3& p_local_pos,const Vector3& p_local_normal, float p_depth, int p_local_shape, const Vector3& p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID& p_collider,const Vector3& p_collider_velocity_at_pos) {
+
+ int c_max=contacts.size();
+
+ if (c_max==0)
+ return;
+
+ Contact *c = &contacts[0];
+
+
+ int idx=-1;
+
+ if (contact_count<c_max) {
+ idx=contact_count++;
+ } else {
+
+ float least_depth=1e20;
+ int least_deep=-1;
+ for(int i=0;i<c_max;i++) {
+
+ if (i==0 || c[i].depth<least_depth) {
+ least_deep=i;
+ least_depth=c[i].depth;
+ }
+ }
+
+ if (least_deep>=0 && least_depth<p_depth) {
+
+ idx=least_deep;
+ }
+ if (idx==-1)
+ return; //none least deepe than this
+ }
+
+ c[idx].local_pos=p_local_pos;
+ c[idx].local_normal=p_local_normal;
+ c[idx].depth=p_depth;
+ c[idx].local_shape=p_local_shape;
+ c[idx].collider_pos=p_collider_pos;
+ c[idx].collider_shape=p_collider_shape;
+ c[idx].collider_instance_id=p_collider_instance_id;
+ c[idx].collider=p_collider;
+ c[idx].collider_velocity_at_pos=p_collider_velocity_at_pos;
+
+}
+
+
+class PhysicsDirectBodyStateSW : public PhysicsDirectBodyState {
+
+ OBJ_TYPE( PhysicsDirectBodyStateSW, PhysicsDirectBodyState );
+
+public:
+
+ static PhysicsDirectBodyStateSW *singleton;
+ BodySW *body;
+ real_t step;
+
+ virtual Vector3 get_total_gravity() const { return body->get_gravity(); } // get gravity vector working on this body space/area
+ virtual float get_total_density() const { return body->get_density(); } // get density of this body space/area
+
+ virtual float get_inverse_mass() const { return body->get_inv_mass(); } // get the mass
+ virtual Vector3 get_inverse_inertia() const { return body->get_inv_inertia(); } // get density of this body space
+ virtual Matrix3 get_inverse_inertia_tensor() const { return body->get_inv_inertia_tensor(); } // get density of this body space
+
+ virtual void set_linear_velocity(const Vector3& p_velocity) { body->set_linear_velocity(p_velocity); }
+ virtual Vector3 get_linear_velocity() const { return body->get_linear_velocity(); }
+
+ virtual void set_angular_velocity(const Vector3& p_velocity) { body->set_angular_velocity(p_velocity); }
+ virtual Vector3 get_angular_velocity() const { return body->get_angular_velocity(); }
+
+ virtual void set_transform(const Transform& p_transform) { body->set_state(PhysicsServer::BODY_STATE_TRANSFORM,p_transform); }
+ virtual Transform get_transform() const { return body->get_transform(); }
+
+ virtual void add_force(const Vector3& p_force, const Vector3& p_pos) { body->add_force(p_force,p_pos); }
+
+ virtual void set_sleep_state(bool p_enable) { body->set_active(!p_enable); }
+ virtual bool is_sleeping() const { return !body->is_active(); }
+
+ virtual int get_contact_count() const { return body->contact_count; }
+
+ virtual Vector3 get_contact_local_pos(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector3());
+ return body->contacts[p_contact_idx].local_pos;
+ }
+ virtual Vector3 get_contact_local_normal(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector3()); return body->contacts[p_contact_idx].local_normal; }
+ virtual int get_contact_local_shape(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,-1); return body->contacts[p_contact_idx].local_shape; }
+
+ virtual RID get_contact_collider(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,RID()); return body->contacts[p_contact_idx].collider; }
+ virtual Vector3 get_contact_collider_pos(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector3()); return body->contacts[p_contact_idx].collider_pos; }
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,0); return body->contacts[p_contact_idx].collider_instance_id; }
+ virtual int get_contact_collider_shape(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,0); return body->contacts[p_contact_idx].collider_shape; }
+ virtual Vector3 get_contact_collider_velocity_at_pos(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector3()); return body->contacts[p_contact_idx].collider_velocity_at_pos; }
+
+ virtual PhysicsDirectSpaceState* get_space_state();
+
+
+ virtual real_t get_step() const { return step; }
+ PhysicsDirectBodyStateSW() { singleton=this; body=NULL; }
+};
+
+
+#endif // BODY__SW_H
diff --git a/servers/physics/broad_phase_basic.cpp b/servers/physics/broad_phase_basic.cpp
new file mode 100644
index 0000000000..5db78e669f
--- /dev/null
+++ b/servers/physics/broad_phase_basic.cpp
@@ -0,0 +1,216 @@
+/*************************************************************************/
+/* broad_phase_basic.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "broad_phase_basic.h"
+#include "list.h"
+#include "print_string.h"
+BroadPhaseSW::ID BroadPhaseBasic::create(CollisionObjectSW *p_object_, int p_subindex) {
+
+ if (p_object_==NULL) {
+
+ ERR_FAIL_COND_V(p_object_==NULL,0);
+ }
+
+ current++;
+
+ Element e;
+ e.owner=p_object_;
+ e._static=false;
+ e.subindex=p_subindex;
+
+ element_map[current]=e;
+ return current;
+}
+
+void BroadPhaseBasic::move(ID p_id, const AABB& p_aabb) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ E->get().aabb=p_aabb;
+
+}
+void BroadPhaseBasic::set_static(ID p_id, bool p_static) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ E->get()._static=p_static;
+
+}
+void BroadPhaseBasic::remove(ID p_id) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ List<PairKey> to_erase;
+ //unpair must be done immediately on removal to avoid potential invalid pointers
+ for (Map<PairKey,void*>::Element *F=pair_map.front();F;F=F->next()) {
+
+ if (F->key().a==p_id || F->key().b==p_id) {
+
+ if (unpair_callback) {
+ Element *elem_A=&element_map[F->key().a];
+ Element *elem_B=&element_map[F->key().b];
+ unpair_callback(elem_A->owner,elem_A->subindex,elem_B->owner,elem_B->subindex,F->get(),unpair_userdata);
+ }
+ to_erase.push_back(F->key());
+ }
+ }
+ while(to_erase.size()) {
+
+ pair_map.erase(to_erase.front()->get());
+ to_erase.pop_front();
+ }
+ element_map.erase(E);
+
+}
+
+CollisionObjectSW *BroadPhaseBasic::get_object(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,NULL);
+ return E->get().owner;
+
+}
+bool BroadPhaseBasic::is_static(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,false);
+ return E->get()._static;
+
+}
+int BroadPhaseBasic::get_subindex(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,-1);
+ return E->get().subindex;
+}
+
+int BroadPhaseBasic::cull_segment(const Vector3& p_from, const Vector3& p_to,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices) {
+
+ int rc=0;
+
+ for (Map<ID,Element>::Element *E=element_map.front();E;E=E->next()) {
+
+ const AABB aabb=E->get().aabb;
+ if (aabb.intersects_segment(p_from,p_to)) {
+
+ p_results[rc]=E->get().owner;
+ p_result_indices[rc]=E->get().subindex;
+ rc++;
+ if (rc>=p_max_results)
+ break;
+ }
+ }
+
+ return rc;
+
+}
+int BroadPhaseBasic::cull_aabb(const AABB& p_aabb,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices) {
+
+ int rc=0;
+
+ for (Map<ID,Element>::Element *E=element_map.front();E;E=E->next()) {
+
+ const AABB aabb=E->get().aabb;
+ if (aabb.intersects(p_aabb)) {
+
+ p_results[rc]=E->get().owner;
+ p_result_indices[rc]=E->get().subindex;
+ rc++;
+ if (rc>=p_max_results)
+ break;
+ }
+ }
+
+ return rc;
+}
+
+void BroadPhaseBasic::set_pair_callback(PairCallback p_pair_callback,void *p_userdata) {
+
+ pair_userdata=p_userdata;
+ pair_callback=p_pair_callback;
+
+}
+void BroadPhaseBasic::set_unpair_callback(UnpairCallback p_pair_callback,void *p_userdata) {
+
+ unpair_userdata=p_userdata;
+ unpair_callback=p_pair_callback;
+
+}
+
+void BroadPhaseBasic::update() {
+
+ // recompute pairs
+ for(Map<ID,Element>::Element *I=element_map.front();I;I=I->next()) {
+
+ for(Map<ID,Element>::Element *J=I->next();J;J=J->next()) {
+
+ Element *elem_A=&I->get();
+ Element *elem_B=&J->get();
+
+ if (elem_A->owner == elem_B->owner)
+ continue;
+
+
+ bool pair_ok=elem_A->aabb.intersects( elem_B->aabb ) && (!elem_A->_static || !elem_B->_static );
+
+ PairKey key(I->key(),J->key());
+
+ Map<PairKey,void*>::Element *E=pair_map.find(key);
+
+ if (!pair_ok && E) {
+ if (unpair_callback)
+ unpair_callback(elem_A->owner,elem_A->subindex,elem_B->owner,elem_B->subindex,E->get(),unpair_userdata);
+ pair_map.erase(key);
+ }
+
+ if (pair_ok && !E) {
+
+ void *data=NULL;
+ if (pair_callback)
+ data=pair_callback(elem_A->owner,elem_A->subindex,elem_B->owner,elem_B->subindex,unpair_userdata);
+ pair_map.insert(key,data);
+ }
+ }
+ }
+
+}
+
+BroadPhaseSW *BroadPhaseBasic::_create() {
+
+ return memnew( BroadPhaseBasic );
+}
+
+BroadPhaseBasic::BroadPhaseBasic() {
+
+ current=1;
+ unpair_callback=NULL;
+ unpair_userdata=NULL;
+ pair_callback=NULL;
+ pair_userdata=NULL;
+
+}
diff --git a/servers/physics/broad_phase_basic.h b/servers/physics/broad_phase_basic.h
new file mode 100644
index 0000000000..7b77466292
--- /dev/null
+++ b/servers/physics/broad_phase_basic.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* broad_phase_basic.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 BROAD_PHASE_BASIC_H
+#define BROAD_PHASE_BASIC_H
+
+#include "broad_phase_sw.h"
+#include "map.h"
+
+class BroadPhaseBasic : public BroadPhaseSW {
+
+ struct Element {
+
+ CollisionObjectSW *owner;
+ bool _static;
+ AABB aabb;
+ int subindex;
+ };
+
+
+ Map<ID,Element> element_map;
+
+ ID current;
+
+ struct PairKey {
+
+ union {
+ struct {
+ ID a;
+ ID b;
+ };
+ uint64_t key;
+ };
+
+ _FORCE_INLINE_ bool operator<(const PairKey& p_key) const {
+ return key < p_key.key;
+ }
+
+ PairKey() { key=0; }
+ PairKey(ID p_a, ID p_b) { if (p_a>p_b) { a=p_b; b=p_a; } else { a=p_a; b=p_b; }}
+
+ };
+
+ Map<PairKey,void*> pair_map;
+
+
+ PairCallback pair_callback;
+ void *pair_userdata;
+ UnpairCallback unpair_callback;
+ void *unpair_userdata;
+
+public:
+
+ // 0 is an invalid ID
+ virtual ID create(CollisionObjectSW *p_object_, int p_subindex=0);
+ virtual void move(ID p_id, const AABB& p_aabb);
+ virtual void set_static(ID p_id, bool p_static);
+ virtual void remove(ID p_id);
+
+ virtual CollisionObjectSW *get_object(ID p_id) const;
+ virtual bool is_static(ID p_id) const;
+ virtual int get_subindex(ID p_id) const;
+
+ virtual int cull_segment(const Vector3& p_from, const Vector3& p_to,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices=NULL);
+ virtual int cull_aabb(const AABB& p_aabb,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices=NULL);
+
+ virtual void set_pair_callback(PairCallback p_pair_callback,void *p_userdata);
+ virtual void set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata);
+
+ virtual void update();
+
+ static BroadPhaseSW *_create();
+ BroadPhaseBasic();
+};
+
+#endif // BROAD_PHASE_BASIC_H
diff --git a/servers/physics/broad_phase_octree.cpp b/servers/physics/broad_phase_octree.cpp
new file mode 100644
index 0000000000..edf4aae2b2
--- /dev/null
+++ b/servers/physics/broad_phase_octree.cpp
@@ -0,0 +1,133 @@
+/*************************************************************************/
+/* broad_phase_octree.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "broad_phase_octree.h"
+#include "collision_object_sw.h"
+
+ID BroadPhaseOctree::create(CollisionObjectSW *p_object, int p_subindex) {
+
+ ID oid = octree.create(p_object,AABB(),p_subindex,false,1<<p_object->get_type(),0);
+ return oid;
+}
+
+void BroadPhaseOctree::move(ID p_id, const AABB& p_aabb){
+
+ octree.move(p_id,p_aabb);
+}
+
+void BroadPhaseOctree::set_static(ID p_id, bool p_static){
+
+ CollisionObjectSW *it = octree.get(p_id);
+ octree.set_pairable(p_id,p_static?false:true,1<<it->get_type(),p_static?0:0xFFFFF); //pair everything, don't care 1?
+
+}
+void BroadPhaseOctree::remove(ID p_id){
+
+ octree.erase(p_id);
+}
+
+CollisionObjectSW *BroadPhaseOctree::get_object(ID p_id) const{
+
+ CollisionObjectSW *it = octree.get(p_id);
+ ERR_FAIL_COND_V(!it,NULL);
+ return it;
+}
+bool BroadPhaseOctree::is_static(ID p_id) const{
+
+ return !octree.is_pairable(p_id);
+}
+int BroadPhaseOctree::get_subindex(ID p_id) const{
+
+ return octree.get_subindex(p_id);
+}
+
+int BroadPhaseOctree::cull_segment(const Vector3& p_from, const Vector3& p_to,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices){
+
+ return octree.cull_segment(p_from,p_to,p_results,p_max_results,p_result_indices);
+}
+
+int BroadPhaseOctree::cull_aabb(const AABB& p_aabb,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices) {
+
+ return octree.cull_AABB(p_aabb,p_results,p_max_results,p_result_indices);
+
+}
+
+
+void* BroadPhaseOctree::_pair_callback(void*self,OctreeElementID p_A, CollisionObjectSW*p_object_A,int subindex_A,OctreeElementID p_B, CollisionObjectSW*p_object_B,int subindex_B) {
+
+ BroadPhaseOctree *bpo=(BroadPhaseOctree*)(self);
+ if (!bpo->pair_callback)
+ return NULL;
+
+ return bpo->pair_callback(p_object_A,subindex_A,p_object_B,subindex_B,bpo->pair_userdata);
+
+}
+
+void BroadPhaseOctree::_unpair_callback(void*self,OctreeElementID p_A, CollisionObjectSW*p_object_A,int subindex_A,OctreeElementID p_B, CollisionObjectSW*p_object_B,int subindex_B,void*pairdata) {
+
+ BroadPhaseOctree *bpo=(BroadPhaseOctree*)(self);
+ if (!bpo->unpair_callback)
+ return;
+
+ bpo->unpair_callback(p_object_A,subindex_A,p_object_B,subindex_B,pairdata,bpo->unpair_userdata);
+
+}
+
+
+void BroadPhaseOctree::set_pair_callback(PairCallback p_pair_callback,void *p_userdata){
+
+ pair_callback=p_pair_callback;
+ pair_userdata=p_userdata;
+
+}
+void BroadPhaseOctree::set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata){
+
+ unpair_callback=p_unpair_callback;
+ unpair_userdata=p_userdata;
+
+}
+
+void BroadPhaseOctree::update() {
+ // does.. not?
+}
+
+BroadPhaseSW *BroadPhaseOctree::_create() {
+
+ return memnew( BroadPhaseOctree );
+}
+
+BroadPhaseOctree::BroadPhaseOctree() {
+ octree.set_pair_callback(_pair_callback,this);
+ octree.set_unpair_callback(_unpair_callback,this);
+ pair_callback=NULL;
+ pair_userdata=NULL;
+ pair_callback=NULL;
+ unpair_userdata=NULL;
+}
+
+
diff --git a/servers/physics/broad_phase_octree.h b/servers/physics/broad_phase_octree.h
new file mode 100644
index 0000000000..d365213966
--- /dev/null
+++ b/servers/physics/broad_phase_octree.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* broad_phase_octree.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 BROAD_PHASE_OCTREE_H
+#define BROAD_PHASE_OCTREE_H
+
+#include "broad_phase_sw.h"
+#include "octree.h"
+
+class BroadPhaseOctree : public BroadPhaseSW {
+
+
+ Octree<CollisionObjectSW,true> octree;
+
+ static void* _pair_callback(void*,OctreeElementID, CollisionObjectSW*,int,OctreeElementID, CollisionObjectSW*,int);
+ static void _unpair_callback(void*,OctreeElementID, CollisionObjectSW*,int,OctreeElementID, CollisionObjectSW*,int,void*);
+
+
+ PairCallback pair_callback;
+ void *pair_userdata;
+ UnpairCallback unpair_callback;
+ void *unpair_userdata;
+
+public:
+
+ // 0 is an invalid ID
+ virtual ID create(CollisionObjectSW *p_object_, int p_subindex=0);
+ virtual void move(ID p_id, const AABB& p_aabb);
+ virtual void set_static(ID p_id, bool p_static);
+ virtual void remove(ID p_id);
+
+ virtual CollisionObjectSW *get_object(ID p_id) const;
+ virtual bool is_static(ID p_id) const;
+ virtual int get_subindex(ID p_id) const;
+
+ virtual int cull_segment(const Vector3& p_from, const Vector3& p_to,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices=NULL);
+ virtual int cull_aabb(const AABB& p_aabb,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices=NULL);
+
+ virtual void set_pair_callback(PairCallback p_pair_callback,void *p_userdata);
+ virtual void set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata);
+
+ virtual void update();
+
+ static BroadPhaseSW *_create();
+ BroadPhaseOctree();
+};
+
+#endif // BROAD_PHASE_OCTREE_H
diff --git a/servers/physics/broad_phase_sw.cpp b/servers/physics/broad_phase_sw.cpp
new file mode 100644
index 0000000000..1211db141f
--- /dev/null
+++ b/servers/physics/broad_phase_sw.cpp
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* broad_phase_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "broad_phase_sw.h"
+
+BroadPhaseSW::CreateFunction BroadPhaseSW::create_func=NULL;
+
+BroadPhaseSW::~BroadPhaseSW()
+{
+}
diff --git a/servers/physics/broad_phase_sw.h b/servers/physics/broad_phase_sw.h
new file mode 100644
index 0000000000..57301a2af9
--- /dev/null
+++ b/servers/physics/broad_phase_sw.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* broad_phase_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 BROAD_PHASE_SW_H
+#define BROAD_PHASE_SW_H
+
+#include "math_funcs.h"
+#include "aabb.h"
+
+class CollisionObjectSW;
+
+
+class BroadPhaseSW {
+
+public:
+ typedef BroadPhaseSW* (*CreateFunction)();
+
+ static CreateFunction create_func;
+
+ typedef uint32_t ID;
+
+
+ typedef void* (*PairCallback)(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_userdata);
+ typedef void (*UnpairCallback)(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_data,void *p_userdata);
+
+ // 0 is an invalid ID
+ virtual ID create(CollisionObjectSW *p_object_, int p_subindex=0)=0;
+ virtual void move(ID p_id, const AABB& p_aabb)=0;
+ virtual void set_static(ID p_id, bool p_static)=0;
+ virtual void remove(ID p_id)=0;
+
+ virtual CollisionObjectSW *get_object(ID p_id) const=0;
+ virtual bool is_static(ID p_id) const=0;
+ virtual int get_subindex(ID p_id) const=0;
+
+ virtual int cull_segment(const Vector3& p_from, const Vector3& p_to,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices=NULL)=0;
+ virtual int cull_aabb(const AABB& p_aabb,CollisionObjectSW** p_results,int p_max_results,int *p_result_indices=NULL)=0;
+
+ virtual void set_pair_callback(PairCallback p_pair_callback,void *p_userdata)=0;
+ virtual void set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata)=0;
+
+ virtual void update()=0;
+
+ virtual ~BroadPhaseSW();
+
+};
+
+#endif // BROAD_PHASE__SW_H
diff --git a/servers/physics/collision_object_sw.cpp b/servers/physics/collision_object_sw.cpp
new file mode 100644
index 0000000000..156004d15d
--- /dev/null
+++ b/servers/physics/collision_object_sw.cpp
@@ -0,0 +1,219 @@
+/*************************************************************************/
+/* collision_object_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "collision_object_sw.h"
+#include "space_sw.h"
+
+void CollisionObjectSW::add_shape(ShapeSW *p_shape,const Transform& p_transform) {
+
+ Shape s;
+ s.shape=p_shape;
+ s.xform=p_transform;
+ s.xform_inv=s.xform.affine_inverse();
+ s.bpid=0; //needs update
+ shapes.push_back(s);
+ p_shape->add_owner(this);
+ _update_shapes();
+ _shapes_changed();
+
+}
+
+void CollisionObjectSW::set_shape(int p_index,ShapeSW *p_shape){
+
+ ERR_FAIL_INDEX(p_index,shapes.size());
+ shapes[p_index].shape->remove_owner(this);
+ shapes[p_index].shape=p_shape;
+
+ p_shape->add_owner(this);
+ _update_shapes();
+ _shapes_changed();
+
+}
+void CollisionObjectSW::set_shape_transform(int p_index,const Transform& p_transform){
+
+ ERR_FAIL_INDEX(p_index,shapes.size());
+
+ shapes[p_index].xform=p_transform;
+ shapes[p_index].xform_inv=p_transform.affine_inverse();
+ _update_shapes();
+ _shapes_changed();
+}
+
+void CollisionObjectSW::remove_shape(ShapeSW *p_shape) {
+
+ //remove a shape, all the times it appears
+ for(int i=0;i<shapes.size();i++) {
+
+ if (shapes[i].shape==p_shape) {
+ remove_shape(i);
+ i--;
+ }
+ }
+}
+
+void CollisionObjectSW::remove_shape(int p_index){
+
+ //remove anything from shape to be erased to end, so subindices don't change
+ ERR_FAIL_INDEX(p_index,shapes.size());
+ for(int i=p_index;i<shapes.size();i++) {
+
+ if (shapes[i].bpid==0)
+ continue;
+ //should never get here with a null owner
+ space->get_broadphase()->remove(shapes[i].bpid);
+ shapes[i].bpid=0;
+ }
+ shapes[p_index].shape->remove_owner(this);
+ shapes.remove(p_index);
+
+ _shapes_changed();
+
+}
+
+void CollisionObjectSW::_set_static(bool p_static) {
+ if (_static==p_static)
+ return;
+ _static=p_static;
+
+ if (!space)
+ return;
+ for(int i=0;i<get_shape_count();i++) {
+ Shape &s=shapes[i];
+ if (s.bpid>0) {
+ space->get_broadphase()->set_static(s.bpid,_static);
+ }
+ }
+
+}
+
+void CollisionObjectSW::_unregister_shapes() {
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid>0) {
+ space->get_broadphase()->remove(s.bpid);
+ s.bpid=0;
+ }
+ }
+
+}
+
+void CollisionObjectSW::_update_shapes() {
+
+ if (!space)
+ return;
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid==0) {
+ s.bpid=space->get_broadphase()->create(this,i);
+ space->get_broadphase()->set_static(s.bpid,_static);
+ }
+
+ //not quite correct, should compute the next matrix..
+ AABB shape_aabb=s.shape->get_aabb();
+ Transform xform = transform * s.xform;
+ shape_aabb=xform.xform(shape_aabb);
+ s.aabb_cache=shape_aabb;
+ s.aabb_cache=s.aabb_cache.grow( (s.aabb_cache.size.x + s.aabb_cache.size.y)*0.5*0.05 );
+
+
+ space->get_broadphase()->move(s.bpid,s.aabb_cache);
+ }
+
+}
+
+void CollisionObjectSW::_update_shapes_with_motion(const Vector3& p_motion) {
+
+
+ if (!space)
+ return;
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid==0) {
+ s.bpid=space->get_broadphase()->create(this,i);
+ space->get_broadphase()->set_static(s.bpid,_static);
+ }
+
+ //not quite correct, should compute the next matrix..
+ AABB shape_aabb=s.shape->get_aabb();
+ Transform xform = transform * s.xform;
+ shape_aabb=xform.xform(shape_aabb);
+ shape_aabb=shape_aabb.merge(AABB( shape_aabb.pos+p_motion,shape_aabb.size)); //use motion
+ s.aabb_cache=shape_aabb;
+
+ space->get_broadphase()->move(s.bpid,shape_aabb);
+ }
+
+
+}
+
+void CollisionObjectSW::_set_space(SpaceSW *p_space) {
+
+ if (space) {
+
+ space->remove_object(this);
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid) {
+ space->get_broadphase()->remove(s.bpid);
+ s.bpid=0;
+ }
+ }
+
+ }
+
+ space=p_space;
+
+ if (space) {
+
+ space->add_object(this);
+ _update_shapes();
+ }
+
+}
+
+void CollisionObjectSW::_shape_changed() {
+
+ _update_shapes();
+ _shapes_changed();
+}
+
+CollisionObjectSW::CollisionObjectSW(Type p_type) {
+
+ _static=true;
+ type=p_type;
+ space=NULL;
+ instance_id=0;
+}
diff --git a/servers/physics/collision_object_sw.h b/servers/physics/collision_object_sw.h
new file mode 100644
index 0000000000..6d60f2f078
--- /dev/null
+++ b/servers/physics/collision_object_sw.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* collision_object_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 COLLISION_OBJECT_SW_H
+#define COLLISION_OBJECT_SW_H
+
+#include "shape_sw.h"
+#include "servers/physics_server.h"
+#include "self_list.h"
+#include "broad_phase_sw.h"
+
+class SpaceSW;
+
+class CollisionObjectSW : public ShapeOwnerSW {
+public:
+ enum Type {
+ TYPE_AREA,
+ TYPE_BODY
+ };
+private:
+
+ Type type;
+ RID self;
+ ObjectID instance_id;
+
+ struct Shape {
+
+ Transform xform;
+ Transform xform_inv;
+ BroadPhaseSW::ID bpid;
+ AABB aabb_cache; //for rayqueries
+ ShapeSW *shape;
+ };
+
+ Vector<Shape> shapes;
+ SpaceSW *space;
+ Transform transform;
+ Transform inv_transform;
+ bool _static;
+
+ void _update_shapes();
+
+protected:
+
+
+ void _update_shapes_with_motion(const Vector3& p_motion);
+ void _unregister_shapes();
+
+ _FORCE_INLINE_ void _set_transform(const Transform& p_transform) { transform=p_transform; _update_shapes(); }
+ _FORCE_INLINE_ void _set_inv_transform(const Transform& p_transform) { inv_transform=p_transform; }
+ void _set_static(bool p_static);
+
+ virtual void _shapes_changed()=0;
+ void _set_space(SpaceSW *space);
+
+ CollisionObjectSW(Type p_type);
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ _FORCE_INLINE_ void set_instance_id(const ObjectID& p_instance_id) { instance_id=p_instance_id; }
+ _FORCE_INLINE_ ObjectID get_instance_id() const { return instance_id; }
+
+ void _shape_changed();
+
+ _FORCE_INLINE_ Type get_type() const { return type; }
+ void add_shape(ShapeSW *p_shape,const Transform& p_transform=Transform());
+ void set_shape(int p_index,ShapeSW *p_shape);
+ void set_shape_transform(int p_index,const Transform& p_transform);
+ _FORCE_INLINE_ int get_shape_count() const { return shapes.size(); }
+ _FORCE_INLINE_ ShapeSW *get_shape(int p_index) const { return shapes[p_index].shape; }
+ _FORCE_INLINE_ const Transform& get_shape_transform(int p_index) const { return shapes[p_index].xform; }
+ _FORCE_INLINE_ const Transform& get_shape_inv_transform(int p_index) const { return shapes[p_index].xform_inv; }
+ _FORCE_INLINE_ const AABB& get_shape_aabb(int p_index) const { return shapes[p_index].aabb_cache; }
+
+ _FORCE_INLINE_ Transform get_transform() const { return transform; }
+ _FORCE_INLINE_ Transform get_inv_transform() const { return inv_transform; }
+ _FORCE_INLINE_ SpaceSW* get_space() const { return space; }
+
+
+
+ void remove_shape(ShapeSW *p_shape);
+ void remove_shape(int p_index);
+
+ virtual void set_space(SpaceSW *p_space)=0;
+
+ _FORCE_INLINE_ bool is_static() const { return _static; }
+
+ virtual ~CollisionObjectSW() {}
+
+};
+
+#endif // COLLISION_OBJECT_SW_H
diff --git a/servers/physics/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp
new file mode 100644
index 0000000000..1cd40db772
--- /dev/null
+++ b/servers/physics/collision_solver_sat.cpp
@@ -0,0 +1,1331 @@
+/*************************************************************************/
+/* collision_solver_sat.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "collision_solver_sat.h"
+#include "geometry.h"
+
+#define _EDGE_IS_VALID_SUPPORT_TRESHOLD 0.02
+
+struct _CollectorCallback {
+
+ CollisionSolverSW::CallbackResult callback;
+ void *userdata;
+ bool swap;
+ bool collided;
+ Vector3 normal;
+ Vector3 *prev_axis;
+
+ _FORCE_INLINE_ void call(const Vector3& p_point_A, const Vector3& p_point_B) {
+
+ //if (normal.dot(p_point_A) >= normal.dot(p_point_B))
+ // return;
+ if (swap)
+ callback(p_point_B,p_point_A,userdata);
+ else
+ callback(p_point_A,p_point_B,userdata);
+ }
+
+};
+
+typedef void (*GenerateContactsFunc)(const Vector3 *,int, const Vector3 *,int ,_CollectorCallback *);
+
+
+static void _generate_contacts_point_point(const Vector3 * p_points_A,int p_point_count_A, const Vector3 * p_points_B,int p_point_count_B,_CollectorCallback *p_callback) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 1 );
+ ERR_FAIL_COND( p_point_count_B != 1 );
+#endif
+
+ p_callback->call(*p_points_A,*p_points_B);
+}
+
+static void _generate_contacts_point_edge(const Vector3 * p_points_A,int p_point_count_A, const Vector3 * p_points_B,int p_point_count_B,_CollectorCallback *p_callback) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 1 );
+ ERR_FAIL_COND( p_point_count_B != 2 );
+#endif
+
+ Vector3 closest_B = Geometry::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B );
+ p_callback->call(*p_points_A,closest_B);
+
+}
+
+static void _generate_contacts_point_face(const Vector3 * p_points_A,int p_point_count_A, const Vector3 * p_points_B,int p_point_count_B,_CollectorCallback *p_callback) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 1 );
+ ERR_FAIL_COND( p_point_count_B < 3 );
+#endif
+
+
+ Vector3 closest_B=Plane(p_points_B[0],p_points_B[1],p_points_B[2]).project( *p_points_A );
+
+ p_callback->call(*p_points_A,closest_B);
+
+}
+
+
+static void _generate_contacts_edge_edge(const Vector3 * p_points_A,int p_point_count_A, const Vector3 * p_points_B,int p_point_count_B,_CollectorCallback *p_callback) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 2 );
+ ERR_FAIL_COND( p_point_count_B != 2 ); // circle is actually a 4x3 matrix
+#endif
+
+
+
+ Vector3 rel_A=p_points_A[1]-p_points_A[0];
+ Vector3 rel_B=p_points_B[1]-p_points_B[0];
+
+ Vector3 c=rel_A.cross(rel_B).cross(rel_B);
+
+// if ( Math::abs(rel_A.dot(c) )<_EDGE_IS_VALID_SUPPORT_TRESHOLD ) {
+ if ( Math::abs(rel_A.dot(c) )<CMP_EPSILON ) {
+
+ // should handle somehow..
+ //ERR_PRINT("TODO FIX");
+ //return;
+
+ Vector3 axis = rel_A.normalized(); //make an axis
+ Vector3 base_A = p_points_A[0] - axis * axis.dot(p_points_A[0]);
+ Vector3 base_B = p_points_B[0] - axis * axis.dot(p_points_B[0]);
+
+ //sort all 4 points in axis
+ float dvec[4]={ axis.dot(p_points_A[0]), axis.dot(p_points_A[1]), axis.dot(p_points_B[0]), axis.dot(p_points_B[1]) };
+
+ SortArray<float> sa;
+ sa.sort(dvec,4);
+
+ //use the middle ones as contacts
+ p_callback->call(base_A+axis*dvec[1],base_B+axis*dvec[1]);
+ p_callback->call(base_A+axis*dvec[2],base_B+axis*dvec[2]);
+
+ return;
+
+ }
+
+ real_t d = (c.dot( p_points_B[0] ) - p_points_A[0].dot(c))/rel_A.dot(c);
+
+ if (d<0.0)
+ d=0.0;
+ else if (d>1.0)
+ d=1.0;
+
+ Vector3 closest_A=p_points_A[0]+rel_A*d;
+ Vector3 closest_B=Geometry::get_closest_point_to_segment_uncapped(closest_A, p_points_B);
+ p_callback->call(closest_A,closest_B);
+
+}
+
+static void _generate_contacts_face_face(const Vector3 * p_points_A,int p_point_count_A, const Vector3 * p_points_B,int p_point_count_B,_CollectorCallback *p_callback) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A <2 );
+ ERR_FAIL_COND( p_point_count_B <3 );
+#endif
+
+ static const int max_clip=32;
+
+ Vector3 _clipbuf1[max_clip];
+ Vector3 _clipbuf2[max_clip];
+ Vector3 *clipbuf_src=_clipbuf1;
+ Vector3 *clipbuf_dst=_clipbuf2;
+ int clipbuf_len=p_point_count_A;
+
+ // copy A points to clipbuf_src
+ for (int i=0;i<p_point_count_A;i++) {
+
+ clipbuf_src[i]=p_points_A[i];
+ }
+
+ Plane plane_B(p_points_B[0],p_points_B[1],p_points_B[2]);
+
+ // go through all of B points
+ for (int i=0;i<p_point_count_B;i++) {
+
+ int i_n=(i+1)%p_point_count_B;
+
+ Vector3 edge0_B=p_points_B[i];
+ Vector3 edge1_B=p_points_B[i_n];
+
+ Vector3 clip_normal = (edge0_B - edge1_B).cross( plane_B.normal ).normalized();
+ // make a clip plane
+
+
+ Plane clip(edge0_B,clip_normal);
+ // avoid double clip if A is edge
+ int dst_idx=0;
+ bool edge = clipbuf_len==2;
+ for (int j=0;j<clipbuf_len;j++) {
+
+ int j_n=(j+1)%clipbuf_len;
+
+ Vector3 edge0_A=clipbuf_src[j];
+ Vector3 edge1_A=clipbuf_src[j_n];
+
+ real_t dist0 = clip.distance_to(edge0_A);
+ real_t dist1 = clip.distance_to(edge1_A);
+
+
+ if ( dist0 <= 0 ) { // behind plane
+
+ ERR_FAIL_COND( dst_idx >= max_clip );
+ clipbuf_dst[dst_idx++]=clipbuf_src[j];
+
+ }
+
+
+ // check for different sides and non coplanar
+// if ( (dist0*dist1) < -CMP_EPSILON && !(edge && j)) {
+ if ( (dist0*dist1) < 0 && !(edge && j)) {
+
+ // calculate intersection
+ Vector3 rel = edge1_A - edge0_A;
+ real_t den=clip.normal.dot( rel );
+ real_t dist=-(clip.normal.dot( edge0_A )-clip.d)/den;
+ Vector3 inters = edge0_A+rel*dist;
+
+ ERR_FAIL_COND( dst_idx >= max_clip );
+ clipbuf_dst[dst_idx]=inters;
+ dst_idx++;
+ }
+ }
+
+ clipbuf_len=dst_idx;
+ SWAP(clipbuf_src,clipbuf_dst);
+ }
+
+
+ // generate contacts
+ //Plane plane_A(p_points_A[0],p_points_A[1],p_points_A[2]);
+
+ int added=0;
+
+ for (int i=0;i<clipbuf_len;i++) {
+
+ float d = plane_B.distance_to(clipbuf_src[i]);
+ if (d>CMP_EPSILON)
+ continue;
+
+ Vector3 closest_B=clipbuf_src[i] - plane_B.normal*d;
+
+ p_callback->call(clipbuf_src[i],closest_B);
+ added++;
+
+ }
+
+}
+
+
+static void _generate_contacts_from_supports(const Vector3 * p_points_A,int p_point_count_A, const Vector3 * p_points_B,int p_point_count_B,_CollectorCallback *p_callback) {
+
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A <1 );
+ ERR_FAIL_COND( p_point_count_B <1 );
+#endif
+
+
+ static const GenerateContactsFunc generate_contacts_func_table[3][3]={
+ {
+ _generate_contacts_point_point,
+ _generate_contacts_point_edge,
+ _generate_contacts_point_face,
+ },{
+ 0,
+ _generate_contacts_edge_edge,
+ _generate_contacts_face_face,
+ },{
+ 0,0,
+ _generate_contacts_face_face,
+ }
+ };
+
+ int pointcount_B;
+ int pointcount_A;
+ const Vector3 *points_A;
+ const Vector3 *points_B;
+
+ if (p_point_count_A > p_point_count_B) {
+ //swap
+ p_callback->swap = !p_callback->swap;
+ p_callback->normal = -p_callback->normal;
+
+ pointcount_B = p_point_count_A;
+ pointcount_A = p_point_count_B;
+ points_A=p_points_B;
+ points_B=p_points_A;
+ } else {
+
+ pointcount_B = p_point_count_B;
+ pointcount_A = p_point_count_A;
+ points_A=p_points_A;
+ points_B=p_points_B;
+ }
+
+ int version_A = (pointcount_A > 3 ? 3 : pointcount_A) -1;
+ int version_B = (pointcount_B > 3 ? 3 : pointcount_B) -1;
+
+ GenerateContactsFunc contacts_func = generate_contacts_func_table[version_A][version_B];
+ ERR_FAIL_COND(!contacts_func);
+ contacts_func(points_A,pointcount_A,points_B,pointcount_B,p_callback);
+
+}
+
+
+
+template<class ShapeA, class ShapeB>
+class SeparatorAxisTest {
+
+ const ShapeA *shape_A;
+ const ShapeB *shape_B;
+ const Transform *transform_A;
+ const Transform *transform_B;
+ real_t best_depth;
+ Vector3 best_axis;
+ _CollectorCallback *callback;
+
+ Vector3 separator_axis;
+
+public:
+
+ _FORCE_INLINE_ bool test_previous_axis() {
+
+ if (callback && callback->prev_axis && *callback->prev_axis!=Vector3())
+ return test_axis(*callback->prev_axis);
+ else
+ return true;
+ }
+
+ _FORCE_INLINE_ bool test_axis(const Vector3& p_axis) {
+
+ Vector3 axis=p_axis;
+
+ if ( Math::abs(axis.x)<CMP_EPSILON &&
+ Math::abs(axis.y)<CMP_EPSILON &&
+ Math::abs(axis.z)<CMP_EPSILON ) {
+ // strange case, try an upwards separator
+ axis=Vector3(0.0,1.0,0.0);
+ }
+
+ real_t min_A,max_A,min_B,max_B;
+
+ shape_A->project_range(axis,*transform_A,min_A,max_A);
+ shape_B->project_range(axis,*transform_B,min_B,max_B);
+
+ min_B -= ( max_A - min_A ) * 0.5;
+ max_B += ( max_A - min_A ) * 0.5;
+
+ real_t dmin = min_B - ( min_A + max_A ) * 0.5;
+ real_t dmax = max_B - ( min_A + max_A ) * 0.5;
+
+ if (dmin > 0.0 || dmax < 0.0) {
+ separator_axis=axis;
+ return false; // doesn't contain 0
+ }
+
+ //use the smallest depth
+
+ dmin = Math::abs(dmin);
+
+ if ( dmax < dmin ) {
+ if ( dmax < best_depth ) {
+ best_depth=dmax;
+ best_axis=axis;
+ }
+ } else {
+ if ( dmin < best_depth ) {
+ best_depth=dmin;
+ best_axis=-axis; // keep it as A axis
+ }
+ }
+
+ return true;
+ }
+
+
+ _FORCE_INLINE_ void generate_contacts() {
+
+ // nothing to do, don't generate
+ if (best_axis==Vector3(0.0,0.0,0.0))
+ return;
+
+ if (!callback->callback) {
+ //just was checking intersection?
+ callback->collided=true;
+ if (callback->prev_axis)
+ *callback->prev_axis=best_axis;
+ return;
+ }
+
+ static const int max_supports=16;
+
+ Vector3 supports_A[max_supports];
+ int support_count_A;
+ shape_A->get_supports(transform_A->basis.xform_inv(-best_axis).normalized(),max_supports,supports_A,support_count_A);
+ for(int i=0;i<support_count_A;i++) {
+ supports_A[i] = transform_A->xform(supports_A[i]);
+ }
+
+
+ Vector3 supports_B[max_supports];
+ int support_count_B;
+ shape_B->get_supports(transform_B->basis.xform_inv(best_axis).normalized(),max_supports,supports_B,support_count_B);
+ for(int i=0;i<support_count_B;i++) {
+ supports_B[i] = transform_B->xform(supports_B[i]);
+ }
+/*
+ print_line("best depth: "+rtos(best_depth));
+ for(int i=0;i<support_count_A;i++) {
+
+ print_line("A-"+itos(i)+": "+supports_A[i]);
+ }
+ for(int i=0;i<support_count_B;i++) {
+
+ print_line("B-"+itos(i)+": "+supports_B[i]);
+ }
+*/
+ callback->normal=best_axis;
+ if (callback->prev_axis)
+ *callback->prev_axis=best_axis;
+ _generate_contacts_from_supports(supports_A,support_count_A,supports_B,support_count_B,callback);
+
+ callback->collided=true;
+ //CollisionSolverSW::CallbackResult cbk=NULL;
+ //cbk(Vector3(),Vector3(),NULL);
+
+ }
+
+ _FORCE_INLINE_ SeparatorAxisTest(const ShapeA *p_shape_A,const Transform& p_transform_A, const ShapeB *p_shape_B,const Transform& p_transform_B,_CollectorCallback *p_callback) {
+ best_depth=1e15;
+ shape_A=p_shape_A;
+ shape_B=p_shape_B;
+ transform_A=&p_transform_A;
+ transform_B=&p_transform_B;
+ callback=p_callback;
+ }
+
+};
+
+/****** SAT TESTS *******/
+/****** SAT TESTS *******/
+/****** SAT TESTS *******/
+/****** SAT TESTS *******/
+
+
+typedef void (*CollisionFunc)(const ShapeSW*,const Transform&,const ShapeSW*,const Transform&,_CollectorCallback *p_callback);
+
+
+static void _collision_sphere_sphere(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+ const SphereShapeSW *sphere_A = static_cast<const SphereShapeSW*>(p_a);
+ const SphereShapeSW *sphere_B = static_cast<const SphereShapeSW*>(p_b);
+
+ SeparatorAxisTest<SphereShapeSW,SphereShapeSW> separator(sphere_A,p_transform_a,sphere_B,p_transform_b,p_collector);
+
+ // previous axis
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis( (p_transform_a.origin-p_transform_b.origin).normalized() ))
+ return;
+
+ separator.generate_contacts();
+}
+
+static void _collision_sphere_box(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+ const SphereShapeSW *sphere_A = static_cast<const SphereShapeSW*>(p_a);
+ const BoxShapeSW *box_B = static_cast<const BoxShapeSW*>(p_b);
+
+ SeparatorAxisTest<SphereShapeSW,BoxShapeSW> separator(sphere_A,p_transform_a,box_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ // test faces
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_b.basis.get_axis(i).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+
+ // calculate closest point to sphere
+
+ Vector3 cnormal=p_transform_b.xform_inv( p_transform_a.origin );
+
+ Vector3 cpoint=p_transform_b.xform( Vector3(
+
+ (cnormal.x<0) ? -box_B->get_half_extents().x : box_B->get_half_extents().x,
+ (cnormal.y<0) ? -box_B->get_half_extents().y : box_B->get_half_extents().y,
+ (cnormal.z<0) ? -box_B->get_half_extents().z : box_B->get_half_extents().z
+ ) );
+
+ // use point to test axis
+ Vector3 point_axis = (p_transform_a.origin - cpoint).normalized();
+
+ if (!separator.test_axis( point_axis ))
+ return;
+
+ // test edges
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = point_axis.cross( p_transform_b.basis.get_axis(i) ).cross( p_transform_b.basis.get_axis(i) ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ separator.generate_contacts();
+
+
+}
+
+
+static void _collision_sphere_capsule(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+ const SphereShapeSW *sphere_A = static_cast<const SphereShapeSW*>(p_a);
+ const CapsuleShapeSW *capsule_B = static_cast<const CapsuleShapeSW*>(p_b);
+
+ SeparatorAxisTest<SphereShapeSW,CapsuleShapeSW> separator(sphere_A,p_transform_a,capsule_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //capsule sphere 1, sphere
+
+ Vector3 capsule_axis = p_transform_b.basis.get_axis(2) * (capsule_B->get_height() * 0.5);
+
+ Vector3 capsule_ball_1 = p_transform_b.origin + capsule_axis;
+
+ if (!separator.test_axis( (capsule_ball_1 - p_transform_a.origin).normalized() ) )
+ return;
+
+ //capsule sphere 2, sphere
+
+ Vector3 capsule_ball_2 = p_transform_b.origin - capsule_axis;
+
+ if (!separator.test_axis( (capsule_ball_1 - p_transform_a.origin).normalized() ) )
+ return;
+
+ //capsule edge, sphere
+
+ Vector3 b2a = p_transform_a.origin - p_transform_b.origin;
+
+ Vector3 axis = b2a.cross( capsule_axis ).cross( capsule_axis ).normalized();
+
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ separator.generate_contacts();
+}
+
+static void _collision_sphere_convex_polygon(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+ const SphereShapeSW *sphere_A = static_cast<const SphereShapeSW*>(p_a);
+ const ConvexPolygonShapeSW *convex_polygon_B = static_cast<const ConvexPolygonShapeSW*>(p_b);
+
+ SeparatorAxisTest<SphereShapeSW,ConvexPolygonShapeSW> separator(sphere_A,p_transform_a,convex_polygon_B,p_transform_b,p_collector);
+
+
+ if (!separator.test_previous_axis())
+ return;
+
+ const Geometry::MeshData &mesh = convex_polygon_B->get_mesh();
+
+ const Geometry::MeshData::Face *faces = mesh.faces.ptr();
+ int face_count = mesh.faces.size();
+ const Geometry::MeshData::Edge *edges = mesh.edges.ptr();
+ int edge_count = mesh.edges.size();
+ const Vector3 *vertices = mesh.vertices.ptr();
+ int vertex_count = mesh.vertices.size();
+
+
+ // faces of B
+ for (int i=0;i<face_count;i++) {
+
+ Vector3 axis = p_transform_b.xform( faces[i].plane ).normal;
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+
+ // edges of B
+ for(int i=0;i<edge_count;i++) {
+
+
+ Vector3 v1=p_transform_b.xform( vertices[ edges[i].a ] );
+ Vector3 v2=p_transform_b.xform( vertices[ edges[i].b ] );
+ Vector3 v3=p_transform_a.origin;
+
+
+ Vector3 n1=v2-v1;
+ Vector3 n2=v2-v3;
+
+ Vector3 axis = n1.cross(n2).cross(n1).normalized();;
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+
+ // vertices of B
+ for(int i=0;i<vertex_count;i++) {
+
+
+ Vector3 v1=p_transform_b.xform( vertices[i] );
+ Vector3 v2=p_transform_a.origin;
+
+ Vector3 axis = (v2-v1).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+
+ separator.generate_contacts();
+
+
+}
+
+static void _collision_sphere_face(const ShapeSW *p_a,const Transform &p_transform_a, const ShapeSW *p_b,const Transform& p_transform_b, _CollectorCallback *p_collector) {
+
+ const SphereShapeSW *sphere_A = static_cast<const SphereShapeSW*>(p_a);
+ const FaceShapeSW *face_B = static_cast<const FaceShapeSW*>(p_b);
+
+
+
+ SeparatorAxisTest<SphereShapeSW,FaceShapeSW> separator(sphere_A,p_transform_a,face_B,p_transform_b,p_collector);
+
+
+ Vector3 vertex[3]={
+ p_transform_b.xform( face_B->vertex[0] ),
+ p_transform_b.xform( face_B->vertex[1] ),
+ p_transform_b.xform( face_B->vertex[2] ),
+ };
+
+ if (!separator.test_axis( (vertex[0]-vertex[2]).cross(vertex[0]-vertex[1]).normalized() ))
+ return;
+
+ // edges and points of B
+ for(int i=0;i<3;i++) {
+
+
+ Vector3 n1=vertex[i]-p_transform_a.origin;
+
+ if (!separator.test_axis( n1.normalized() )) {
+ return;
+ }
+
+ Vector3 n2=vertex[(i+1)%3]-vertex[i];
+
+ Vector3 axis = n1.cross(n2).cross(n2).normalized();
+
+ if (!separator.test_axis( axis )) {
+ return;
+ }
+
+ }
+
+ separator.generate_contacts();
+}
+
+
+
+
+
+static void _collision_box_box(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+ const BoxShapeSW *box_A = static_cast<const BoxShapeSW*>(p_a);
+ const BoxShapeSW *box_B = static_cast<const BoxShapeSW*>(p_b);
+
+ SeparatorAxisTest<BoxShapeSW,BoxShapeSW> separator(box_A,p_transform_a,box_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ // test faces of A
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_a.basis.get_axis(i).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+
+ // test faces of B
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_b.basis.get_axis(i).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+
+ // test combined edges
+ for (int i=0;i<3;i++) {
+
+ for (int j=0;j<3;j++) {
+
+ Vector3 axis = p_transform_a.basis.get_axis(i).cross( p_transform_b.basis.get_axis(j) );
+
+ if (axis.length_squared()<CMP_EPSILON)
+ continue;
+ axis.normalize();
+
+
+ if (!separator.test_axis( axis )) {
+ return;
+ }
+ }
+ }
+
+ separator.generate_contacts();
+
+
+}
+
+
+static void _collision_box_capsule(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+ const BoxShapeSW *box_A = static_cast<const BoxShapeSW*>(p_a);
+ const CapsuleShapeSW *capsule_B = static_cast<const CapsuleShapeSW*>(p_b);
+
+ SeparatorAxisTest<BoxShapeSW,CapsuleShapeSW> separator(box_A,p_transform_a,capsule_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ // faces of A
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_a.basis.get_axis(i);
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+
+ Vector3 cyl_axis = p_transform_b.basis.get_axis(2).normalized();
+
+ // edges of A, capsule cylinder
+
+ for (int i=0;i<3;i++) {
+
+ // cylinder
+ Vector3 box_axis = p_transform_a.basis.get_axis(i);
+ Vector3 axis = box_axis.cross( cyl_axis );
+ if (axis.length_squared() < CMP_EPSILON)
+ continue;
+
+ if (!separator.test_axis( axis.normalized() ))
+ return;
+ }
+
+ // points of A, capsule cylinder
+ // this sure could be made faster somehow..
+
+ for (int i=0;i<2;i++) {
+ for (int j=0;j<2;j++) {
+ for (int k=0;k<2;k++) {
+ Vector3 he = box_A->get_half_extents();
+ he.x*=(i*2-1);
+ he.y*=(j*2-1);
+ he.z*=(k*2-1);
+ Vector3 point=p_transform_a.origin;
+ for(int l=0;l<3;l++)
+ point+=p_transform_a.basis.get_axis(l)*he[l];
+
+ //Vector3 axis = (point - cyl_axis * cyl_axis.dot(point)).normalized();
+ Vector3 axis = Plane(cyl_axis,0).project(point).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+ }
+ }
+
+ // capsule balls, edges of A
+
+ for (int i=0;i<2;i++) {
+
+
+ Vector3 capsule_axis = p_transform_b.basis.get_axis(2)*(capsule_B->get_height()*0.5);
+
+ Vector3 sphere_pos = p_transform_b.origin + ((i==0)?capsule_axis:-capsule_axis);
+
+
+ Vector3 cnormal=p_transform_a.xform_inv( sphere_pos );
+
+ Vector3 cpoint=p_transform_a.xform( Vector3(
+
+ (cnormal.x<0) ? -box_A->get_half_extents().x : box_A->get_half_extents().x,
+ (cnormal.y<0) ? -box_A->get_half_extents().y : box_A->get_half_extents().y,
+ (cnormal.z<0) ? -box_A->get_half_extents().z : box_A->get_half_extents().z
+ ) );
+
+ // use point to test axis
+ Vector3 point_axis = (sphere_pos - cpoint).normalized();
+
+ if (!separator.test_axis( point_axis ))
+ return;
+
+ // test edges of A
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = point_axis.cross( p_transform_a.basis.get_axis(i) ).cross( p_transform_a.basis.get_axis(i) ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+ }
+
+
+ separator.generate_contacts();
+}
+
+
+static void _collision_box_convex_polygon(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+
+ const BoxShapeSW *box_A = static_cast<const BoxShapeSW*>(p_a);
+ const ConvexPolygonShapeSW *convex_polygon_B = static_cast<const ConvexPolygonShapeSW*>(p_b);
+
+ SeparatorAxisTest<BoxShapeSW,ConvexPolygonShapeSW> separator(box_A,p_transform_a,convex_polygon_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+
+ const Geometry::MeshData &mesh = convex_polygon_B->get_mesh();
+
+ const Geometry::MeshData::Face *faces = mesh.faces.ptr();
+ int face_count = mesh.faces.size();
+ const Geometry::MeshData::Edge *edges = mesh.edges.ptr();
+ int edge_count = mesh.edges.size();
+ const Vector3 *vertices = mesh.vertices.ptr();
+ int vertex_count = mesh.vertices.size();
+
+ // faces of A
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_a.basis.get_axis(i).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // faces of B
+ for (int i=0;i<face_count;i++) {
+
+ Vector3 axis = p_transform_b.xform( faces[i].plane ).normal;
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // A<->B edges
+ for (int i=0;i<3;i++) {
+
+ Vector3 e1 = p_transform_a.basis.get_axis(i);
+
+ for (int j=0;j<edge_count;j++) {
+
+ Vector3 e2=p_transform_b.basis.xform(vertices[edges[j].a]) - p_transform_b.basis.xform(vertices[edges[j].b]);
+
+ Vector3 axis=e1.cross( e2 ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+ }
+
+ separator.generate_contacts();
+
+
+}
+
+static void _collision_box_face(const ShapeSW *p_a,const Transform &p_transform_a, const ShapeSW *p_b,const Transform& p_transform_b, _CollectorCallback *p_collector) {
+
+
+ const BoxShapeSW *box_A = static_cast<const BoxShapeSW*>(p_a);
+ const FaceShapeSW *face_B = static_cast<const FaceShapeSW*>(p_b);
+
+ SeparatorAxisTest<BoxShapeSW,FaceShapeSW> separator(box_A,p_transform_a,face_B,p_transform_b,p_collector);
+
+ Vector3 vertex[3]={
+ p_transform_b.xform( face_B->vertex[0] ),
+ p_transform_b.xform( face_B->vertex[1] ),
+ p_transform_b.xform( face_B->vertex[2] ),
+ };
+
+ if (!separator.test_axis( (vertex[0]-vertex[2]).cross(vertex[0]-vertex[1]).normalized() ))
+ return;
+
+ // faces of A
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_a.basis.get_axis(i).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // combined edges
+ for(int i=0;i<3;i++) {
+
+ Vector3 e=vertex[i]-vertex[(i+1)%3];
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis = p_transform_a.basis.get_axis(i);
+
+ if (!separator.test_axis( e.cross(axis).normalized() ))
+ return;
+ }
+
+ }
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_capsule_capsule(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+ const CapsuleShapeSW *capsule_A = static_cast<const CapsuleShapeSW*>(p_a);
+ const CapsuleShapeSW *capsule_B = static_cast<const CapsuleShapeSW*>(p_b);
+
+ SeparatorAxisTest<CapsuleShapeSW,CapsuleShapeSW> separator(capsule_A,p_transform_a,capsule_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ // some values
+
+ Vector3 capsule_A_axis = p_transform_a.basis.get_axis(2) * (capsule_A->get_height() * 0.5);
+ Vector3 capsule_B_axis = p_transform_b.basis.get_axis(2) * (capsule_B->get_height() * 0.5);
+
+ Vector3 capsule_A_ball_1 = p_transform_a.origin + capsule_A_axis;
+ Vector3 capsule_A_ball_2 = p_transform_a.origin - capsule_A_axis;
+ Vector3 capsule_B_ball_1 = p_transform_b.origin + capsule_B_axis;
+ Vector3 capsule_B_ball_2 = p_transform_b.origin - capsule_B_axis;
+
+ //balls-balls
+
+ if (!separator.test_axis( (capsule_A_ball_1 - capsule_B_ball_1 ).normalized() ) )
+ return;
+ if (!separator.test_axis( (capsule_A_ball_1 - capsule_B_ball_2 ).normalized() ) )
+ return;
+
+ if (!separator.test_axis( (capsule_A_ball_2 - capsule_B_ball_1 ).normalized() ) )
+ return;
+ if (!separator.test_axis( (capsule_A_ball_2 - capsule_B_ball_2 ).normalized() ) )
+ return;
+
+
+ // edges-balls
+
+ if (!separator.test_axis( (capsule_A_ball_1 - capsule_B_ball_1 ).cross(capsule_A_axis).cross(capsule_A_axis).normalized() ) )
+ return;
+
+ if (!separator.test_axis( (capsule_A_ball_1 - capsule_B_ball_2 ).cross(capsule_A_axis).cross(capsule_A_axis).normalized() ) )
+ return;
+
+ if (!separator.test_axis( (capsule_B_ball_1 - capsule_A_ball_1 ).cross(capsule_B_axis).cross(capsule_B_axis).normalized() ) )
+ return;
+
+ if (!separator.test_axis( (capsule_B_ball_1 - capsule_A_ball_2 ).cross(capsule_B_axis).cross(capsule_B_axis).normalized() ) )
+ return;
+
+ // edges
+
+ if (!separator.test_axis( capsule_A_axis.cross(capsule_B_axis).normalized() ) )
+ return;
+
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_capsule_convex_polygon(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+ const CapsuleShapeSW *capsule_A = static_cast<const CapsuleShapeSW*>(p_a);
+ const ConvexPolygonShapeSW *convex_polygon_B = static_cast<const ConvexPolygonShapeSW*>(p_b);
+
+ SeparatorAxisTest<CapsuleShapeSW,ConvexPolygonShapeSW> separator(capsule_A,p_transform_a,convex_polygon_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ const Geometry::MeshData &mesh = convex_polygon_B->get_mesh();
+
+ const Geometry::MeshData::Face *faces = mesh.faces.ptr();
+ int face_count = mesh.faces.size();
+ const Geometry::MeshData::Edge *edges = mesh.edges.ptr();
+ int edge_count = mesh.edges.size();
+ const Vector3 *vertices = mesh.vertices.ptr();
+ int vertex_count = mesh.vertices.size();
+
+ // faces of B
+ for (int i=0;i<face_count;i++) {
+
+ Vector3 axis = p_transform_b.xform( faces[i].plane ).normal;
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // edges of B, capsule cylinder
+
+ for (int i=0;i<edge_count;i++) {
+
+ // cylinder
+ Vector3 edge_axis = p_transform_b.basis.xform( vertices[ edges[i].a] ) - p_transform_b.basis.xform( vertices[ edges[i].b] );
+ Vector3 axis = edge_axis.cross( p_transform_a.basis.get_axis(2) ).normalized();
+
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // capsule balls, edges of B
+
+ for (int i=0;i<2;i++) {
+
+ // edges of B, capsule cylinder
+
+ Vector3 capsule_axis = p_transform_a.basis.get_axis(2)*(capsule_A->get_height()*0.5);
+
+ Vector3 sphere_pos = p_transform_a.origin + ((i==0)?capsule_axis:-capsule_axis);
+
+ for (int j=0;j<edge_count;j++) {
+
+
+ Vector3 n1=sphere_pos - p_transform_b.xform( vertices[ edges[j].a] );
+ Vector3 n2=p_transform_b.basis.xform( vertices[ edges[j].a] ) - p_transform_b.basis.xform( vertices[ edges[j].b] );
+
+ Vector3 axis = n1.cross(n2).cross(n2).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+ }
+
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_capsule_face(const ShapeSW *p_a,const Transform &p_transform_a, const ShapeSW *p_b,const Transform& p_transform_b, _CollectorCallback *p_collector) {
+
+ const CapsuleShapeSW *capsule_A = static_cast<const CapsuleShapeSW*>(p_a);
+ const FaceShapeSW *face_B = static_cast<const FaceShapeSW*>(p_b);
+
+ SeparatorAxisTest<CapsuleShapeSW,FaceShapeSW> separator(capsule_A,p_transform_a,face_B,p_transform_b,p_collector);
+
+
+
+ Vector3 vertex[3]={
+ p_transform_b.xform( face_B->vertex[0] ),
+ p_transform_b.xform( face_B->vertex[1] ),
+ p_transform_b.xform( face_B->vertex[2] ),
+ };
+
+ if (!separator.test_axis( (vertex[0]-vertex[2]).cross(vertex[0]-vertex[1]).normalized() ))
+ return;
+
+ // edges of B, capsule cylinder
+
+ Vector3 capsule_axis = p_transform_a.basis.get_axis(2)*(capsule_A->get_height()*0.5);
+
+ for (int i=0;i<3;i++) {
+
+ // edge-cylinder
+ Vector3 edge_axis = vertex[i]-vertex[(i+1)%3];
+ Vector3 axis = edge_axis.cross( capsule_axis ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ if (!separator.test_axis( (p_transform_a.origin-vertex[i]).cross(capsule_axis).cross(capsule_axis).normalized() ))
+ return;
+
+ for (int j=0;j<2;j++) {
+
+ // point-spheres
+ Vector3 sphere_pos = p_transform_a.origin + ( (j==0) ? capsule_axis : -capsule_axis );
+
+ Vector3 n1=sphere_pos - vertex[i];
+
+ if (!separator.test_axis( n1.normalized() ))
+ return;
+
+ Vector3 n2=edge_axis;
+
+ axis = n1.cross(n2).cross(n2);
+
+ if (!separator.test_axis( axis.normalized() ))
+ return;
+
+
+ }
+
+ }
+
+
+ separator.generate_contacts();
+
+}
+
+
+static void _collision_convex_polygon_convex_polygon(const ShapeSW *p_a,const Transform &p_transform_a,const ShapeSW *p_b,const Transform &p_transform_b,_CollectorCallback *p_collector) {
+
+
+ const ConvexPolygonShapeSW *convex_polygon_A = static_cast<const ConvexPolygonShapeSW*>(p_a);
+ const ConvexPolygonShapeSW *convex_polygon_B = static_cast<const ConvexPolygonShapeSW*>(p_b);
+
+ SeparatorAxisTest<ConvexPolygonShapeSW,ConvexPolygonShapeSW> separator(convex_polygon_A,p_transform_a,convex_polygon_B,p_transform_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ const Geometry::MeshData &mesh_A = convex_polygon_A->get_mesh();
+
+ const Geometry::MeshData::Face *faces_A = mesh_A.faces.ptr();
+ int face_count_A = mesh_A.faces.size();
+ const Geometry::MeshData::Edge *edges_A = mesh_A.edges.ptr();
+ int edge_count_A = mesh_A.edges.size();
+ const Vector3 *vertices_A = mesh_A.vertices.ptr();
+ int vertex_count_A = mesh_A.vertices.size();
+
+ const Geometry::MeshData &mesh_B = convex_polygon_B->get_mesh();
+
+ const Geometry::MeshData::Face *faces_B = mesh_B.faces.ptr();
+ int face_count_B = mesh_B.faces.size();
+ const Geometry::MeshData::Edge *edges_B = mesh_B.edges.ptr();
+ int edge_count_B = mesh_B.edges.size();
+ const Vector3 *vertices_B = mesh_B.vertices.ptr();
+ int vertex_count_B = mesh_B.vertices.size();
+
+ // faces of A
+ for (int i=0;i<face_count_A;i++) {
+
+ Vector3 axis = p_transform_a.xform( faces_A[i].plane ).normal;
+// Vector3 axis = p_transform_a.basis.xform( faces_A[i].plane.normal ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // faces of B
+ for (int i=0;i<face_count_B;i++) {
+
+ Vector3 axis = p_transform_b.xform( faces_B[i].plane ).normal;
+// Vector3 axis = p_transform_b.basis.xform( faces_B[i].plane.normal ).normalized();
+
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+ // A<->B edges
+ for (int i=0;i<edge_count_A;i++) {
+
+ Vector3 e1=p_transform_a.basis.xform( vertices_A[ edges_A[i].a] ) -p_transform_a.basis.xform( vertices_A[ edges_A[i].b] );
+
+ for (int j=0;j<edge_count_B;j++) {
+
+ Vector3 e2=p_transform_b.basis.xform( vertices_B[ edges_B[j].a] ) -p_transform_b.basis.xform( vertices_B[ edges_B[j].b] );
+
+ Vector3 axis=e1.cross( e2 ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+
+ }
+ }
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_convex_polygon_face(const ShapeSW *p_a,const Transform &p_transform_a, const ShapeSW *p_b,const Transform& p_transform_b, _CollectorCallback *p_collector) {
+
+
+ const ConvexPolygonShapeSW *convex_polygon_A = static_cast<const ConvexPolygonShapeSW*>(p_a);
+ const FaceShapeSW *face_B = static_cast<const FaceShapeSW*>(p_b);
+
+ SeparatorAxisTest<ConvexPolygonShapeSW,FaceShapeSW> separator(convex_polygon_A,p_transform_a,face_B,p_transform_b,p_collector);
+
+ const Geometry::MeshData &mesh = convex_polygon_A->get_mesh();
+
+ const Geometry::MeshData::Face *faces = mesh.faces.ptr();
+ int face_count = mesh.faces.size();
+ const Geometry::MeshData::Edge *edges = mesh.edges.ptr();
+ int edge_count = mesh.edges.size();
+ const Vector3 *vertices = mesh.vertices.ptr();
+ int vertex_count = mesh.vertices.size();
+
+
+
+ Vector3 vertex[3]={
+ p_transform_b.xform( face_B->vertex[0] ),
+ p_transform_b.xform( face_B->vertex[1] ),
+ p_transform_b.xform( face_B->vertex[2] ),
+ };
+
+ if (!separator.test_axis( (vertex[0]-vertex[2]).cross(vertex[0]-vertex[1]).normalized() ))
+ return;
+
+
+ // faces of A
+ for (int i=0;i<face_count;i++) {
+
+// Vector3 axis = p_transform_a.xform( faces[i].plane ).normal;
+ Vector3 axis = p_transform_a.basis.xform( faces[i].plane.normal ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+
+
+ // A<->B edges
+ for (int i=0;i<edge_count;i++) {
+
+ Vector3 e1=p_transform_a.xform( vertices[edges[i].a] ) - p_transform_a.xform( vertices[edges[i].b] );
+
+ for (int j=0;j<3;j++) {
+
+ Vector3 e2=vertex[j]-vertex[(j+1)%3];
+
+ Vector3 axis=e1.cross( e2 ).normalized();
+
+ if (!separator.test_axis( axis ))
+ return;
+ }
+ }
+
+ separator.generate_contacts();
+
+}
+
+
+bool sat_calculate_penetration(const ShapeSW *p_shape_A, const Transform& p_transform_A, const ShapeSW *p_shape_B, const Transform& p_transform_B, CollisionSolverSW::CallbackResult p_result_callback,void *p_userdata,bool p_swap,Vector3* r_prev_axis) {
+
+ PhysicsServer::ShapeType type_A=p_shape_A->get_type();
+
+ ERR_FAIL_COND_V(type_A==PhysicsServer::SHAPE_PLANE,false);
+ ERR_FAIL_COND_V(type_A==PhysicsServer::SHAPE_RAY,false);
+ ERR_FAIL_COND_V(p_shape_A->is_concave(),false);
+
+ PhysicsServer::ShapeType type_B=p_shape_B->get_type();
+
+ ERR_FAIL_COND_V(type_B==PhysicsServer::SHAPE_PLANE,false);
+ ERR_FAIL_COND_V(type_B==PhysicsServer::SHAPE_RAY,false);
+ ERR_FAIL_COND_V(p_shape_B->is_concave(),false);
+
+
+ static const CollisionFunc collision_table[5][5]={
+ {_collision_sphere_sphere,
+ _collision_sphere_box,
+ _collision_sphere_capsule,
+ _collision_sphere_convex_polygon,
+ _collision_sphere_face},
+ {0,
+ _collision_box_box,
+ _collision_box_capsule,
+ _collision_box_convex_polygon,
+ _collision_box_face},
+ {0,
+ 0,
+ _collision_capsule_capsule,
+ _collision_capsule_convex_polygon,
+ _collision_capsule_face},
+ {0,
+ 0,
+ 0,
+ _collision_convex_polygon_convex_polygon,
+ _collision_convex_polygon_face},
+ {0,
+ 0,
+ 0,
+ 0,
+ 0},
+ };
+
+ _CollectorCallback callback;
+ callback.callback=p_result_callback;
+ callback.swap=p_swap;
+ callback.userdata=p_userdata;
+ callback.collided=false;
+ callback.prev_axis=r_prev_axis;
+
+ const ShapeSW *A=p_shape_A;
+ const ShapeSW *B=p_shape_B;
+ const Transform *transform_A=&p_transform_A;
+ const Transform *transform_B=&p_transform_B;
+
+ if (type_A > type_B) {
+ SWAP(A,B);
+ SWAP(transform_A,transform_B);
+ SWAP(type_A,type_B);
+ callback.swap = !callback.swap;
+ }
+
+
+ CollisionFunc collision_func = collision_table[type_A-2][type_B-2];
+ ERR_FAIL_COND_V(!collision_func,false);
+
+
+ collision_func(A,*transform_A,B,*transform_B,&callback);
+
+ return callback.collided;
+
+}
diff --git a/servers/physics/collision_solver_sat.h b/servers/physics/collision_solver_sat.h
new file mode 100644
index 0000000000..5023a17810
--- /dev/null
+++ b/servers/physics/collision_solver_sat.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* collision_solver_sat.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 COLLISION_SOLVER_SAT_H
+#define COLLISION_SOLVER_SAT_H
+
+#include "collision_solver_sw.h"
+
+
+bool sat_calculate_penetration(const ShapeSW *p_shape_A, const Transform& p_transform_A, const ShapeSW *p_shape_B, const Transform& p_transform_B, CollisionSolverSW::CallbackResult p_result_callback,void *p_userdata, bool p_swap=false,Vector3* r_prev_axis=NULL);
+
+#endif // COLLISION_SOLVER_SAT_H
diff --git a/servers/physics/collision_solver_sw.cpp b/servers/physics/collision_solver_sw.cpp
new file mode 100644
index 0000000000..da28a4934f
--- /dev/null
+++ b/servers/physics/collision_solver_sw.cpp
@@ -0,0 +1,241 @@
+/*************************************************************************/
+/* collision_solver_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "collision_solver_sw.h"
+#include "collision_solver_sat.h"
+
+#include "gjk_epa.h"
+#include "collision_solver_sat.h"
+
+
+#define collision_solver sat_calculate_penetration
+//#define collision_solver gjk_epa_calculate_penetration
+
+
+bool CollisionSolverSW::solve_static_plane(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result) {
+
+ const PlaneShapeSW *plane = static_cast<const PlaneShapeSW*>(p_shape_A);
+ if (p_shape_B->get_type()==PhysicsServer::SHAPE_PLANE)
+ return false;
+ Plane p = p_transform_A.xform(plane->get_plane());
+
+ static const int max_supports = 16;
+ Vector3 supports[max_supports];
+ int support_count;
+
+ p_shape_B->get_supports(p_transform_B.basis.xform_inv(-p.normal).normalized(),max_supports,supports,support_count);
+
+ bool found=false;
+
+ for(int i=0;i<support_count;i++) {
+
+ supports[i] = p_transform_B.xform( supports[i] );
+ if (p.distance_to(supports[i])>=0)
+ continue;
+ found=true;
+
+ Vector3 support_A = p.project(supports[i]);
+
+ if (p_result_callback) {
+ if (p_swap_result)
+ p_result_callback(supports[i],support_A,p_userdata);
+ else
+ p_result_callback(support_A,supports[i],p_userdata);
+ }
+
+ }
+
+
+ return found;
+}
+
+bool CollisionSolverSW::solve_ray(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result) {
+
+
+ const RayShapeSW *ray = static_cast<const RayShapeSW*>(p_shape_A);
+
+ Vector3 from = p_transform_A.origin;
+ Vector3 to = from+p_transform_A.basis.get_axis(2)*ray->get_length();
+ Vector3 support_A=to;
+
+ Transform ai = p_transform_B.affine_inverse();
+
+ from = ai.xform(from);
+ to = ai.xform(to);
+
+ Vector3 p,n;
+ if (!p_shape_B->intersect_segment(from,to,p,n))
+ return false;
+
+ Vector3 support_B=p_transform_B.xform(p);
+
+ if (p_result_callback) {
+ if (p_swap_result)
+ p_result_callback(support_B,support_A,p_userdata);
+ else
+ p_result_callback(support_A,support_B,p_userdata);
+ }
+ return true;
+}
+
+struct _ConcaveCollisionInfo {
+
+ const Transform *transform_A;
+ const ShapeSW *shape_A;
+ const Transform *transform_B;
+ CollisionSolverSW::CallbackResult result_callback;
+ void *userdata;
+ bool swap_result;
+ bool collided;
+ int aabb_tests;
+ int collisions;
+
+};
+
+void CollisionSolverSW::concave_callback(void *p_userdata, ShapeSW *p_convex) {
+
+
+ _ConcaveCollisionInfo &cinfo = *(_ConcaveCollisionInfo*)(p_userdata);
+ cinfo.aabb_tests++;
+
+ bool collided = collision_solver(cinfo.shape_A, *cinfo.transform_A, p_convex,*cinfo.transform_B, cinfo.result_callback, cinfo.userdata, cinfo.swap_result );
+ if (!collided)
+ return;
+
+ cinfo.collided=true;
+ cinfo.collisions++;
+
+}
+
+bool CollisionSolverSW::solve_concave(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result) {
+
+
+ const ConcaveShapeSW *concave_B=static_cast<const ConcaveShapeSW*>(p_shape_B);
+
+ _ConcaveCollisionInfo cinfo;
+ cinfo.transform_A=&p_transform_A;
+ cinfo.shape_A=p_shape_A;
+ cinfo.transform_B=&p_transform_B;
+ cinfo.result_callback=p_result_callback;
+ cinfo.userdata=p_userdata;
+ cinfo.swap_result=p_swap_result;
+ cinfo.collided=false;
+ cinfo.collisions=0;
+
+ cinfo.aabb_tests=0;
+
+ Transform rel_transform = p_transform_A;
+ rel_transform.origin-=p_transform_B.origin;
+
+ //quickly compute a local AABB
+
+ AABB local_aabb;
+ for(int i=0;i<3;i++) {
+
+ Vector3 axis( p_transform_B.basis.get_axis(i) );
+ float axis_scale = 1.0/axis.length();
+ axis*=axis_scale;
+
+ float smin,smax;
+ p_shape_A->project_range(axis,rel_transform,smin,smax);
+ smin*=axis_scale;
+ smax*=axis_scale;
+
+ local_aabb.pos[i]=smin;
+ local_aabb.size[i]=smax-smin;
+ }
+
+ concave_B->cull(local_aabb,concave_callback,&cinfo);
+
+
+ return cinfo.collided;
+}
+
+
+bool CollisionSolverSW::solve_static(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,Vector3 *r_sep_axis) {
+
+
+ PhysicsServer::ShapeType type_A=p_shape_A->get_type();
+ PhysicsServer::ShapeType type_B=p_shape_B->get_type();
+ bool concave_A=p_shape_A->is_concave();
+ bool concave_B=p_shape_B->is_concave();
+
+ bool swap = false;
+
+ if (type_A>type_B) {
+ SWAP(type_A,type_B);
+ SWAP(concave_A,concave_B);
+ swap=true;
+ }
+
+ if (type_A==PhysicsServer::SHAPE_PLANE) {
+
+ if (type_B==PhysicsServer::SHAPE_PLANE)
+ return false;
+ if (type_B==PhysicsServer::SHAPE_RAY) {
+ return false;
+ }
+
+ if (swap) {
+ return solve_static_plane(p_shape_B,p_transform_B,p_shape_A,p_transform_A,p_result_callback,p_userdata,true);
+ } else {
+ return solve_static_plane(p_shape_A,p_transform_A,p_shape_B,p_transform_B,p_result_callback,p_userdata,false);
+ }
+
+ } else if (type_A==PhysicsServer::SHAPE_RAY) {
+
+ if (type_B==PhysicsServer::SHAPE_RAY)
+ return false;
+
+ if (swap) {
+ return solve_ray(p_shape_B,p_transform_B,p_shape_A,p_transform_A,p_result_callback,p_userdata,true);
+ } else {
+ return solve_ray(p_shape_A,p_transform_A,p_shape_B,p_transform_B,p_result_callback,p_userdata,false);
+ }
+
+ } else if (concave_B) {
+
+
+ if (concave_A)
+ return false;
+
+ if (!swap)
+ return solve_concave(p_shape_A,p_transform_A,p_shape_B,p_transform_B,p_result_callback,p_userdata,false);
+ else
+ return solve_concave(p_shape_B,p_transform_B,p_shape_A,p_transform_A,p_result_callback,p_userdata,true);
+
+
+
+ } else {
+
+ return collision_solver(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback,p_userdata,false,r_sep_axis);
+ }
+
+
+ return false;
+}
diff --git a/servers/physics/collision_solver_sw.h b/servers/physics/collision_solver_sw.h
new file mode 100644
index 0000000000..e135ab92e0
--- /dev/null
+++ b/servers/physics/collision_solver_sw.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* collision_solver_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 COLLISION_SOLVER_SW_H
+#define COLLISION_SOLVER_SW_H
+
+#include "shape_sw.h"
+
+class CollisionSolverSW
+{
+public:
+ typedef void (*CallbackResult)(const Vector3& p_point_A,const Vector3& p_point_B,void *p_userdata);
+private:
+
+ static void concave_callback(void *p_userdata, ShapeSW *p_convex);
+ static bool solve_static_plane(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result);
+ static bool solve_ray(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result);
+ static bool solve_concave(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result);
+
+public:
+
+
+ static bool solve_static(const ShapeSW *p_shape_A,const Transform& p_transform_A,const ShapeSW *p_shape_B,const Transform& p_transform_B,CallbackResult p_result_callback,void *p_userdata,Vector3 *r_sep_axis=NULL);
+
+};
+
+#endif // COLLISION_SOLVER__SW_H
diff --git a/servers/physics/constraint_sw.cpp b/servers/physics/constraint_sw.cpp
new file mode 100644
index 0000000000..f1179bdb5c
--- /dev/null
+++ b/servers/physics/constraint_sw.cpp
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* constraint_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "constraint_sw.h"
+
diff --git a/servers/physics/constraint_sw.h b/servers/physics/constraint_sw.h
new file mode 100644
index 0000000000..1be96422e1
--- /dev/null
+++ b/servers/physics/constraint_sw.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* constraint_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 CONSTRAINT_SW_H
+#define CONSTRAINT_SW_H
+
+#include "body_sw.h"
+
+class ConstraintSW {
+
+ BodySW **_body_ptr;
+ int _body_count;
+ uint64_t island_step;
+ ConstraintSW *island_next;
+ ConstraintSW *island_list_next;
+
+
+ RID self;
+
+protected:
+ ConstraintSW(BodySW **p_body_ptr=NULL,int p_body_count=0) { _body_ptr=p_body_ptr; _body_count=p_body_count; island_step=0; }
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ _FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
+ _FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step=p_step; }
+
+
+ _FORCE_INLINE_ ConstraintSW* get_island_next() const { return island_next; }
+ _FORCE_INLINE_ void set_island_next(ConstraintSW* p_next) { island_next=p_next; }
+
+ _FORCE_INLINE_ ConstraintSW* get_island_list_next() const { return island_list_next; }
+ _FORCE_INLINE_ void set_island_list_next(ConstraintSW* p_next) { island_list_next=p_next; }
+
+ _FORCE_INLINE_ BodySW **get_body_ptr() const { return _body_ptr; }
+ _FORCE_INLINE_ int get_body_count() const { return _body_count; }
+
+
+ virtual bool setup(float p_step)=0;
+ virtual void solve(float p_step)=0;
+
+ virtual ~ConstraintSW() {}
+};
+
+#endif // CONSTRAINT__SW_H
diff --git a/servers/physics/gjk_epa.cpp b/servers/physics/gjk_epa.cpp
new file mode 100644
index 0000000000..37edc0d414
--- /dev/null
+++ b/servers/physics/gjk_epa.cpp
@@ -0,0 +1,900 @@
+/*************************************************************************/
+/* gjk_epa.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "gjk_epa.h"
+
+/*************** Bullet's GJK-EPA2 IMPLEMENTATION *******************/
+
+ // Config
+
+/* GJK */
+#define GJK_MAX_ITERATIONS 128
+#define GJK_ACCURARY ((real_t)0.0001)
+#define GJK_MIN_DISTANCE ((real_t)0.0001)
+#define GJK_DUPLICATED_EPS ((real_t)0.0001)
+#define GJK_SIMPLEX2_EPS ((real_t)0.0)
+#define GJK_SIMPLEX3_EPS ((real_t)0.0)
+#define GJK_SIMPLEX4_EPS ((real_t)0.0)
+
+/* EPA */
+#define EPA_MAX_VERTICES 64
+#define EPA_MAX_FACES (EPA_MAX_VERTICES*2)
+#define EPA_MAX_ITERATIONS 255
+#define EPA_ACCURACY ((real_t)0.0001)
+#define EPA_FALLBACK (10*EPA_ACCURACY)
+#define EPA_PLANE_EPS ((real_t)0.00001)
+#define EPA_INSIDE_EPS ((real_t)0.01)
+
+namespace GjkEpa2 {
+
+
+struct sResults {
+ enum eStatus {
+ Separated, /* Shapes doesnt penetrate */
+ Penetrating, /* Shapes are penetrating */
+ GJK_Failed, /* GJK phase fail, no big issue, shapes are probably just 'touching' */
+ EPA_Failed /* EPA phase fail, bigger problem, need to save parameters, and debug */
+ } status;
+
+ Vector3 witnesses[2];
+ Vector3 normal;
+ real_t distance;
+};
+
+// Shorthands
+typedef unsigned int U;
+typedef unsigned char U1;
+
+// MinkowskiDiff
+struct MinkowskiDiff {
+
+ const ShapeSW* m_shapes[2];
+
+ Transform transform_A;
+ Transform transform_B;
+
+ // i wonder how this could be sped up... if it can
+ _FORCE_INLINE_ Vector3 Support0 ( const Vector3& d ) const {
+ return transform_A.xform( m_shapes[0]->get_support( transform_A.basis.xform_inv(d).normalized() ) );
+ }
+
+ _FORCE_INLINE_ Vector3 Support1 ( const Vector3& d ) const {
+ return transform_B.xform( m_shapes[1]->get_support( transform_B.basis.xform_inv(d).normalized() ) );
+ }
+
+ _FORCE_INLINE_ Vector3 Support ( const Vector3& d ) const {
+ return ( Support0 ( d )-Support1 ( -d ) );
+ }
+
+ _FORCE_INLINE_ Vector3 Support ( const Vector3& d,U index ) const
+ {
+ if ( index )
+ return ( Support1 ( d ) );
+ else
+ return ( Support0 ( d ) );
+ }
+};
+
+typedef MinkowskiDiff tShape;
+
+
+// GJK
+struct GJK
+{
+ /* Types */
+ struct sSV
+ {
+ Vector3 d,w;
+ };
+ struct sSimplex
+ {
+ sSV* c[4];
+ real_t p[4];
+ U rank;
+ };
+ struct eStatus { enum _ {
+ Valid,
+ Inside,
+ Failed };};
+ /* Fields */
+ tShape m_shape;
+ Vector3 m_ray;
+ real_t m_distance;
+ sSimplex m_simplices[2];
+ sSV m_store[4];
+ sSV* m_free[4];
+ U m_nfree;
+ U m_current;
+ sSimplex* m_simplex;
+ eStatus::_ m_status;
+ /* Methods */
+ GJK()
+ {
+ Initialize();
+ }
+ void Initialize()
+ {
+ m_ray = Vector3(0,0,0);
+ m_nfree = 0;
+ m_status = eStatus::Failed;
+ m_current = 0;
+ m_distance = 0;
+ }
+ eStatus::_ Evaluate(const tShape& shapearg,const Vector3& guess)
+ {
+ U iterations=0;
+ real_t sqdist=0;
+ real_t alpha=0;
+ Vector3 lastw[4];
+ U clastw=0;
+ /* Initialize solver */
+ m_free[0] = &m_store[0];
+ m_free[1] = &m_store[1];
+ m_free[2] = &m_store[2];
+ m_free[3] = &m_store[3];
+ m_nfree = 4;
+ m_current = 0;
+ m_status = eStatus::Valid;
+ m_shape = shapearg;
+ m_distance = 0;
+ /* Initialize simplex */
+ m_simplices[0].rank = 0;
+ m_ray = guess;
+ const real_t sqrl= m_ray.length_squared();
+ appendvertice(m_simplices[0],sqrl>0?-m_ray:Vector3(1,0,0));
+ m_simplices[0].p[0] = 1;
+ m_ray = m_simplices[0].c[0]->w;
+ sqdist = sqrl;
+ lastw[0] =
+ lastw[1] =
+ lastw[2] =
+ lastw[3] = m_ray;
+ /* Loop */
+ do {
+ const U next=1-m_current;
+ sSimplex& cs=m_simplices[m_current];
+ sSimplex& ns=m_simplices[next];
+ /* Check zero */
+ const real_t rl=m_ray.length();
+ if(rl<GJK_MIN_DISTANCE)
+ {/* Touching or inside */
+ m_status=eStatus::Inside;
+ break;
+ }
+ /* Append new vertice in -'v' direction */
+ appendvertice(cs,-m_ray);
+ const Vector3& w=cs.c[cs.rank-1]->w;
+ bool found=false;
+ for(U i=0;i<4;++i)
+ {
+ if((w-lastw[i]).length_squared()<GJK_DUPLICATED_EPS)
+ { found=true;break; }
+ }
+ if(found)
+ {/* Return old simplex */
+ removevertice(m_simplices[m_current]);
+ break;
+ }
+ else
+ {/* Update lastw */
+ lastw[clastw=(clastw+1)&3]=w;
+ }
+ /* Check for termination */
+ const real_t omega=vec3_dot(m_ray,w)/rl;
+ alpha=MAX(omega,alpha);
+ if(((rl-alpha)-(GJK_ACCURARY*rl))<=0)
+ {/* Return old simplex */
+ removevertice(m_simplices[m_current]);
+ break;
+ }
+ /* Reduce simplex */
+ real_t weights[4];
+ U mask=0;
+ switch(cs.rank)
+ {
+ case 2: sqdist=projectorigin( cs.c[0]->w,
+ cs.c[1]->w,
+ weights,mask);break;
+ case 3: sqdist=projectorigin( cs.c[0]->w,
+ cs.c[1]->w,
+ cs.c[2]->w,
+ weights,mask);break;
+ case 4: sqdist=projectorigin( cs.c[0]->w,
+ cs.c[1]->w,
+ cs.c[2]->w,
+ cs.c[3]->w,
+ weights,mask);break;
+ }
+ if(sqdist>=0)
+ {/* Valid */
+ ns.rank = 0;
+ m_ray = Vector3(0,0,0);
+ m_current = next;
+ for(U i=0,ni=cs.rank;i<ni;++i)
+ {
+ if(mask&(1<<i))
+ {
+ ns.c[ns.rank] = cs.c[i];
+ ns.p[ns.rank++] = weights[i];
+ m_ray += cs.c[i]->w*weights[i];
+ }
+ else
+ {
+ m_free[m_nfree++] = cs.c[i];
+ }
+ }
+ if(mask==15) m_status=eStatus::Inside;
+ }
+ else
+ {/* Return old simplex */
+ removevertice(m_simplices[m_current]);
+ break;
+ }
+ m_status=((++iterations)<GJK_MAX_ITERATIONS)?m_status:eStatus::Failed;
+ } while(m_status==eStatus::Valid);
+ m_simplex=&m_simplices[m_current];
+ switch(m_status)
+ {
+ case eStatus::Valid: m_distance=m_ray.length();break;
+ case eStatus::Inside: m_distance=0;break;
+ default: {}
+ }
+ return(m_status);
+ }
+ bool EncloseOrigin()
+ {
+ switch(m_simplex->rank)
+ {
+ case 1:
+ {
+ for(U i=0;i<3;++i)
+ {
+ Vector3 axis=Vector3(0,0,0);
+ axis[i]=1;
+ appendvertice(*m_simplex, axis);
+ if(EncloseOrigin()) return(true);
+ removevertice(*m_simplex);
+ appendvertice(*m_simplex,-axis);
+ if(EncloseOrigin()) return(true);
+ removevertice(*m_simplex);
+ }
+ }
+ break;
+ case 2:
+ {
+ const Vector3 d=m_simplex->c[1]->w-m_simplex->c[0]->w;
+ for(U i=0;i<3;++i)
+ {
+ Vector3 axis=Vector3(0,0,0);
+ axis[i]=1;
+ const Vector3 p=vec3_cross(d,axis);
+ if(p.length_squared()>0)
+ {
+ appendvertice(*m_simplex, p);
+ if(EncloseOrigin()) return(true);
+ removevertice(*m_simplex);
+ appendvertice(*m_simplex,-p);
+ if(EncloseOrigin()) return(true);
+ removevertice(*m_simplex);
+ }
+ }
+ }
+ break;
+ case 3:
+ {
+ const Vector3 n=vec3_cross(m_simplex->c[1]->w-m_simplex->c[0]->w,
+ m_simplex->c[2]->w-m_simplex->c[0]->w);
+ if(n.length_squared()>0)
+ {
+ appendvertice(*m_simplex,n);
+ if(EncloseOrigin()) return(true);
+ removevertice(*m_simplex);
+ appendvertice(*m_simplex,-n);
+ if(EncloseOrigin()) return(true);
+ removevertice(*m_simplex);
+ }
+ }
+ break;
+ case 4:
+ {
+ if(Math::abs(det( m_simplex->c[0]->w-m_simplex->c[3]->w,
+ m_simplex->c[1]->w-m_simplex->c[3]->w,
+ m_simplex->c[2]->w-m_simplex->c[3]->w))>0)
+ return(true);
+ }
+ break;
+ }
+ return(false);
+ }
+ /* Internals */
+ void getsupport(const Vector3& d,sSV& sv) const
+ {
+ sv.d = d/d.length();
+ sv.w = m_shape.Support(sv.d);
+ }
+ void removevertice(sSimplex& simplex)
+ {
+ m_free[m_nfree++]=simplex.c[--simplex.rank];
+ }
+ void appendvertice(sSimplex& simplex,const Vector3& v)
+ {
+ simplex.p[simplex.rank]=0;
+ simplex.c[simplex.rank]=m_free[--m_nfree];
+ getsupport(v,*simplex.c[simplex.rank++]);
+ }
+ static real_t det(const Vector3& a,const Vector3& b,const Vector3& c)
+ {
+ return( a.y*b.z*c.x+a.z*b.x*c.y-
+ a.x*b.z*c.y-a.y*b.x*c.z+
+ a.x*b.y*c.z-a.z*b.y*c.x);
+ }
+ static real_t projectorigin( const Vector3& a,
+ const Vector3& b,
+ real_t* w,U& m)
+ {
+ const Vector3 d=b-a;
+ const real_t l=d.length_squared();
+ if(l>GJK_SIMPLEX2_EPS)
+ {
+ const real_t t(l>0?-vec3_dot(a,d)/l:0);
+ if(t>=1) { w[0]=0;w[1]=1;m=2;return(b.length_squared()); }
+ else if(t<=0) { w[0]=1;w[1]=0;m=1;return(a.length_squared()); }
+ else { w[0]=1-(w[1]=t);m=3;return((a+d*t).length_squared()); }
+ }
+ return(-1);
+ }
+ static real_t projectorigin( const Vector3& a,
+ const Vector3& b,
+ const Vector3& c,
+ real_t* w,U& m)
+ {
+ static const U imd3[]={1,2,0};
+ const Vector3* vt[]={&a,&b,&c};
+ const Vector3 dl[]={a-b,b-c,c-a};
+ const Vector3 n=vec3_cross(dl[0],dl[1]);
+ const real_t l=n.length_squared();
+ if(l>GJK_SIMPLEX3_EPS)
+ {
+ real_t mindist=-1;
+ real_t subw[2];
+ U subm;
+ for(U i=0;i<3;++i)
+ {
+ if(vec3_dot(*vt[i],vec3_cross(dl[i],n))>0)
+ {
+ const U j=imd3[i];
+ const real_t subd(projectorigin(*vt[i],*vt[j],subw,subm));
+ if((mindist<0)||(subd<mindist))
+ {
+ mindist = subd;
+ m = static_cast<U>(((subm&1)?1<<i:0)+((subm&2)?1<<j:0));
+ w[i] = subw[0];
+ w[j] = subw[1];
+ w[imd3[j]] = 0;
+ }
+ }
+ }
+ if(mindist<0)
+ {
+ const real_t d=vec3_dot(a,n);
+ const real_t s=Math::sqrt(l);
+ const Vector3 p=n*(d/l);
+ mindist = p.length_squared();
+ m = 7;
+ w[0] = (vec3_cross(dl[1],b-p)).length()/s;
+ w[1] = (vec3_cross(dl[2],c-p)).length()/s;
+ w[2] = 1-(w[0]+w[1]);
+ }
+ return(mindist);
+ }
+ return(-1);
+ }
+ static real_t projectorigin( const Vector3& a,
+ const Vector3& b,
+ const Vector3& c,
+ const Vector3& d,
+ real_t* w,U& m)
+ {
+ static const U imd3[]={1,2,0};
+ const Vector3* vt[]={&a,&b,&c,&d};
+ const Vector3 dl[]={a-d,b-d,c-d};
+ const real_t vl=det(dl[0],dl[1],dl[2]);
+ const bool ng=(vl*vec3_dot(a,vec3_cross(b-c,a-b)))<=0;
+ if(ng&&(Math::abs(vl)>GJK_SIMPLEX4_EPS))
+ {
+ real_t mindist=-1;
+ real_t subw[3];
+ U subm;
+ for(U i=0;i<3;++i)
+ {
+ const U j=imd3[i];
+ const real_t s=vl*vec3_dot(d,vec3_cross(dl[i],dl[j]));
+ if(s>0)
+ {
+ const real_t subd=projectorigin(*vt[i],*vt[j],d,subw,subm);
+ if((mindist<0)||(subd<mindist))
+ {
+ mindist = subd;
+ m = static_cast<U>((subm&1?1<<i:0)+
+ (subm&2?1<<j:0)+
+ (subm&4?8:0));
+ w[i] = subw[0];
+ w[j] = subw[1];
+ w[imd3[j]] = 0;
+ w[3] = subw[2];
+ }
+ }
+ }
+ if(mindist<0)
+ {
+ mindist = 0;
+ m = 15;
+ w[0] = det(c,b,d)/vl;
+ w[1] = det(a,c,d)/vl;
+ w[2] = det(b,a,d)/vl;
+ w[3] = 1-(w[0]+w[1]+w[2]);
+ }
+ return(mindist);
+ }
+ return(-1);
+ }
+};
+
+ // EPA
+ struct EPA
+ {
+ /* Types */
+ typedef GJK::sSV sSV;
+ struct sFace
+ {
+ Vector3 n;
+ real_t d;
+ real_t p;
+ sSV* c[3];
+ sFace* f[3];
+ sFace* l[2];
+ U1 e[3];
+ U1 pass;
+ };
+ struct sList
+ {
+ sFace* root;
+ U count;
+ sList() : root(0),count(0) {}
+ };
+ struct sHorizon
+ {
+ sFace* cf;
+ sFace* ff;
+ U nf;
+ sHorizon() : cf(0),ff(0),nf(0) {}
+ };
+ struct eStatus { enum _ {
+ Valid,
+ Touching,
+ Degenerated,
+ NonConvex,
+ InvalidHull,
+ OutOfFaces,
+ OutOfVertices,
+ AccuraryReached,
+ FallBack,
+ Failed };};
+ /* Fields */
+ eStatus::_ m_status;
+ GJK::sSimplex m_result;
+ Vector3 m_normal;
+ real_t m_depth;
+ sSV m_sv_store[EPA_MAX_VERTICES];
+ sFace m_fc_store[EPA_MAX_FACES];
+ U m_nextsv;
+ sList m_hull;
+ sList m_stock;
+ /* Methods */
+ EPA()
+ {
+ Initialize();
+ }
+
+
+ static inline void bind(sFace* fa,U ea,sFace* fb,U eb)
+ {
+ fa->e[ea]=(U1)eb;fa->f[ea]=fb;
+ fb->e[eb]=(U1)ea;fb->f[eb]=fa;
+ }
+ static inline void append(sList& list,sFace* face)
+ {
+ face->l[0] = 0;
+ face->l[1] = list.root;
+ if(list.root) list.root->l[0]=face;
+ list.root = face;
+ ++list.count;
+ }
+ static inline void remove(sList& list,sFace* face)
+ {
+ if(face->l[1]) face->l[1]->l[0]=face->l[0];
+ if(face->l[0]) face->l[0]->l[1]=face->l[1];
+ if(face==list.root) list.root=face->l[1];
+ --list.count;
+ }
+
+
+ void Initialize()
+ {
+ m_status = eStatus::Failed;
+ m_normal = Vector3(0,0,0);
+ m_depth = 0;
+ m_nextsv = 0;
+ for(U i=0;i<EPA_MAX_FACES;++i)
+ {
+ append(m_stock,&m_fc_store[EPA_MAX_FACES-i-1]);
+ }
+ }
+ eStatus::_ Evaluate(GJK& gjk,const Vector3& guess)
+ {
+ GJK::sSimplex& simplex=*gjk.m_simplex;
+ if((simplex.rank>1)&&gjk.EncloseOrigin())
+ {
+
+ /* Clean up */
+ while(m_hull.root)
+ {
+ sFace* f = m_hull.root;
+ remove(m_hull,f);
+ append(m_stock,f);
+ }
+ m_status = eStatus::Valid;
+ m_nextsv = 0;
+ /* Orient simplex */
+ if(gjk.det( simplex.c[0]->w-simplex.c[3]->w,
+ simplex.c[1]->w-simplex.c[3]->w,
+ simplex.c[2]->w-simplex.c[3]->w)<0)
+ {
+ SWAP(simplex.c[0],simplex.c[1]);
+ SWAP(simplex.p[0],simplex.p[1]);
+ }
+ /* Build initial hull */
+ sFace* tetra[]={newface(simplex.c[0],simplex.c[1],simplex.c[2],true),
+ newface(simplex.c[1],simplex.c[0],simplex.c[3],true),
+ newface(simplex.c[2],simplex.c[1],simplex.c[3],true),
+ newface(simplex.c[0],simplex.c[2],simplex.c[3],true)};
+ if(m_hull.count==4)
+ {
+ sFace* best=findbest();
+ sFace outer=*best;
+ U pass=0;
+ U iterations=0;
+ bind(tetra[0],0,tetra[1],0);
+ bind(tetra[0],1,tetra[2],0);
+ bind(tetra[0],2,tetra[3],0);
+ bind(tetra[1],1,tetra[3],2);
+ bind(tetra[1],2,tetra[2],1);
+ bind(tetra[2],2,tetra[3],1);
+ m_status=eStatus::Valid;
+ for(;iterations<EPA_MAX_ITERATIONS;++iterations)
+ {
+ if(m_nextsv<EPA_MAX_VERTICES)
+ {
+ sHorizon horizon;
+ sSV* w=&m_sv_store[m_nextsv++];
+ bool valid=true;
+ best->pass = (U1)(++pass);
+ gjk.getsupport(best->n,*w);
+ const real_t wdist=vec3_dot(best->n,w->w)-best->d;
+ if(wdist>EPA_ACCURACY)
+ {
+ for(U j=0;(j<3)&&valid;++j)
+ {
+ valid&=expand( pass,w,
+ best->f[j],best->e[j],
+ horizon);
+ }
+ if(valid&&(horizon.nf>=3))
+ {
+ bind(horizon.cf,1,horizon.ff,2);
+ remove(m_hull,best);
+ append(m_stock,best);
+ best=findbest();
+ if(best->p>=outer.p) outer=*best;
+ } else { m_status=eStatus::InvalidHull;break; }
+ } else { m_status=eStatus::AccuraryReached;break; }
+ } else { m_status=eStatus::OutOfVertices;break; }
+ }
+ const Vector3 projection=outer.n*outer.d;
+ m_normal = outer.n;
+ m_depth = outer.d;
+ m_result.rank = 3;
+ m_result.c[0] = outer.c[0];
+ m_result.c[1] = outer.c[1];
+ m_result.c[2] = outer.c[2];
+ m_result.p[0] = vec3_cross( outer.c[1]->w-projection,
+ outer.c[2]->w-projection).length();
+ m_result.p[1] = vec3_cross( outer.c[2]->w-projection,
+ outer.c[0]->w-projection).length();
+ m_result.p[2] = vec3_cross( outer.c[0]->w-projection,
+ outer.c[1]->w-projection).length();
+ const real_t sum=m_result.p[0]+m_result.p[1]+m_result.p[2];
+ m_result.p[0] /= sum;
+ m_result.p[1] /= sum;
+ m_result.p[2] /= sum;
+ return(m_status);
+ }
+ }
+ /* Fallback */
+ m_status = eStatus::FallBack;
+ m_normal = -guess;
+ const real_t nl=m_normal.length();
+ if(nl>0)
+ m_normal = m_normal/nl;
+ else
+ m_normal = Vector3(1,0,0);
+ m_depth = 0;
+ m_result.rank=1;
+ m_result.c[0]=simplex.c[0];
+ m_result.p[0]=1;
+ return(m_status);
+ }
+ sFace* newface(sSV* a,sSV* b,sSV* c,bool forced)
+ {
+ if(m_stock.root)
+ {
+ sFace* face=m_stock.root;
+ remove(m_stock,face);
+ append(m_hull,face);
+ face->pass = 0;
+ face->c[0] = a;
+ face->c[1] = b;
+ face->c[2] = c;
+ face->n = vec3_cross(b->w-a->w,c->w-a->w);
+ const real_t l=face->n.length();
+ const bool v=l>EPA_ACCURACY;
+ face->p = MIN(MIN(
+ vec3_dot(a->w,vec3_cross(face->n,a->w-b->w)),
+ vec3_dot(b->w,vec3_cross(face->n,b->w-c->w))),
+ vec3_dot(c->w,vec3_cross(face->n,c->w-a->w))) /
+ (v?l:1);
+ face->p = face->p>=-EPA_INSIDE_EPS?0:face->p;
+ if(v)
+ {
+ face->d = vec3_dot(a->w,face->n)/l;
+ face->n /= l;
+ if(forced||(face->d>=-EPA_PLANE_EPS))
+ {
+ return(face);
+ } else m_status=eStatus::NonConvex;
+ } else m_status=eStatus::Degenerated;
+ remove(m_hull,face);
+ append(m_stock,face);
+ return(0);
+ }
+ m_status=m_stock.root?eStatus::OutOfVertices:eStatus::OutOfFaces;
+ return(0);
+ }
+ sFace* findbest()
+ {
+ sFace* minf=m_hull.root;
+ real_t mind=minf->d*minf->d;
+ real_t maxp=minf->p;
+ for(sFace* f=minf->l[1];f;f=f->l[1])
+ {
+ const real_t sqd=f->d*f->d;
+ if((f->p>=maxp)&&(sqd<mind))
+ {
+ minf=f;
+ mind=sqd;
+ maxp=f->p;
+ }
+ }
+ return(minf);
+ }
+ bool expand(U pass,sSV* w,sFace* f,U e,sHorizon& horizon)
+ {
+ static const U i1m3[]={1,2,0};
+ static const U i2m3[]={2,0,1};
+ if(f->pass!=pass)
+ {
+ const U e1=i1m3[e];
+ if((vec3_dot(f->n,w->w)-f->d)<-EPA_PLANE_EPS)
+ {
+ sFace* nf=newface(f->c[e1],f->c[e],w,false);
+ if(nf)
+ {
+ bind(nf,0,f,e);
+ if(horizon.cf) bind(horizon.cf,1,nf,2); else horizon.ff=nf;
+ horizon.cf=nf;
+ ++horizon.nf;
+ return(true);
+ }
+ }
+ else
+ {
+ const U e2=i2m3[e];
+ f->pass = (U1)pass;
+ if( expand(pass,w,f->f[e1],f->e[e1],horizon)&&
+ expand(pass,w,f->f[e2],f->e[e2],horizon))
+ {
+ remove(m_hull,f);
+ append(m_stock,f);
+ return(true);
+ }
+ }
+ }
+ return(false);
+ }
+
+ };
+
+ //
+ static void Initialize( const ShapeSW* shape0,const Transform& wtrs0,
+ const ShapeSW* shape1,const Transform& wtrs1,
+ sResults& results,
+ tShape& shape,
+ bool withmargins)
+ {
+ /* Results */
+ results.witnesses[0] =
+ results.witnesses[1] = Vector3(0,0,0);
+ results.status = sResults::Separated;
+ /* Shape */
+ shape.m_shapes[0] = shape0;
+ shape.m_shapes[1] = shape1;
+ shape.transform_A = wtrs0;
+ shape.transform_B = wtrs1;
+
+ }
+
+
+
+//
+// Api
+//
+
+//
+
+//
+bool Distance( const ShapeSW* shape0,
+ const Transform& wtrs0,
+ const ShapeSW* shape1,
+ const Transform& wtrs1,
+ const Vector3& guess,
+ sResults& results)
+{
+ tShape shape;
+ Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,false);
+ GJK gjk;
+ GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,guess);
+ if(gjk_status==GJK::eStatus::Valid)
+ {
+ Vector3 w0=Vector3(0,0,0);
+ Vector3 w1=Vector3(0,0,0);
+ for(U i=0;i<gjk.m_simplex->rank;++i)
+ {
+ const real_t p=gjk.m_simplex->p[i];
+ w0+=shape.Support( gjk.m_simplex->c[i]->d,0)*p;
+ w1+=shape.Support(-gjk.m_simplex->c[i]->d,1)*p;
+ }
+ results.witnesses[0] = wtrs0.xform(w0);
+ results.witnesses[1] = wtrs0.xform(w1);
+ results.normal = w0-w1;
+ results.distance = results.normal.length();
+ results.normal /= results.distance>GJK_MIN_DISTANCE?results.distance:1;
+ return(true);
+ }
+ else
+ {
+ results.status = gjk_status==GJK::eStatus::Inside?
+ sResults::Penetrating :
+ sResults::GJK_Failed ;
+ return(false);
+ }
+}
+
+//
+bool Penetration( const ShapeSW* shape0,
+ const Transform& wtrs0,
+ const ShapeSW* shape1,
+ const Transform& wtrs1,
+ const Vector3& guess,
+ sResults& results
+ )
+{
+ tShape shape;
+ Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,false);
+ GJK gjk;
+ GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,-guess);
+ switch(gjk_status)
+ {
+ case GJK::eStatus::Inside:
+ {
+ EPA epa;
+ EPA::eStatus::_ epa_status=epa.Evaluate(gjk,-guess);
+ if(epa_status!=EPA::eStatus::Failed)
+ {
+ Vector3 w0=Vector3(0,0,0);
+ for(U i=0;i<epa.m_result.rank;++i)
+ {
+ w0+=shape.Support(epa.m_result.c[i]->d,0)*epa.m_result.p[i];
+ }
+ results.status = sResults::Penetrating;
+ results.witnesses[0] = w0;
+ results.witnesses[1] = w0-epa.m_normal*epa.m_depth;
+ results.normal = -epa.m_normal;
+ results.distance = -epa.m_depth;
+ return(true);
+ } else results.status=sResults::EPA_Failed;
+ }
+ break;
+ case GJK::eStatus::Failed:
+ results.status=sResults::GJK_Failed;
+ break;
+ default: {}
+ }
+ return(false);
+}
+
+
+/* Symbols cleanup */
+
+#undef GJK_MAX_ITERATIONS
+#undef GJK_ACCURARY
+#undef GJK_MIN_DISTANCE
+#undef GJK_DUPLICATED_EPS
+#undef GJK_SIMPLEX2_EPS
+#undef GJK_SIMPLEX3_EPS
+#undef GJK_SIMPLEX4_EPS
+
+#undef EPA_MAX_VERTICES
+#undef EPA_MAX_FACES
+#undef EPA_MAX_ITERATIONS
+#undef EPA_ACCURACY
+#undef EPA_FALLBACK
+#undef EPA_PLANE_EPS
+#undef EPA_INSIDE_EPS
+
+
+} // end of namespace
+
+
+
+bool gjk_epa_calculate_penetration(const ShapeSW *p_shape_A, const Transform& p_transform_A, const ShapeSW *p_shape_B, const Transform& p_transform_B, CollisionSolverSW::CallbackResult p_result_callback,void *p_userdata, bool p_swap ) {
+
+ GjkEpa2::sResults res;
+
+ if (GjkEpa2::Penetration(p_shape_A,p_transform_A,p_shape_B,p_transform_B,p_transform_B.origin-p_transform_A.origin,res)) {
+ if (p_result_callback) {
+ if (p_swap)
+ p_result_callback(res.witnesses[1],res.witnesses[0],p_userdata);
+ else
+ p_result_callback(res.witnesses[0],res.witnesses[1],p_userdata);
+ }
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/servers/physics/gjk_epa.h b/servers/physics/gjk_epa.h
new file mode 100644
index 0000000000..0303478f17
--- /dev/null
+++ b/servers/physics/gjk_epa.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* gjk_epa.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 GJK_EPA_H
+#define GJK_EPA_H
+
+#include "shape_sw.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+#include "collision_solver_sw.h"
+
+bool gjk_epa_calculate_penetration(const ShapeSW *p_shape_A, const Transform& p_transform_A, const ShapeSW *p_shape_B, const Transform& p_transform_B, CollisionSolverSW::CallbackResult p_result_callback,void *p_userdata, bool p_swap=false);
+
+#endif
diff --git a/servers/physics/joints_sw.cpp b/servers/physics/joints_sw.cpp
new file mode 100644
index 0000000000..f9e22e1665
--- /dev/null
+++ b/servers/physics/joints_sw.cpp
@@ -0,0 +1,450 @@
+/*************************************************************************/
+/* joints_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "joints_sw.h"
+#include "space_sw.h"
+
+#if 0
+
+//based on chipmunk joint constraints
+
+/* Copyright (c) 2007 Scott Lembcke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+static inline real_t k_scalar(Body2DSW *a,Body2DSW *b,const Vector2& rA, const Vector2& rB, const Vector2& n) {
+
+
+ real_t value=0;
+
+
+ {
+ value+=a->get_inv_mass();
+ real_t rcn = rA.cross(n);
+ value+=a->get_inv_inertia() * rcn * rcn;
+ }
+
+ if (b) {
+
+ value+=b->get_inv_mass();
+ real_t rcn = rB.cross(n);
+ value+=b->get_inv_inertia() * rcn * rcn;
+ }
+
+ return value;
+
+}
+
+
+bool PinJoint2DSW::setup(float p_step) {
+
+ Space2DSW *space = A->get_space();
+ ERR_FAIL_COND_V(!space,false;)
+ rA = A->get_transform().xform(anchor_A);
+ rB = B?B->get_transform().xform(anchor_B):anchor_B;
+
+ Vector2 delta = rB - rA;
+
+ rA-= A->get_transform().get_origin();
+ if (B)
+ rB-=B->get_transform().get_origin();
+
+
+ real_t jdist = delta.length();
+ correct=false;
+ if (jdist==0)
+ return false; // do not correct
+
+ correct=true;
+
+ n = delta / jdist;
+
+ // calculate mass normal
+ mass_normal = 1.0f/k_scalar(A, B, rA, rB, n);
+
+ // calculate bias velocity
+ //real_t maxBias = joint->constraint.maxBias;
+ bias = -(get_bias()==0?space->get_constraint_bias():get_bias())*(1.0/p_step)*(jdist-dist);
+ bias = CLAMP(bias, -get_max_bias(), +get_max_bias());
+
+ // compute max impulse
+ jn_max = get_max_force() * p_step;
+
+ // apply accumulated impulse
+ Vector2 j = n * jn_acc;
+ A->apply_impulse(rA,-j);
+ if (B)
+ B->apply_impulse(rB,j);
+
+ return true;
+}
+
+
+static inline Vector2
+relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB){
+ Vector2 sum = a->get_linear_velocity() -rA.tangent() * a->get_angular_velocity();
+ if (b)
+ return (b->get_linear_velocity() -rB.tangent() * b->get_angular_velocity()) - sum;
+ else
+ return -sum;
+}
+
+static inline real_t
+normal_relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB, Vector2 n){
+ return relative_velocity(a, b, rA, rB).dot(n);
+}
+
+
+void PinJoint2DSW::solve(float p_step){
+
+ if (!correct)
+ return;
+
+ Vector2 ln = n;
+
+ // compute relative velocity
+ real_t vrn = normal_relative_velocity(A,B, rA, rB, ln);
+
+ // compute normal impulse
+ real_t jn = (bias - vrn)*mass_normal;
+ real_t jnOld = jn_acc;
+ jn_acc = CLAMP(jnOld + jn,-jn_max,jn_max); //cpfclamp(jnOld + jn, -joint->jnMax, joint->jnMax);
+ jn = jn_acc - jnOld;
+
+ Vector2 j = jn*ln;
+
+ A->apply_impulse(rA,-j);
+ if (B)
+ B->apply_impulse(rB,j);
+
+}
+
+
+PinJoint2DSW::PinJoint2DSW(const Vector2& p_pos,Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,p_body_b?2:1) {
+
+ A=p_body_a;
+ B=p_body_b;
+ anchor_A = p_body_a->get_inv_transform().xform(p_pos);
+ anchor_B = p_body_b?p_body_b->get_inv_transform().xform(p_pos):p_pos;
+
+ jn_acc=0;
+ dist=0;
+
+ p_body_a->add_constraint(this,0);
+ if (p_body_b)
+ p_body_b->add_constraint(this,1);
+
+}
+
+PinJoint2DSW::~PinJoint2DSW() {
+
+ if (A)
+ A->remove_constraint(this);
+ if (B)
+ B->remove_constraint(this);
+
+}
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+
+static inline void
+k_tensor(Body2DSW *a, Body2DSW *b, Vector2 r1, Vector2 r2, Vector2 *k1, Vector2 *k2)
+{
+ // calculate mass matrix
+ // If I wasn't lazy and wrote a proper matrix class, this wouldn't be so gross...
+ real_t k11, k12, k21, k22;
+ real_t m_sum = a->get_inv_mass() + b->get_inv_mass();
+
+ // start with I*m_sum
+ k11 = m_sum; k12 = 0.0f;
+ k21 = 0.0f; k22 = m_sum;
+
+ // add the influence from r1
+ real_t a_i_inv = a->get_inv_inertia();
+ real_t r1xsq = r1.x * r1.x * a_i_inv;
+ real_t r1ysq = r1.y * r1.y * a_i_inv;
+ real_t r1nxy = -r1.x * r1.y * a_i_inv;
+ k11 += r1ysq; k12 += r1nxy;
+ k21 += r1nxy; k22 += r1xsq;
+
+ // add the influnce from r2
+ real_t b_i_inv = b->get_inv_inertia();
+ real_t r2xsq = r2.x * r2.x * b_i_inv;
+ real_t r2ysq = r2.y * r2.y * b_i_inv;
+ real_t r2nxy = -r2.x * r2.y * b_i_inv;
+ k11 += r2ysq; k12 += r2nxy;
+ k21 += r2nxy; k22 += r2xsq;
+
+ // invert
+ real_t determinant = k11*k22 - k12*k21;
+ ERR_FAIL_COND(determinant== 0.0);
+
+ real_t det_inv = 1.0f/determinant;
+ *k1 = Vector2( k22*det_inv, -k12*det_inv);
+ *k2 = Vector2(-k21*det_inv, k11*det_inv);
+}
+
+static _FORCE_INLINE_ Vector2
+mult_k(const Vector2& vr, const Vector2 &k1, const Vector2 &k2)
+{
+ return Vector2(vr.dot(k1), vr.dot(k2));
+}
+
+bool GrooveJoint2DSW::setup(float p_step) {
+
+
+ // calculate endpoints in worldspace
+ Vector2 ta = A->get_transform().xform(A_groove_1);
+ Vector2 tb = A->get_transform().xform(A_groove_2);
+ Space2DSW *space=A->get_space();
+
+ // calculate axis
+ Vector2 n = -(tb - ta).tangent().normalized();
+ real_t d = ta.dot(n);
+
+ xf_normal = n;
+ rB = B->get_transform().basis_xform(B_anchor);
+
+ // calculate tangential distance along the axis of rB
+ real_t td = (B->get_transform().get_origin() + rB).cross(n);
+ // calculate clamping factor and rB
+ if(td <= ta.cross(n)){
+ clamp = 1.0f;
+ rA = ta - A->get_transform().get_origin();
+ } else if(td >= tb.cross(n)){
+ clamp = -1.0f;
+ rA = tb - A->get_transform().get_origin();
+ } else {
+ clamp = 0.0f;
+ //joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p);
+ rA = ((-n.tangent() * -td) + n*d) - A->get_transform().get_origin();
+ }
+
+ // Calculate mass tensor
+ k_tensor(A, B, rA, rB, &k1, &k2);
+
+ // compute max impulse
+ jn_max = get_max_force() * p_step;
+
+ // calculate bias velocity
+// cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
+// joint->bias = cpvclamp(cpvmult(delta, -joint->constraint.biasCoef*dt_inv), joint->constraint.maxBias);
+
+
+ Vector2 delta = (B->get_transform().get_origin() +rB) - (A->get_transform().get_origin() + rA);
+ gbias=(delta*-(get_bias()==0?space->get_constraint_bias():get_bias())*(1.0/p_step)).clamped(get_max_bias());
+
+ // apply accumulated impulse
+ A->apply_impulse(rA,-jn_acc);
+ B->apply_impulse(rB,jn_acc);
+
+ correct=true;
+ return true;
+}
+
+void GrooveJoint2DSW::solve(float p_step){
+
+
+ // compute impulse
+ Vector2 vr = relative_velocity(A, B, rA,rB);
+
+ Vector2 j = mult_k(gbias-vr, k1, k2);
+ Vector2 jOld = jn_acc;
+ j+=jOld;
+
+ jn_acc = (((clamp * j.cross(xf_normal)) > 0) ? j : xf_normal.project(j)).clamped(jn_max);
+
+ j = jn_acc - jOld;
+
+ A->apply_impulse(rA,-j);
+ B->apply_impulse(rB,j);
+}
+
+
+GrooveJoint2DSW::GrooveJoint2DSW(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,2) {
+
+ A=p_body_a;
+ B=p_body_b;
+
+ A_groove_1 = A->get_inv_transform().xform(p_a_groove1);
+ A_groove_2 = A->get_inv_transform().xform(p_a_groove2);
+ B_anchor=B->get_inv_transform().xform(p_b_anchor);
+ A_groove_normal = -(A_groove_2 - A_groove_1).normalized().tangent();
+
+ A->add_constraint(this,0);
+ B->add_constraint(this,1);
+
+}
+
+GrooveJoint2DSW::~GrooveJoint2DSW() {
+
+ A->remove_constraint(this);
+ B->remove_constraint(this);
+}
+
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+
+bool DampedSpringJoint2DSW::setup(float p_step) {
+
+ rA = A->get_transform().basis_xform(anchor_A);
+ rB = B->get_transform().basis_xform(anchor_B);
+
+ Vector2 delta = (B->get_transform().get_origin() + rB) - (A->get_transform().get_origin() + rA) ;
+ real_t dist = delta.length();
+
+ if (dist)
+ n=delta/dist;
+ else
+ n=Vector2();
+
+ real_t k = k_scalar(A, B, rA, rB, n);
+ n_mass = 1.0f/k;
+
+ target_vrn = 0.0f;
+ v_coef = 1.0f - Math::exp(-damping*(p_step)*k);
+
+ // apply spring force
+ real_t f_spring = (rest_length - dist) * stiffness;
+ Vector2 j = n * f_spring*(p_step);
+
+ A->apply_impulse(rA,-j);
+ B->apply_impulse(rB,j);
+
+
+ return true;
+}
+
+void DampedSpringJoint2DSW::solve(float p_step) {
+
+ // compute relative velocity
+ real_t vrn = normal_relative_velocity(A, B, rA, rB, n) - target_vrn;
+
+ // compute velocity loss from drag
+ // not 100% certain this is derived correctly, though it makes sense
+ real_t v_damp = -vrn*v_coef;
+ target_vrn = vrn + v_damp;
+ Vector2 j=n*v_damp*n_mass;
+
+ A->apply_impulse(rA,-j);
+ B->apply_impulse(rB,j);
+
+}
+
+void DampedSpringJoint2DSW::set_param(Physics2DServer::DampedStringParam p_param, real_t p_value) {
+
+ switch(p_param) {
+
+ case Physics2DServer::DAMPED_STRING_REST_LENGTH: {
+
+ rest_length=p_value;
+ } break;
+ case Physics2DServer::DAMPED_STRING_DAMPING: {
+
+ damping=p_value;
+ } break;
+ case Physics2DServer::DAMPED_STRING_STIFFNESS: {
+
+ stiffness=p_value;
+ } break;
+ }
+
+}
+
+real_t DampedSpringJoint2DSW::get_param(Physics2DServer::DampedStringParam p_param) const{
+
+ switch(p_param) {
+
+ case Physics2DServer::DAMPED_STRING_REST_LENGTH: {
+
+ return rest_length;
+ } break;
+ case Physics2DServer::DAMPED_STRING_DAMPING: {
+
+ return damping;
+ } break;
+ case Physics2DServer::DAMPED_STRING_STIFFNESS: {
+
+ return stiffness;
+ } break;
+ }
+
+ ERR_FAIL_V(0);
+}
+
+
+DampedSpringJoint2DSW::DampedSpringJoint2DSW(const Vector2& p_anchor_a,const Vector2& p_anchor_b, Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,2) {
+
+
+ A=p_body_a;
+ B=p_body_b;
+ anchor_A = A->get_inv_transform().xform(p_anchor_a);
+ anchor_B = B->get_inv_transform().xform(p_anchor_b);
+
+ rest_length=p_anchor_a.distance_to(p_anchor_b);
+ stiffness=20;
+ damping=1.5;
+
+
+ A->add_constraint(this,0);
+ B->add_constraint(this,1);
+
+}
+
+DampedSpringJoint2DSW::~DampedSpringJoint2DSW() {
+
+ A->remove_constraint(this);
+ B->remove_constraint(this);
+
+}
+
+
+#endif
diff --git a/servers/physics/joints_sw.h b/servers/physics/joints_sw.h
new file mode 100644
index 0000000000..c10568fb52
--- /dev/null
+++ b/servers/physics/joints_sw.h
@@ -0,0 +1,176 @@
+/*************************************************************************/
+/* joints_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 JOINTS_SW_H
+#define JOINTS_SW_H
+
+#include "constraint_sw.h"
+#include "body_sw.h"
+
+
+
+class JointSW : public ConstraintSW {
+
+ real_t max_force;
+ real_t bias;
+ real_t max_bias;
+public:
+
+ _FORCE_INLINE_ void set_max_force(real_t p_force) { max_force=p_force; }
+ _FORCE_INLINE_ real_t get_max_force() const { return max_force; }
+
+ _FORCE_INLINE_ void set_bias(real_t p_bias) { bias=p_bias; }
+ _FORCE_INLINE_ real_t get_bias() const { return bias; }
+
+ _FORCE_INLINE_ void set_max_bias(real_t p_bias) { max_bias=p_bias; }
+ _FORCE_INLINE_ real_t get_max_bias() const { return max_bias; }
+
+// virtual PhysicsServer::JointType get_type() const=0;
+ JointSW(BodySW **p_body_ptr=NULL,int p_body_count=0) : ConstraintSW(p_body_ptr,p_body_count) { bias=0; max_force=max_bias=3.40282e+38; };
+
+};
+
+#if 0
+class PinJointSW : public JointSW {
+
+ union {
+ struct {
+ BodySW *A;
+ BodySW *B;
+ };
+
+ BodySW *_arr[2];
+ };
+
+ Vector2 anchor_A;
+ Vector2 anchor_B;
+ real_t dist;
+ real_t jn_acc;
+ real_t jn_max;
+ real_t max_distance;
+ real_t mass_normal;
+ real_t bias;
+
+ Vector2 rA,rB;
+ Vector2 n; //normal
+ bool correct;
+
+
+public:
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_PIN; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+
+ PinJointSW(const Vector2& p_pos,BodySW* p_body_a,BodySW* p_body_b=NULL);
+ ~PinJointSW();
+};
+
+
+class GrooveJointSW : public JointSW {
+
+ union {
+ struct {
+ BodySW *A;
+ BodySW *B;
+ };
+
+ BodySW *_arr[2];
+ };
+
+ Vector2 A_groove_1;
+ Vector2 A_groove_2;
+ Vector2 A_groove_normal;
+ Vector2 B_anchor;
+ Vector2 jn_acc;
+ Vector2 gbias;
+ real_t jn_max;
+ real_t clamp;
+ Vector2 xf_normal;
+ Vector2 rA,rB;
+ Vector2 k1,k2;
+
+
+ bool correct;
+
+public:
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_GROOVE; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+
+ GrooveJointSW(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, BodySW* p_body_a,BodySW* p_body_b);
+ ~GrooveJointSW();
+};
+
+
+class DampedSpringJointSW : public JointSW {
+
+ union {
+ struct {
+ BodySW *A;
+ BodySW *B;
+ };
+
+ BodySW *_arr[2];
+ };
+
+
+ Vector2 anchor_A;
+ Vector2 anchor_B;
+
+ real_t rest_length;
+ real_t damping;
+ real_t stiffness;
+
+ Vector2 rA,rB;
+ Vector2 n;
+ real_t n_mass;
+ real_t target_vrn;
+ real_t v_coef;
+
+public:
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_DAMPED_SPRING; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+ void set_param(PhysicsServer::DampedStringParam p_param, real_t p_value);
+ real_t get_param(PhysicsServer::DampedStringParam p_param) const;
+
+ DampedSpringJointSW(const Vector2& p_anchor_a,const Vector2& p_anchor_b, BodySW* p_body_a,BodySW* p_body_b);
+ ~DampedSpringJointSW();
+};
+#endif
+
+#endif // JOINTS__SW_H
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
new file mode 100644
index 0000000000..072f11aa52
--- /dev/null
+++ b/servers/physics/physics_server_sw.cpp
@@ -0,0 +1,1050 @@
+/*************************************************************************/
+/* physics_server_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_sw.h"
+#include "broad_phase_basic.h"
+#include "broad_phase_octree.h"
+
+RID PhysicsServerSW::shape_create(ShapeType p_shape) {
+
+ ShapeSW *shape=NULL;
+ switch(p_shape) {
+
+ case SHAPE_PLANE: {
+
+ shape=memnew( PlaneShapeSW );
+ } break;
+ case SHAPE_RAY: {
+
+ shape=memnew( RayShapeSW );
+ } break;
+ case SHAPE_SPHERE: {
+
+ shape=memnew( SphereShapeSW);
+ } break;
+ case SHAPE_BOX: {
+
+ shape=memnew( BoxShapeSW);
+ } break;
+ case SHAPE_CAPSULE: {
+
+ shape=memnew( CapsuleShapeSW );
+ } break;
+ case SHAPE_CONVEX_POLYGON: {
+
+ shape=memnew( ConvexPolygonShapeSW );
+ } break;
+ case SHAPE_CONCAVE_POLYGON: {
+
+ shape=memnew( ConcavePolygonShapeSW );
+ } break;
+ case SHAPE_HEIGHTMAP: {
+
+ shape=memnew( HeightMapShapeSW );
+ } break;
+ case SHAPE_CUSTOM: {
+
+ ERR_FAIL_V(RID());
+
+ } break;
+
+ }
+
+ RID id = shape_owner.make_rid(shape);
+ shape->set_self(id);
+
+ return id;
+};
+
+void PhysicsServerSW::shape_set_data(RID p_shape, const Variant& p_data) {
+
+ ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_data(p_data);
+
+
+};
+
+
+void PhysicsServerSW::shape_set_custom_solver_bias(RID p_shape, real_t p_bias) {
+
+ ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_custom_bias(p_bias);
+
+}
+
+
+PhysicsServer::ShapeType PhysicsServerSW::shape_get_type(RID p_shape) const {
+
+ const ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,SHAPE_CUSTOM);
+ return shape->get_type();
+
+};
+
+Variant PhysicsServerSW::shape_get_data(RID p_shape) const {
+
+ const ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,Variant());
+ ERR_FAIL_COND_V(!shape->is_configured(),Variant());
+ return shape->get_data();
+
+};
+
+real_t PhysicsServerSW::shape_get_custom_solver_bias(RID p_shape) const {
+
+ const ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,0);
+ return shape->get_custom_bias();
+
+}
+
+
+RID PhysicsServerSW::space_create() {
+
+ SpaceSW *space = memnew( SpaceSW );
+ RID id = space_owner.make_rid(space);
+ space->set_self(id);
+ RID area_id = area_create();
+ AreaSW *area = area_owner.get(area_id);
+ ERR_FAIL_COND_V(!area,RID());
+ space->set_default_area(area);
+ area->set_space(space);
+ area->set_priority(-1);
+
+ return id;
+};
+
+void PhysicsServerSW::space_set_active(RID p_space,bool p_active) {
+
+ SpaceSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ if (p_active)
+ active_spaces.insert(space);
+ else
+ active_spaces.erase(space);
+}
+
+bool PhysicsServerSW::space_is_active(RID p_space) const {
+
+ const SpaceSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,false);
+
+ return active_spaces.has(space);
+
+}
+
+void PhysicsServerSW::space_set_param(RID p_space,SpaceParameter p_param, real_t p_value) {
+
+ SpaceSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+
+ space->set_param(p_param,p_value);
+
+}
+
+real_t PhysicsServerSW::space_get_param(RID p_space,SpaceParameter p_param) const {
+
+ const SpaceSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,0);
+ return space->get_param(p_param);
+}
+
+PhysicsDirectSpaceState* PhysicsServerSW::space_get_direct_state(RID p_space) {
+
+ SpaceSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,NULL);
+ if (!doing_sync || space->is_locked()) {
+
+ ERR_EXPLAIN("Space state is inaccesible right now, wait for iteration or fixed process notification.");
+ ERR_FAIL_V(NULL);
+
+
+ }
+
+ return space->get_direct_state();
+}
+
+RID PhysicsServerSW::area_create() {
+
+ AreaSW *area = memnew( AreaSW );
+ RID rid = area_owner.make_rid(area);
+ area->set_self(rid);
+ return rid;
+};
+
+void PhysicsServerSW::area_set_space(RID p_area, RID p_space) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ SpaceSW *space=NULL;
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+
+ area->set_space(space);
+
+};
+
+RID PhysicsServerSW::area_get_space(RID p_area) const {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,RID());
+
+ SpaceSW *space = area->get_space();
+ if (!space)
+ return RID();
+ return space->get_self();
+};
+
+void PhysicsServerSW::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) {
+
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_space_override_mode(p_mode);
+}
+
+PhysicsServer::AreaSpaceOverrideMode PhysicsServerSW::area_get_space_override_mode(RID p_area) const {
+
+ const AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,AREA_SPACE_OVERRIDE_DISABLED);
+
+ return area->get_space_override_mode();
+}
+
+
+void PhysicsServerSW::area_add_shape(RID p_area, RID p_shape, const Transform& p_transform) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ area->add_shape(shape,p_transform);
+
+}
+
+void PhysicsServerSW::area_set_shape(RID p_area, int p_shape_idx,RID p_shape) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ ERR_FAIL_COND(!shape->is_configured());
+
+ area->set_shape(p_shape_idx,shape);
+
+}
+void PhysicsServerSW::area_set_shape_transform(RID p_area, int p_shape_idx, const Transform& p_transform) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_shape_transform(p_shape_idx,p_transform);
+}
+
+int PhysicsServerSW::area_get_shape_count(RID p_area) const {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,-1);
+
+ return area->get_shape_count();
+
+}
+RID PhysicsServerSW::area_get_shape(RID p_area, int p_shape_idx) const {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,RID());
+
+ ShapeSW *shape = area->get_shape(p_shape_idx);
+ ERR_FAIL_COND_V(!shape,RID());
+
+ return shape->get_self();
+}
+Transform PhysicsServerSW::area_get_shape_transform(RID p_area, int p_shape_idx) const {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,Transform());
+
+ return area->get_shape_transform(p_shape_idx);
+}
+
+void PhysicsServerSW::area_remove_shape(RID p_area, int p_shape_idx) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->remove_shape(p_shape_idx);
+}
+
+void PhysicsServerSW::area_clear_shapes(RID p_area) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ while(area->get_shape_count())
+ area->remove_shape(0);
+
+}
+
+void PhysicsServerSW::area_attach_object_instance_ID(RID p_area,ObjectID p_ID) {
+
+ if (space_owner.owns(p_area)) {
+ SpaceSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_instance_id(p_ID);
+
+}
+ObjectID PhysicsServerSW::area_get_object_instance_ID(RID p_area) const {
+
+ if (space_owner.owns(p_area)) {
+ SpaceSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,0);
+ return area->get_instance_id();
+
+
+}
+
+
+void PhysicsServerSW::area_set_param(RID p_area,AreaParameter p_param,const Variant& p_value) {
+
+ if (space_owner.owns(p_area)) {
+ SpaceSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_param(p_param,p_value);
+
+};
+
+
+void PhysicsServerSW::area_set_transform(RID p_area, const Transform& p_transform) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_transform(p_transform);
+
+};
+
+Variant PhysicsServerSW::area_get_param(RID p_area,AreaParameter p_param) const {
+
+ if (space_owner.owns(p_area)) {
+ SpaceSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,Variant());
+
+ return area->get_param(p_param);
+};
+
+Transform PhysicsServerSW::area_get_transform(RID p_area) const {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,Transform());
+
+ return area->get_transform();
+};
+
+void PhysicsServerSW::area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method) {
+
+ AreaSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_monitor_callback(p_receiver?p_receiver->get_instance_ID():0,p_method);
+
+
+}
+
+
+/* BODY API */
+
+RID PhysicsServerSW::body_create(BodyMode p_mode,bool p_init_sleeping) {
+
+ BodySW *body = memnew( BodySW );
+ if (p_mode!=BODY_MODE_RIGID)
+ body->set_mode(p_mode);
+ if (p_init_sleeping)
+ body->set_state(BODY_STATE_SLEEPING,p_init_sleeping);
+ RID rid = body_owner.make_rid(body);
+ body->set_self(rid);
+ return rid;
+};
+
+
+void PhysicsServerSW::body_set_space(RID p_body, RID p_space) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ SpaceSW *space=NULL;
+
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+
+ if (body->get_space()==space)
+ return; //pointles
+
+ body->set_space(space);
+
+};
+
+RID PhysicsServerSW::body_get_space(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,RID());
+
+ SpaceSW *space = body->get_space();
+ if (!space)
+ return RID();
+ return space->get_self();
+};
+
+
+void PhysicsServerSW::body_set_mode(RID p_body, BodyMode p_mode) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_mode(p_mode);
+};
+
+PhysicsServer::BodyMode PhysicsServerSW::body_get_mode(RID p_body, BodyMode p_mode) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,BODY_MODE_STATIC);
+
+ return body->get_mode();
+};
+
+void PhysicsServerSW::body_add_shape(RID p_body, RID p_shape, const Transform& p_transform) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ body->add_shape(shape,p_transform);
+
+}
+
+void PhysicsServerSW::body_set_shape(RID p_body, int p_shape_idx,RID p_shape) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ ShapeSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ ERR_FAIL_COND(!shape->is_configured());
+
+ body->set_shape(p_shape_idx,shape);
+
+}
+void PhysicsServerSW::body_set_shape_transform(RID p_body, int p_shape_idx, const Transform& p_transform) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_shape_transform(p_shape_idx,p_transform);
+}
+
+int PhysicsServerSW::body_get_shape_count(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,-1);
+
+ return body->get_shape_count();
+
+}
+RID PhysicsServerSW::body_get_shape(RID p_body, int p_shape_idx) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,RID());
+
+ ShapeSW *shape = body->get_shape(p_shape_idx);
+ ERR_FAIL_COND_V(!shape,RID());
+
+ return shape->get_self();
+}
+
+void PhysicsServerSW::body_set_shape_as_trigger(RID p_body, int p_shape_idx,bool p_enable) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+}
+
+bool PhysicsServerSW::body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const{
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,false);
+
+ // todo ?
+
+ return false;
+}
+
+
+Transform PhysicsServerSW::body_get_shape_transform(RID p_body, int p_shape_idx) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Transform());
+
+ return body->get_shape_transform(p_shape_idx);
+}
+
+void PhysicsServerSW::body_remove_shape(RID p_body, int p_shape_idx) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->remove_shape(p_shape_idx);
+}
+
+void PhysicsServerSW::body_clear_shapes(RID p_body) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ while(body->get_shape_count())
+ body->remove_shape(0);
+
+}
+
+void PhysicsServerSW::body_set_enable_continuous_collision_detection(RID p_body,bool p_enable) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_continuous_collision_detection(p_enable);
+
+}
+
+bool PhysicsServerSW::body_is_continuous_collision_detection_enabled(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,false);
+
+ return body->is_continuous_collision_detection_enabled();
+}
+
+void PhysicsServerSW::body_attach_object_instance_ID(RID p_body,uint32_t p_ID) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_instance_id(p_ID);
+
+};
+
+uint32_t PhysicsServerSW::body_get_object_instance_ID(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return body->get_instance_id();
+};
+
+
+void PhysicsServerSW::body_set_user_flags(RID p_body, uint32_t p_flags) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+};
+
+uint32_t PhysicsServerSW::body_get_user_flags(RID p_body, uint32_t p_flags) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return 0;
+};
+
+void PhysicsServerSW::body_set_param(RID p_body, BodyParameter p_param, float p_value) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_param(p_param,p_value);
+};
+
+float PhysicsServerSW::body_get_param(RID p_body, BodyParameter p_param) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return body->get_param(p_param);
+};
+
+
+void PhysicsServerSW::body_static_simulate_motion(RID p_body,const Transform& p_new_transform) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->simulate_motion(p_new_transform,last_step);
+
+};
+
+void PhysicsServerSW::body_set_state(RID p_body, BodyState p_state, const Variant& p_variant) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_state(p_state,p_variant);
+};
+
+Variant PhysicsServerSW::body_get_state(RID p_body, BodyState p_state) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Variant());
+
+ return body->get_state(p_state);
+};
+
+
+void PhysicsServerSW::body_set_applied_force(RID p_body, const Vector3& p_force) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_applied_force(p_force);
+};
+
+Vector3 PhysicsServerSW::body_get_applied_force(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Vector3());
+ return body->get_applied_force();
+};
+
+void PhysicsServerSW::body_set_applied_torque(RID p_body, const Vector3& p_torque) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_applied_torque(p_torque);
+};
+
+Vector3 PhysicsServerSW::body_get_applied_torque(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Vector3());
+
+ return body->get_applied_torque();
+};
+
+void PhysicsServerSW::body_apply_impulse(RID p_body, const Vector3& p_pos, const Vector3& p_impulse) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_impulse(p_pos,p_impulse);
+};
+
+void PhysicsServerSW::body_set_axis_velocity(RID p_body, const Vector3& p_axis_velocity) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ Vector3 v = body->get_linear_velocity();
+ Vector3 axis = p_axis_velocity.normalized();
+ v-=axis*axis.dot(v);
+ v+=p_axis_velocity;
+ body->set_linear_velocity(v);
+
+};
+
+void PhysicsServerSW::body_add_collision_exception(RID p_body, RID p_body_b) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->add_exception(p_body_b);
+
+};
+
+void PhysicsServerSW::body_remove_collision_exception(RID p_body, RID p_body_b) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->remove_exception(p_body);
+
+};
+
+void PhysicsServerSW::body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ for(int i=0;i<body->get_exceptions().size();i++) {
+ p_exceptions->push_back(body->get_exceptions()[i]);
+ }
+
+};
+
+void PhysicsServerSW::body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+};
+
+float PhysicsServerSW::body_get_contacts_reported_depth_treshold(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+ return 0;
+};
+
+void PhysicsServerSW::body_set_omit_force_integration(RID p_body,bool p_omit) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_omit_force_integration(p_omit);
+};
+
+bool PhysicsServerSW::body_is_omitting_force_integration(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,false);
+ return body->get_omit_force_integration();
+};
+
+void PhysicsServerSW::body_set_max_contacts_reported(RID p_body, int p_contacts) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_max_contacts_reported(p_contacts);
+}
+
+int PhysicsServerSW::body_get_max_contacts_reported(RID p_body) const {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,-1);
+ return body->get_max_contacts_reported();
+}
+
+void PhysicsServerSW::body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata) {
+
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_force_integration_callback(p_receiver?p_receiver->get_instance_ID():ObjectID(0),p_method,p_udata);
+
+}
+
+
+/* JOINT API */
+
+#if 0
+void PhysicsServerSW::joint_set_param(RID p_joint, JointParam p_param, real_t p_value) {
+
+ JointSW *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+
+ switch(p_param) {
+ case JOINT_PARAM_BIAS: joint->set_bias(p_value); break;
+ case JOINT_PARAM_MAX_BIAS: joint->set_max_bias(p_value); break;
+ case JOINT_PARAM_MAX_FORCE: joint->set_max_force(p_value); break;
+ }
+
+
+}
+
+real_t PhysicsServerSW::joint_get_param(RID p_joint,JointParam p_param) const {
+
+ const JointSW *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint,-1);
+
+ switch(p_param) {
+ case JOINT_PARAM_BIAS: return joint->get_bias(); break;
+ case JOINT_PARAM_MAX_BIAS: return joint->get_max_bias(); break;
+ case JOINT_PARAM_MAX_FORCE: return joint->get_max_force(); break;
+ }
+
+ return 0;
+}
+
+
+RID PhysicsServerSW::pin_joint_create(const Vector3& p_pos,RID p_body_a,RID p_body_b) {
+
+ BodySW *A=body_owner.get(p_body_a);
+ ERR_FAIL_COND_V(!A,RID());
+ BodySW *B=NULL;
+ if (body_owner.owns(p_body_b)) {
+ B=body_owner.get(p_body_b);
+ ERR_FAIL_COND_V(!B,RID());
+ }
+
+ JointSW *joint = memnew( PinJointSW(p_pos,A,B) );
+ RID self = joint_owner.make_rid(joint);
+ joint->set_self(self);
+
+ return self;
+}
+
+RID PhysicsServerSW::groove_joint_create(const Vector3& p_a_groove1,const Vector3& p_a_groove2, const Vector3& p_b_anchor, RID p_body_a,RID p_body_b) {
+
+
+ BodySW *A=body_owner.get(p_body_a);
+ ERR_FAIL_COND_V(!A,RID());
+
+ BodySW *B=body_owner.get(p_body_b);
+ ERR_FAIL_COND_V(!B,RID());
+
+ JointSW *joint = memnew( GrooveJointSW(p_a_groove1,p_a_groove2,p_b_anchor,A,B) );
+ RID self = joint_owner.make_rid(joint);
+ joint->set_self(self);
+ return self;
+
+
+}
+
+RID PhysicsServerSW::damped_spring_joint_create(const Vector3& p_anchor_a,const Vector3& p_anchor_b,RID p_body_a,RID p_body_b) {
+
+ BodySW *A=body_owner.get(p_body_a);
+ ERR_FAIL_COND_V(!A,RID());
+
+ BodySW *B=body_owner.get(p_body_b);
+ ERR_FAIL_COND_V(!B,RID());
+
+ JointSW *joint = memnew( DampedSpringJointSW(p_anchor_a,p_anchor_b,A,B) );
+ RID self = joint_owner.make_rid(joint);
+ joint->set_self(self);
+ return self;
+
+}
+
+void PhysicsServerSW::damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value) {
+
+
+ JointSW *j = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!j);
+ ERR_FAIL_COND(j->get_type()!=JOINT_DAMPED_SPRING);
+
+ DampedSpringJointSW *dsj = static_cast<DampedSpringJointSW*>(j);
+ dsj->set_param(p_param,p_value);
+}
+
+real_t PhysicsServerSW::damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const {
+
+ JointSW *j = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!j,0);
+ ERR_FAIL_COND_V(j->get_type()!=JOINT_DAMPED_SPRING,0);
+
+ DampedSpringJointSW *dsj = static_cast<DampedSpringJointSW*>(j);
+ return dsj->get_param(p_param);
+}
+
+PhysicsServer::JointType PhysicsServerSW::joint_get_type(RID p_joint) const {
+
+
+ JointSW *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint,JOINT_PIN);
+
+ return joint->get_type();
+}
+
+#endif
+
+void PhysicsServerSW::free(RID p_rid) {
+
+ if (shape_owner.owns(p_rid)) {
+
+ ShapeSW *shape = shape_owner.get(p_rid);
+
+ while(shape->get_owners().size()) {
+ ShapeOwnerSW *so=shape->get_owners().front()->key();
+ so->remove_shape(shape);
+ }
+
+ shape_owner.free(p_rid);
+ memdelete(shape);
+ } else if (body_owner.owns(p_rid)) {
+
+ BodySW *body = body_owner.get(p_rid);
+
+// if (body->get_state_query())
+// _clear_query(body->get_state_query());
+
+// if (body->get_direct_state_query())
+// _clear_query(body->get_direct_state_query());
+
+ body->set_space(NULL);
+
+
+ while( body->get_shape_count() ) {
+
+ body->remove_shape(0);
+ }
+
+ while (body->get_constraint_map().size()) {
+ RID self = body->get_constraint_map().front()->key()->get_self();
+ ERR_FAIL_COND(!self.is_valid());
+ free(self);
+ }
+
+ body_owner.free(p_rid);
+ memdelete(body);
+
+ } else if (area_owner.owns(p_rid)) {
+
+ AreaSW *area = area_owner.get(p_rid);
+
+// if (area->get_monitor_query())
+// _clear_query(area->get_monitor_query());
+
+ area->set_space(NULL);
+
+ while( area->get_shape_count() ) {
+
+ area->remove_shape(0);
+ }
+
+ area_owner.free(p_rid);
+ memdelete(area);
+ } else if (space_owner.owns(p_rid)) {
+
+ SpaceSW *space = space_owner.get(p_rid);
+
+ while(space->get_objects().size()) {
+ CollisionObjectSW *co = (CollisionObjectSW *)space->get_objects().front()->get();
+ co->set_space(NULL);
+ }
+
+ active_spaces.erase(space);
+ free(space->get_default_area()->get_self());
+ space_owner.free(p_rid);
+ memdelete(space);
+ } else if (joint_owner.owns(p_rid)) {
+
+ JointSW *joint = joint_owner.get(p_rid);
+
+ joint_owner.free(p_rid);
+ memdelete(joint);
+
+ } else {
+
+ ERR_EXPLAIN("Invalid ID");
+ ERR_FAIL();
+ }
+
+
+};
+
+void PhysicsServerSW::set_active(bool p_active) {
+
+ active=p_active;
+};
+
+void PhysicsServerSW::init() {
+
+ doing_sync=true;
+ last_step=0.001;
+ iterations=8;// 8?
+ stepper = memnew( StepSW );
+ direct_state = memnew( PhysicsDirectBodyStateSW );
+};
+
+
+void PhysicsServerSW::step(float p_step) {
+
+
+ if (!active)
+ return;
+
+
+ doing_sync=false;
+
+ last_step=p_step;
+ PhysicsDirectBodyStateSW::singleton->step=p_step;
+
+ for( Set<const SpaceSW*>::Element *E=active_spaces.front();E;E=E->next()) {
+
+ stepper->step((SpaceSW*)E->get(),p_step,iterations);
+ }
+};
+
+void PhysicsServerSW::sync() {
+
+};
+
+void PhysicsServerSW::flush_queries() {
+
+ if (!active)
+ return;
+
+ doing_sync=true;
+ for( Set<const SpaceSW*>::Element *E=active_spaces.front();E;E=E->next()) {
+
+ SpaceSW *space=(SpaceSW *)E->get();
+ space->call_queries();
+ }
+
+};
+
+
+
+void PhysicsServerSW::finish() {
+
+ memdelete(stepper);
+ memdelete(direct_state);
+};
+
+
+PhysicsServerSW::PhysicsServerSW() {
+
+ BroadPhaseSW::create_func=BroadPhaseOctree::_create;
+
+ active=true;
+
+};
+
+PhysicsServerSW::~PhysicsServerSW() {
+
+};
+
+
diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h
new file mode 100644
index 0000000000..2a46ba65fb
--- /dev/null
+++ b/servers/physics/physics_server_sw.h
@@ -0,0 +1,215 @@
+/*************************************************************************/
+/* physics_server_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef PHYSICS_SERVER_SW
+#define PHYSICS_SERVER_SW
+
+
+#include "servers/physics_server.h"
+#include "shape_sw.h"
+#include "space_sw.h"
+#include "step_sw.h"
+#include "joints_sw.h"
+
+
+class PhysicsServerSW : public PhysicsServer {
+
+ OBJ_TYPE( PhysicsServerSW, PhysicsServer );
+
+friend class PhysicsDirectSpaceStateSW;
+ bool active;
+ int iterations;
+ bool doing_sync;
+ real_t last_step;
+
+ StepSW *stepper;
+ Set<const SpaceSW*> active_spaces;
+
+ PhysicsDirectBodyStateSW *direct_state;
+
+ mutable RID_Owner<ShapeSW> shape_owner;
+ mutable RID_Owner<SpaceSW> space_owner;
+ mutable RID_Owner<AreaSW> area_owner;
+ mutable RID_Owner<BodySW> body_owner;
+ mutable RID_Owner<JointSW> joint_owner;
+
+// void _clear_query(QuerySW *p_query);
+public:
+
+ virtual RID shape_create(ShapeType p_shape);
+ virtual void shape_set_data(RID p_shape, const Variant& p_data);
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias);
+
+ virtual ShapeType shape_get_type(RID p_shape) const;
+ virtual Variant shape_get_data(RID p_shape) const;
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const;
+
+ /* SPACE API */
+
+ virtual RID space_create();
+ virtual void space_set_active(RID p_space,bool p_active);
+ virtual bool space_is_active(RID p_space) const;
+
+ virtual void space_set_param(RID p_space,SpaceParameter p_param, real_t p_value);
+ virtual real_t space_get_param(RID p_space,SpaceParameter p_param) const;
+
+ // this function only works on fixed process, errors and returns null otherwise
+ virtual PhysicsDirectSpaceState* space_get_direct_state(RID p_space);
+
+
+ /* AREA API */
+
+ virtual RID area_create();
+
+ virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode);
+ virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const;
+
+ virtual void area_set_space(RID p_area, RID p_space);
+ virtual RID area_get_space(RID p_area) const;
+
+ virtual void area_add_shape(RID p_area, RID p_shape, const Transform& p_transform=Transform());
+ virtual void area_set_shape(RID p_area, int p_shape_idx,RID p_shape);
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform& p_transform);
+
+ virtual int area_get_shape_count(RID p_area) const;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const;
+ virtual Transform area_get_shape_transform(RID p_area, int p_shape_idx) const;
+
+ virtual void area_remove_shape(RID p_area, int p_shape_idx);
+ virtual void area_clear_shapes(RID p_area);
+
+ virtual void area_attach_object_instance_ID(RID p_area,ObjectID p_ID);
+ virtual ObjectID area_get_object_instance_ID(RID p_area) const;
+
+ virtual void area_set_param(RID p_area,AreaParameter p_param,const Variant& p_value);
+ virtual void area_set_transform(RID p_area, const Transform& p_transform);
+
+ virtual Variant area_get_param(RID p_parea,AreaParameter p_param) const;
+ virtual Transform area_get_transform(RID p_area) const;
+
+ virtual void area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method);
+
+
+ /* BODY API */
+
+ // create a body of a given type
+ virtual RID body_create(BodyMode p_mode=BODY_MODE_RIGID,bool p_init_sleeping=false);
+
+ virtual void body_set_space(RID p_body, RID p_space);
+ virtual RID body_get_space(RID p_body) const;
+
+ virtual void body_set_mode(RID p_body, BodyMode p_mode);
+ virtual BodyMode body_get_mode(RID p_body, BodyMode p_mode) const;
+
+ virtual void body_add_shape(RID p_body, RID p_shape, const Transform& p_transform=Transform());
+ virtual void body_set_shape(RID p_body, int p_shape_idx,RID p_shape);
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform& p_transform);
+
+ virtual int body_get_shape_count(RID p_body) const;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const;
+ virtual Transform body_get_shape_transform(RID p_body, int p_shape_idx) const;
+
+ virtual void body_set_shape_as_trigger(RID p_body, int p_shape_idx,bool p_enable);
+ virtual bool body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const;
+
+ virtual void body_remove_shape(RID p_body, int p_shape_idx);
+ virtual void body_clear_shapes(RID p_body);
+
+ virtual void body_attach_object_instance_ID(RID p_body,uint32_t p_ID);
+ virtual uint32_t body_get_object_instance_ID(RID p_body) const;
+
+ virtual void body_set_enable_continuous_collision_detection(RID p_body,bool p_enable);
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const;
+
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags);
+ virtual uint32_t body_get_user_flags(RID p_body, uint32_t p_flags) const;
+
+ virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value);
+ virtual float body_get_param(RID p_body, BodyParameter p_param) const;
+
+ //advanced simulation
+ virtual void body_static_simulate_motion(RID p_body,const Transform& p_new_transform);
+
+ virtual void body_set_state(RID p_body, BodyState p_state, const Variant& p_variant);
+ virtual Variant body_get_state(RID p_body, BodyState p_state) const;
+
+ virtual void body_set_applied_force(RID p_body, const Vector3& p_force);
+ virtual Vector3 body_get_applied_force(RID p_body) const;
+
+ virtual void body_set_applied_torque(RID p_body, const Vector3& p_torque);
+ virtual Vector3 body_get_applied_torque(RID p_body) const;
+
+ virtual void body_apply_impulse(RID p_body, const Vector3& p_pos, const Vector3& p_impulse);
+ virtual void body_set_axis_velocity(RID p_body, const Vector3& p_axis_velocity);
+
+ virtual void body_add_collision_exception(RID p_body, RID p_body_b);
+ virtual void body_remove_collision_exception(RID p_body, RID p_body_b);
+ virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions);
+
+ virtual void body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold);
+ virtual float body_get_contacts_reported_depth_treshold(RID p_body) const;
+
+ virtual void body_set_omit_force_integration(RID p_body,bool p_omit);
+ virtual bool body_is_omitting_force_integration(RID p_body) const;
+
+ virtual void body_set_max_contacts_reported(RID p_body, int p_contacts);
+ virtual int body_get_max_contacts_reported(RID p_body) const;
+
+ virtual void body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata=Variant());
+
+ /* JOINT API */
+#if 0
+ virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value);
+ virtual real_t joint_get_param(RID p_joint,JointParam p_param) const;
+
+ virtual RID pin_joint_create(const Vector3& p_pos,RID p_body_a,RID p_body_b=RID());
+ virtual RID groove_joint_create(const Vector3& p_a_groove1,const Vector3& p_a_groove2, const Vector3& p_b_anchor, RID p_body_a,RID p_body_b);
+ virtual RID damped_spring_joint_create(const Vector3& p_anchor_a,const Vector3& p_anchor_b,RID p_body_a,RID p_body_b=RID());
+ virtual void damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value);
+ virtual real_t damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const;
+
+ virtual JointType joint_get_type(RID p_joint) const;
+#endif
+ /* MISC */
+
+ virtual void free(RID p_rid);
+
+ virtual void set_active(bool p_active);
+ virtual void init();
+ virtual void step(float p_step);
+ virtual void sync();
+ virtual void flush_queries();
+ virtual void finish();
+
+ PhysicsServerSW();
+ ~PhysicsServerSW();
+
+};
+
+#endif
+
diff --git a/servers/physics/shape_sw.cpp b/servers/physics/shape_sw.cpp
new file mode 100644
index 0000000000..70e5c00b92
--- /dev/null
+++ b/servers/physics/shape_sw.cpp
@@ -0,0 +1,1664 @@
+/*************************************************************************/
+/* shape_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "shape_sw.h"
+#include "geometry.h"
+#include "sort.h"
+#include "quick_hull.h"
+#define _POINT_SNAP 0.001953125
+#define _EDGE_IS_VALID_SUPPORT_TRESHOLD 0.0002
+#define _FACE_IS_VALID_SUPPORT_TRESHOLD 0.9998
+
+
+void ShapeSW::configure(const AABB& p_aabb) {
+ aabb=p_aabb;
+ configured=true;
+ for (Map<ShapeOwnerSW*,int>::Element *E=owners.front();E;E=E->next()) {
+ ShapeOwnerSW* co=(ShapeOwnerSW*)E->key();
+ co->_shape_changed();
+ }
+}
+
+
+Vector3 ShapeSW::get_support(const Vector3& p_normal) const {
+
+ Vector3 res;
+ int amnt;
+ get_supports(p_normal,1,&res,amnt);
+ return res;
+}
+
+void ShapeSW::add_owner(ShapeOwnerSW *p_owner) {
+
+ Map<ShapeOwnerSW*,int>::Element *E=owners.find(p_owner);
+ if (E) {
+ E->get()++;
+ } else {
+ owners[p_owner]=1;
+ }
+}
+
+void ShapeSW::remove_owner(ShapeOwnerSW *p_owner){
+
+ Map<ShapeOwnerSW*,int>::Element *E=owners.find(p_owner);
+ ERR_FAIL_COND(!E);
+ E->get()--;
+ if (E->get()==0) {
+ owners.erase(E);
+ }
+
+}
+
+bool ShapeSW::is_owner(ShapeOwnerSW *p_owner) const{
+
+ return owners.has(p_owner);
+
+}
+
+const Map<ShapeOwnerSW*,int>& ShapeSW::get_owners() const{
+ return owners;
+}
+
+
+ShapeSW::ShapeSW() {
+
+ custom_bias=0;
+ configured=false;
+}
+
+
+ShapeSW::~ShapeSW() {
+
+ ERR_FAIL_COND(owners.size());
+}
+
+
+
+Plane PlaneShapeSW::get_plane() const {
+
+ return plane;
+}
+
+void PlaneShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ // gibberish, a plane is infinity
+ r_min=-1e7;
+ r_max=1e7;
+}
+
+Vector3 PlaneShapeSW::get_support(const Vector3& p_normal) const {
+
+ return p_normal*1e15;
+}
+
+
+bool PlaneShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+ bool inters=plane.intersects_segment(p_begin,p_end,&r_result);
+ if(inters)
+ r_normal=plane.normal;
+ return inters;
+}
+
+Vector3 PlaneShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ return Vector3(); //wtf
+}
+
+void PlaneShapeSW::_setup(const Plane& p_plane) {
+
+ plane=p_plane;
+ configure(AABB(Vector3(-1e4,-1e4,-1e4),Vector3(1e4*2,1e4*2,1e4*2)));
+}
+
+void PlaneShapeSW::set_data(const Variant& p_data) {
+
+ _setup(p_data);
+
+}
+
+Variant PlaneShapeSW::get_data() const {
+
+ return plane;
+}
+
+PlaneShapeSW::PlaneShapeSW() {
+
+
+}
+
+//
+
+float RayShapeSW::get_length() const {
+
+ return length;
+}
+
+void RayShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ // don't think this will be even used
+ r_min=0;
+ r_max=1;
+}
+
+Vector3 RayShapeSW::get_support(const Vector3& p_normal) const {
+
+ if (p_normal.z>0)
+ return Vector3(0,0,length);
+ else
+ return Vector3(0,0,0);
+}
+
+void RayShapeSW::get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const {
+
+ if (Math::abs(p_normal.z) < _EDGE_IS_VALID_SUPPORT_TRESHOLD) {
+
+ r_amount=2;
+ r_supports[0]=Vector3(0,0,0);
+ r_supports[1]=Vector3(0,0,length);
+ } if (p_normal.z>0) {
+ r_amount=1;
+ *r_supports=Vector3(0,0,length);
+ } else {
+ r_amount=1;
+ *r_supports=Vector3(0,0,0);
+ }
+}
+
+
+bool RayShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+ return false; //simply not possible
+}
+
+Vector3 RayShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ return Vector3();
+}
+
+void RayShapeSW::_setup(float p_length) {
+
+ length=p_length;
+ configure(AABB(Vector3(0,0,0),Vector3(0.1,0.1,length)));
+}
+
+void RayShapeSW::set_data(const Variant& p_data) {
+
+ _setup(p_data);
+
+}
+
+Variant RayShapeSW::get_data() const {
+
+ return length;
+}
+
+RayShapeSW::RayShapeSW() {
+
+ length=1;
+}
+
+
+
+/********** SPHERE *************/
+
+real_t SphereShapeSW::get_radius() const {
+
+ return radius;
+}
+
+void SphereShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ float d = p_normal.dot( p_transform.origin );
+
+ // figure out scale at point
+ Vector3 local_normal = p_transform.basis.xform_inv(p_normal);
+ float scale = local_normal.length();
+
+ r_min = d - (radius) * scale;
+ r_max = d + (radius) * scale;
+
+}
+
+Vector3 SphereShapeSW::get_support(const Vector3& p_normal) const {
+
+ return p_normal*radius;
+}
+
+void SphereShapeSW::get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const {
+
+ *r_supports=p_normal*radius;
+ r_amount=1;
+}
+
+bool SphereShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+ return Geometry::segment_intersects_sphere(p_begin,p_end,Vector3(),radius,&r_result,&r_normal);
+}
+
+Vector3 SphereShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ float s = 0.4 * p_mass * radius * radius;
+ return Vector3(s,s,s);
+}
+
+void SphereShapeSW::_setup(real_t p_radius) {
+
+
+ radius=p_radius;
+ configure(AABB( Vector3(-radius,-radius,-radius), Vector3(radius*2.0,radius*2.0,radius*2.0)));
+
+}
+
+void SphereShapeSW::set_data(const Variant& p_data) {
+
+ _setup(p_data);
+}
+
+Variant SphereShapeSW::get_data() const {
+
+ return radius;
+}
+
+SphereShapeSW::SphereShapeSW() {
+
+ radius=0;
+}
+
+
+/********** BOX *************/
+
+
+void BoxShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ // no matter the angle, the box is mirrored anyway
+ Vector3 local_normal=p_transform.basis.xform_inv(p_normal);
+
+ float length = local_normal.abs().dot(half_extents);
+ float distance = p_normal.dot( p_transform.origin );
+
+ r_min = distance - length;
+ r_max = distance + length;
+
+
+}
+
+Vector3 BoxShapeSW::get_support(const Vector3& p_normal) const {
+
+
+ Vector3 point(
+ (p_normal.x<0) ? -half_extents.x : half_extents.x,
+ (p_normal.y<0) ? -half_extents.y : half_extents.y,
+ (p_normal.z<0) ? -half_extents.z : half_extents.z
+ );
+
+ return point;
+}
+
+void BoxShapeSW::get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const {
+
+ static const int next[3]={1,2,0};
+ static const int next2[3]={2,0,1};
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis;
+ axis[i]=1.0;
+ float dot = p_normal.dot( axis );
+ if ( Math::abs( dot ) > _FACE_IS_VALID_SUPPORT_TRESHOLD ) {
+
+ //Vector3 axis_b;
+
+ bool neg = dot<0;
+ r_amount = 4;
+
+ Vector3 point;
+ point[i]=half_extents[i];
+
+ int i_n=next[i];
+ int i_n2=next2[i];
+
+ static const float sign[4][2]={
+
+ {-1.0, 1.0},
+ { 1.0, 1.0},
+ { 1.0,-1.0},
+ {-1.0,-1.0},
+ };
+
+ for (int j=0;j<4;j++) {
+
+ point[i_n]=sign[j][0]*half_extents[i_n];
+ point[i_n2]=sign[j][1]*half_extents[i_n2];
+ r_supports[j]=neg?-point:point;
+
+ }
+
+ if (neg) {
+ SWAP( r_supports[1], r_supports[2] );
+ SWAP( r_supports[0], r_supports[3] );
+ }
+
+ return;
+ }
+
+ r_amount=0;
+
+ }
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 axis;
+ axis[i]=1.0;
+
+ if (Math::abs(p_normal.dot(axis))<_EDGE_IS_VALID_SUPPORT_TRESHOLD) {
+
+ r_amount= 2;
+
+ int i_n=next[i];
+ int i_n2=next2[i];
+
+ Vector3 point=half_extents;
+
+ if (p_normal[i_n]<0) {
+ point[i_n]=-point[i_n];
+ }
+ if (p_normal[i_n2]<0) {
+ point[i_n2]=-point[i_n2];
+ }
+
+ r_supports[0] = point;
+ point[i]=-point[i];
+ r_supports[1] = point;
+ return;
+ }
+ }
+ /* USE POINT */
+
+ Vector3 point(
+ (p_normal.x<0) ? -half_extents.x : half_extents.x,
+ (p_normal.y<0) ? -half_extents.y : half_extents.y,
+ (p_normal.z<0) ? -half_extents.z : half_extents.z
+ );
+
+ r_amount=1;
+ r_supports[0]=point;
+}
+
+bool BoxShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+ AABB aabb(-half_extents,half_extents*2.0);
+
+ return aabb.intersects_segment(p_begin,p_end,&r_result,&r_normal);
+
+}
+
+Vector3 BoxShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ float lx=half_extents.x;
+ float ly=half_extents.y;
+ float lz=half_extents.z;
+
+ return Vector3( (p_mass/3.0) * (ly*ly + lz*lz), (p_mass/3.0) * (lx*lx + lz*lz), (p_mass/3.0) * (lx*lx + ly*ly) );
+
+}
+
+void BoxShapeSW::_setup(const Vector3& p_half_extents) {
+
+ half_extents=p_half_extents.abs();
+
+ configure(AABB(-half_extents,half_extents*2));
+
+
+}
+
+void BoxShapeSW::set_data(const Variant& p_data) {
+
+
+ _setup(p_data);
+}
+
+Variant BoxShapeSW::get_data() const {
+
+ return half_extents;
+}
+
+BoxShapeSW::BoxShapeSW() {
+
+
+}
+
+
+/********** CAPSULE *************/
+
+
+void CapsuleShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ Vector3 n=p_transform.basis.xform_inv(p_normal).normalized();
+ float h = (n.z > 0) ? height : -height;
+
+ n *= radius;
+ n.z += h * 0.5;
+
+ r_max=p_normal.dot(p_transform.xform(n));
+ r_min=p_normal.dot(p_transform.xform(-n));
+ return;
+
+ n = p_transform.basis.xform(n);
+
+ float distance = p_normal.dot( p_transform.origin );
+ float length = Math::abs(p_normal.dot(n));
+ r_min = distance - length;
+ r_max = distance + length;
+
+ ERR_FAIL_COND( r_max < r_min );
+
+}
+
+Vector3 CapsuleShapeSW::get_support(const Vector3& p_normal) const {
+
+ Vector3 n=p_normal;
+
+ float h = (n.z > 0) ? height : -height;
+
+ n*=radius;
+ n.z += h*0.5;
+ return n;
+}
+
+void CapsuleShapeSW::get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const {
+
+
+ Vector3 n=p_normal;
+
+ float d = n.z;
+
+ if (Math::abs( d )<_EDGE_IS_VALID_SUPPORT_TRESHOLD ) {
+
+ // make it flat
+ n.z=0.0;
+ n.normalize();
+ n*=radius;
+
+ r_amount=2;
+ r_supports[0]=n;
+ r_supports[0].z+=height*0.5;
+ r_supports[1]=n;
+ r_supports[1].z-=height*0.5;
+
+ } else {
+
+ float h = (d > 0) ? height : -height;
+
+ n*=radius;
+ n.z += h*0.5;
+ r_amount=1;
+ *r_supports=n;
+
+ }
+
+}
+
+
+bool CapsuleShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+ Vector3 norm=(p_end-p_begin).normalized();
+ float min_d=1e20;
+
+
+ Vector3 res,n;
+ bool collision=false;
+
+ Vector3 auxres,auxn;
+ bool collided;
+
+ // test against cylinder and spheres :-|
+
+ collided = Geometry::segment_intersects_cylinder(p_begin,p_end,height,radius,&auxres,&auxn);
+
+ if (collided) {
+ float d=norm.dot(auxres);
+ if (d<min_d) {
+ min_d=d;
+ res=auxres;
+ n=auxn;
+ collision=true;
+ }
+ }
+
+ collided = Geometry::segment_intersects_sphere(p_begin,p_end,Vector3(0,0,height*0.5),radius,&auxres,&auxn);
+
+ if (collided) {
+ float d=norm.dot(auxres);
+ if (d<min_d) {
+ min_d=d;
+ res=auxres;
+ n=auxn;
+ collision=true;
+ }
+ }
+
+ collided = Geometry::segment_intersects_sphere(p_begin,p_end,Vector3(0,0,height*-0.5),radius,&auxres,&auxn);
+
+ if (collided) {
+ float d=norm.dot(auxres);
+
+ if (d<min_d) {
+ min_d=d;
+ res=auxres;
+ n=auxn;
+ collision=true;
+ }
+ }
+
+ if (collision) {
+
+ r_result=res;
+ r_normal=n;
+ }
+ return collision;
+}
+
+Vector3 CapsuleShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ // use crappy AABB approximation
+ Vector3 extents=get_aabb().size*0.5;
+
+ return Vector3(
+ (p_mass/3.0) * (extents.y*extents.y + extents.z*extents.z),
+ (p_mass/3.0) * (extents.x*extents.x + extents.z*extents.z),
+ (p_mass/3.0) * (extents.y*extents.y + extents.y*extents.y)
+ );
+
+}
+
+
+
+
+void CapsuleShapeSW::_setup(real_t p_height,real_t p_radius) {
+
+ height=p_height;
+ radius=p_radius;
+ configure(AABB(Vector3(-radius,-radius,-height*0.5-radius),Vector3(radius*2,radius*2,height+radius*2.0)));
+
+}
+
+void CapsuleShapeSW::set_data(const Variant& p_data) {
+
+ Dictionary d = p_data;
+ ERR_FAIL_COND(!d.has("radius"));
+ ERR_FAIL_COND(!d.has("height"));
+ _setup(d["height"],d["radius"]);
+
+}
+
+Variant CapsuleShapeSW::get_data() const {
+
+ Dictionary d;
+ d["radius"]=radius;
+ d["height"]=height;
+ return d;
+
+}
+
+
+CapsuleShapeSW::CapsuleShapeSW() {
+
+ height=radius=0;
+
+}
+
+/********** CONVEX POLYGON *************/
+
+
+void ConvexPolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+
+ int vertex_count=mesh.vertices.size();
+ if (vertex_count==0)
+ return;
+
+ const Vector3 *vrts=&mesh.vertices[0];
+
+ for (int i=0;i<vertex_count;i++) {
+
+ float d=p_normal.dot( p_transform.xform( vrts[i] ) );
+
+ if (i==0 || d > r_max)
+ r_max=d;
+ if (i==0 || d < r_min)
+ r_min=d;
+ }
+}
+
+Vector3 ConvexPolygonShapeSW::get_support(const Vector3& p_normal) const {
+
+ Vector3 n=p_normal;
+
+ int vert_support_idx=-1;
+ float support_max;
+
+ int vertex_count=mesh.vertices.size();
+ if (vertex_count==0)
+ return Vector3();
+
+ const Vector3 *vrts=&mesh.vertices[0];
+
+ for (int i=0;i<vertex_count;i++) {
+
+ float d=n.dot(vrts[i]);
+
+ if (i==0 || d > support_max) {
+ support_max=d;
+ vert_support_idx=i;
+ }
+ }
+
+ return vrts[vert_support_idx];
+
+}
+
+
+
+void ConvexPolygonShapeSW::get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const {
+
+ const Geometry::MeshData::Face *faces = mesh.faces.ptr();
+ int fc = mesh.faces.size();
+
+ const Geometry::MeshData::Edge *edges = mesh.edges.ptr();
+ int ec = mesh.edges.size();
+
+ const Vector3 *vertices = mesh.vertices.ptr();
+ int vc = mesh.vertices.size();
+
+ //find vertex first
+ real_t max;
+ int vtx;
+
+ for (int i=0;i<vc;i++) {
+
+ float d=p_normal.dot(vertices[i]);
+
+ if (i==0 || d > max) {
+ max=d;
+ vtx=i;
+ }
+ }
+
+
+ for(int i=0;i<fc;i++) {
+
+ if (faces[i].plane.normal.dot(p_normal)>_FACE_IS_VALID_SUPPORT_TRESHOLD) {
+
+ int ic = faces[i].indices.size();
+ const int *ind=faces[i].indices.ptr();
+
+ bool valid=false;
+ for(int j=0;j<ic;j++) {
+ if (ind[j]==vtx) {
+ valid=true;
+ break;
+ }
+ }
+
+ if (!valid)
+ continue;
+
+ int m = MIN(p_max,ic);
+ for(int j=0;j<m;j++) {
+
+ r_supports[j]=vertices[ind[j]];
+ }
+ r_amount=m;
+ return;
+ }
+ }
+
+ for(int i=0;i<ec;i++) {
+
+
+ float dot=(vertices[edges[i].a]-vertices[edges[i].b]).normalized().dot(p_normal);
+ dot=ABS(dot);
+ if (dot < _EDGE_IS_VALID_SUPPORT_TRESHOLD && (edges[i].a==vtx || edges[i].b==vtx)) {
+
+ r_amount=2;
+ r_supports[0]=vertices[edges[i].a];
+ r_supports[1]=vertices[edges[i].b];
+ return;
+ }
+ }
+
+
+ r_supports[0]=vertices[vtx];
+ r_amount=1;
+}
+
+bool ConvexPolygonShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+
+
+ const Geometry::MeshData::Face *faces = mesh.faces.ptr();
+ int fc = mesh.faces.size();
+
+ const Vector3 *vertices = mesh.vertices.ptr();
+ int vc = mesh.vertices.size();
+
+ Vector3 n = p_end-p_begin;
+ float min = 1e20;
+ bool col=false;
+
+ for(int i=0;i<fc;i++) {
+
+ if (faces[i].plane.normal.dot(n) > 0)
+ continue; //opposing face
+
+ int ic = faces[i].indices.size();
+ const int *ind=faces[i].indices.ptr();
+
+ for(int j=1;j<ic-1;j++) {
+
+ Face3 f(vertices[ind[0]],vertices[ind[i]],vertices[ind[i+1]]);
+ Vector3 result;
+ if (f.intersects_segment(p_begin,p_end,&result)) {
+ float d = n.dot(result);
+ if (d<min) {
+ min=d;
+ r_result=result;
+ r_normal=faces[i].plane.normal;
+ col=true;
+ }
+
+ break;
+ }
+
+ }
+ }
+
+ return col;
+
+}
+
+Vector3 ConvexPolygonShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ // use crappy AABB approximation
+ Vector3 extents=get_aabb().size*0.5;
+
+ return Vector3(
+ (p_mass/3.0) * (extents.y*extents.y + extents.z*extents.z),
+ (p_mass/3.0) * (extents.x*extents.x + extents.z*extents.z),
+ (p_mass/3.0) * (extents.y*extents.y + extents.y*extents.y)
+ );
+
+}
+
+void ConvexPolygonShapeSW::_setup(const Vector<Vector3>& p_vertices) {
+
+ Error err = QuickHull::build(p_vertices,mesh);
+ AABB _aabb;
+
+ for(int i=0;i<mesh.vertices.size();i++) {
+
+ if (i==0)
+ _aabb.pos=mesh.vertices[i];
+ else
+ _aabb.expand_to(mesh.vertices[i]);
+ }
+
+ configure(_aabb);
+
+
+}
+
+void ConvexPolygonShapeSW::set_data(const Variant& p_data) {
+
+ _setup(p_data);
+
+}
+
+Variant ConvexPolygonShapeSW::get_data() const {
+
+ return mesh.vertices;
+}
+
+
+ConvexPolygonShapeSW::ConvexPolygonShapeSW() {
+
+
+}
+
+
+/********** FACE POLYGON *************/
+
+
+void FaceShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ for (int i=0;i<3;i++) {
+
+ Vector3 v=p_transform.xform(vertex[i]);
+ float d=p_normal.dot(v);
+
+ if (i==0 || d > r_max)
+ r_max=d;
+
+ if (i==0 || d < r_min)
+ r_min=d;
+ }
+}
+
+Vector3 FaceShapeSW::get_support(const Vector3& p_normal) const {
+
+
+ Vector3 n=p_normal;
+
+ int vert_support_idx=-1;
+ float support_max;
+
+ for (int i=0;i<3;i++) {
+
+ //float d=n.dot(vertex[i]);
+ float d=p_normal.dot(vertex[i]);
+
+ if (i==0 || d > support_max) {
+ support_max=d;
+ vert_support_idx=i;
+ }
+ }
+
+ return vertex[vert_support_idx];
+}
+
+void FaceShapeSW::get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const {
+
+ Vector3 n=p_normal;
+
+ /** TEST FACE AS SUPPORT **/
+ if (normal.dot(n) > _FACE_IS_VALID_SUPPORT_TRESHOLD) {
+
+ r_amount=3;
+ for (int i=0;i<3;i++) {
+
+ r_supports[i]=vertex[i];
+ }
+ return;
+
+ }
+
+ /** FIND SUPPORT VERTEX **/
+
+ int vert_support_idx=-1;
+ float support_max;
+
+ for (int i=0;i<3;i++) {
+
+ float d=n.dot(vertex[i]);
+
+ if (i==0 || d > support_max) {
+ support_max=d;
+ vert_support_idx=i;
+ }
+ }
+
+ /** TEST EDGES AS SUPPORT **/
+
+ for (int i=0;i<3;i++) {
+
+ int nx=(i+1)%3;
+ //if (i!=vert_support_idx && nx!=vert_support_idx)
+ // continue;
+
+ // check if edge is valid as a support
+ float dot=(vertex[i]-vertex[nx]).normalized().dot(n);
+ dot=ABS(dot);
+ if (dot < _EDGE_IS_VALID_SUPPORT_TRESHOLD) {
+
+ r_amount=2;
+ r_supports[0]=vertex[i];
+ r_supports[1]=vertex[nx];
+ return;
+ }
+ }
+
+ r_amount=1;
+ r_supports[0]=vertex[vert_support_idx];
+}
+
+bool FaceShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+
+ bool c=Geometry::segment_intersects_triangle(p_begin,p_end,vertex[0],vertex[1],vertex[2],&r_result);
+ if (c)
+ r_normal=Plane(vertex[0],vertex[1],vertex[2]).normal;
+
+ return c;
+}
+
+Vector3 FaceShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ return Vector3(); // Sorry, but i don't think anyone cares, FaceShape!
+
+}
+
+FaceShapeSW::FaceShapeSW() {
+
+ configure(AABB());
+
+}
+
+
+
+DVector<Vector3> ConcavePolygonShapeSW::get_faces() const {
+
+
+ DVector<Vector3> rfaces;
+ rfaces.resize(faces.size()*3);
+
+ for(int i=0;i<faces.size();i++) {
+
+ Face f=faces.get(i);
+
+ for(int j=0;j<3;j++) {
+
+ rfaces.set(i*3+j, vertices.get( f.indices[j] ) );
+ }
+ }
+
+ return rfaces;
+}
+
+void ConcavePolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ int count=vertices.size();
+ DVector<Vector3>::Read r=vertices.read();
+ const Vector3 *vptr=r.ptr();
+
+ for (int i=0;i<count;i++) {
+
+ float d=p_normal.dot( p_transform.xform( vptr[i] ) );
+
+ if (i==0 || d > r_max)
+ r_max=d;
+ if (i==0 || d < r_min)
+ r_min=d;
+
+ }
+}
+
+Vector3 ConcavePolygonShapeSW::get_support(const Vector3& p_normal) const {
+
+
+ int count=vertices.size();
+ DVector<Vector3>::Read r=vertices.read();
+ const Vector3 *vptr=r.ptr();
+
+ Vector3 n=p_normal;
+
+ int vert_support_idx=-1;
+ float support_max;
+
+ for (int i=0;i<count;i++) {
+
+ float d=n.dot(vptr[i]);
+
+ if (i==0 || d > support_max) {
+ support_max=d;
+ vert_support_idx=i;
+ }
+ }
+
+
+ return vptr[vert_support_idx];
+
+}
+
+void ConcavePolygonShapeSW::_cull_segment(int p_idx,_SegmentCullParams *p_params) const {
+
+ const BVH *bvh=&p_params->bvh[p_idx];
+
+
+ //if (p_params->dir.dot(bvh->aabb.get_support(-p_params->dir))>p_params->min_d)
+ // return; //test against whole AABB, which isn't very costly
+
+
+ //printf("addr: %p\n",bvh);
+ if (!bvh->aabb.intersects_segment(p_params->from,p_params->to)) {
+
+ return;
+ }
+
+
+ if (bvh->face_index>=0) {
+
+
+ Vector3 res;
+ Vector3 vertices[3]={
+ p_params->vertices[ p_params->faces[ bvh->face_index ].indices[0] ],
+ p_params->vertices[ p_params->faces[ bvh->face_index ].indices[1] ],
+ p_params->vertices[ p_params->faces[ bvh->face_index ].indices[2] ]
+ };
+
+ if (Geometry::segment_intersects_triangle(
+ p_params->from,
+ p_params->to,
+ vertices[0],
+ vertices[1],
+ vertices[2],
+ &res)) {
+
+
+ float d=p_params->normal.dot(res) - p_params->normal.dot(p_params->from);
+ //TODO, seems segmen/triangle intersection is broken :(
+ if (d>0 && d<p_params->min_d) {
+
+ p_params->min_d=d;
+ p_params->result=res;
+ p_params->normal=Plane(vertices[0],vertices[1],vertices[2]).normal;
+ p_params->collisions++;
+ }
+
+ }
+
+
+
+ } else {
+
+ if (bvh->left>=0)
+ _cull_segment(bvh->left,p_params);
+ if (bvh->right>=0)
+ _cull_segment(bvh->right,p_params);
+
+
+ }
+}
+
+bool ConcavePolygonShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const {
+
+ // unlock data
+ DVector<Face>::Read fr=faces.read();
+ DVector<Vector3>::Read vr=vertices.read();
+ DVector<BVH>::Read br=bvh.read();
+
+
+ _SegmentCullParams params;
+ params.from=p_begin;
+ params.to=p_end;
+ params.collisions=0;
+ params.normal=(p_end-p_begin).normalized();
+
+ params.faces=fr.ptr();
+ params.vertices=vr.ptr();
+ params.bvh=br.ptr();
+
+ params.min_d=1e20;
+ // cull
+ _cull_segment(0,&params);
+
+ if (params.collisions>0) {
+
+
+ r_result=params.result;
+ r_normal=params.normal;
+ return true;
+ } else {
+
+ return false;
+ }
+}
+
+void ConcavePolygonShapeSW::_cull(int p_idx,_CullParams *p_params) const {
+
+ const BVH* bvh=&p_params->bvh[p_idx];
+
+ if (!p_params->aabb.intersects( bvh->aabb ))
+ return;
+
+ if (bvh->face_index>=0) {
+
+ const Face *f=&p_params->faces[ bvh->face_index ];
+ FaceShapeSW *face=p_params->face;
+ face->normal=f->normal;
+ face->vertex[0]=p_params->vertices[f->indices[0]];
+ face->vertex[1]=p_params->vertices[f->indices[1]];
+ face->vertex[2]=p_params->vertices[f->indices[2]];
+ p_params->callback(p_params->userdata,face);
+
+ } else {
+
+ if (bvh->left>=0) {
+
+ _cull(bvh->left,p_params);
+
+ }
+
+ if (bvh->right>=0) {
+
+ _cull(bvh->right,p_params);
+ }
+
+ }
+}
+
+void ConcavePolygonShapeSW::cull(const AABB& p_local_aabb,Callback p_callback,void* p_userdata) const {
+
+ // make matrix local to concave
+
+ AABB local_aabb=p_local_aabb;
+
+ // unlock data
+ DVector<Face>::Read fr=faces.read();
+ DVector<Vector3>::Read vr=vertices.read();
+ DVector<BVH>::Read br=bvh.read();
+
+ FaceShapeSW face; // use this to send in the callback
+
+ _CullParams params;
+ params.aabb=local_aabb;
+ params.face=&face;
+ params.faces=fr.ptr();
+ params.vertices=vr.ptr();
+ params.bvh=br.ptr();
+ params.callback=p_callback;
+ params.userdata=p_userdata;
+
+ // cull
+ _cull(0,&params);
+
+}
+
+Vector3 ConcavePolygonShapeSW::get_moment_of_inertia(float p_mass) const {
+
+ // use crappy AABB approximation
+ Vector3 extents=get_aabb().size*0.5;
+
+ return Vector3(
+ (p_mass/3.0) * (extents.y*extents.y + extents.z*extents.z),
+ (p_mass/3.0) * (extents.x*extents.x + extents.z*extents.z),
+ (p_mass/3.0) * (extents.y*extents.y + extents.y*extents.y)
+ );
+}
+
+
+struct _VolumeSW_BVH_Element {
+
+ AABB aabb;
+ Vector3 center;
+ int face_index;
+};
+
+struct _VolumeSW_BVH_CompareX {
+
+ _FORCE_INLINE_ bool operator ()(const _VolumeSW_BVH_Element& a, const _VolumeSW_BVH_Element& b) const {
+
+ return a.center.x<b.center.x;
+ }
+};
+
+
+struct _VolumeSW_BVH_CompareY {
+
+ _FORCE_INLINE_ bool operator ()(const _VolumeSW_BVH_Element& a, const _VolumeSW_BVH_Element& b) const {
+
+ return a.center.y<b.center.y;
+ }
+};
+
+struct _VolumeSW_BVH_CompareZ {
+
+ _FORCE_INLINE_ bool operator ()(const _VolumeSW_BVH_Element& a, const _VolumeSW_BVH_Element& b) const {
+
+ return a.center.z<b.center.z;
+ }
+};
+
+struct _VolumeSW_BVH {
+
+ AABB aabb;
+ _VolumeSW_BVH *left;
+ _VolumeSW_BVH *right;
+
+ int face_index;
+};
+
+
+_VolumeSW_BVH* _volume_sw_build_bvh(_VolumeSW_BVH_Element *p_elements,int p_size,int &count) {
+
+ _VolumeSW_BVH* bvh = memnew( _VolumeSW_BVH );
+
+ if (p_size==1) {
+ //leaf
+ bvh->aabb=p_elements[0].aabb;
+ bvh->left=NULL;
+ bvh->right=NULL;
+ bvh->face_index=p_elements->face_index;
+ count++;
+ return bvh;
+ } else {
+
+ bvh->face_index=-1;
+ }
+
+ AABB aabb;
+ for(int i=0;i<p_size;i++) {
+
+ if (i==0)
+ aabb=p_elements[i].aabb;
+ else
+ aabb.merge_with(p_elements[i].aabb);
+ }
+ bvh->aabb=aabb;
+ switch(aabb.get_longest_axis_index()) {
+
+ case 0: {
+
+ SortArray<_VolumeSW_BVH_Element,_VolumeSW_BVH_CompareX> sort_x;
+ sort_x.sort(p_elements,p_size);
+
+ } break;
+ case 1: {
+
+ SortArray<_VolumeSW_BVH_Element,_VolumeSW_BVH_CompareY> sort_y;
+ sort_y.sort(p_elements,p_size);
+ } break;
+ case 2: {
+
+ SortArray<_VolumeSW_BVH_Element,_VolumeSW_BVH_CompareZ> sort_z;
+ sort_z.sort(p_elements,p_size);
+ } break;
+ }
+
+ int split=p_size/2;
+ bvh->left=_volume_sw_build_bvh(p_elements,split,count);
+ bvh->right=_volume_sw_build_bvh(&p_elements[split],p_size-split,count);
+
+// printf("branch at %p - %i: %i\n",bvh,count,bvh->face_index);
+ count++;
+ return bvh;
+}
+
+
+void ConcavePolygonShapeSW::_fill_bvh(_VolumeSW_BVH* p_bvh_tree,BVH* p_bvh_array,int& p_idx) {
+
+ int idx=p_idx;
+
+
+ p_bvh_array[idx].aabb=p_bvh_tree->aabb;
+ p_bvh_array[idx].face_index=p_bvh_tree->face_index;
+// printf("%p - %i: %i(%p) -- %p:%p\n",%p_bvh_array[idx],p_idx,p_bvh_array[i]->face_index,&p_bvh_tree->face_index,p_bvh_tree->left,p_bvh_tree->right);
+
+
+ if (p_bvh_tree->left) {
+ p_bvh_array[idx].left=++p_idx;
+ _fill_bvh(p_bvh_tree->left,p_bvh_array,p_idx);
+
+ } else {
+
+ p_bvh_array[p_idx].left=-1;
+ }
+
+ if (p_bvh_tree->right) {
+ p_bvh_array[idx].right=++p_idx;
+ _fill_bvh(p_bvh_tree->right,p_bvh_array,p_idx);
+
+ } else {
+
+ p_bvh_array[p_idx].right=-1;
+ }
+
+ memdelete(p_bvh_tree);
+
+}
+
+void ConcavePolygonShapeSW::_setup(DVector<Vector3> p_faces) {
+
+ int src_face_count=p_faces.size();
+ ERR_FAIL_COND(src_face_count%3);
+ src_face_count/=3;
+
+ DVector<Vector3>::Read r = p_faces.read();
+ const Vector3 * facesr= r.ptr();
+
+#if 0
+ Map<Vector3,int> point_map;
+ List<Face> face_list;
+
+
+ for(int i=0;i<src_face_count;i++) {
+
+ Face3 faceaux;
+
+ for(int j=0;j<3;j++) {
+
+ faceaux.vertex[j]=facesr[i*3+j].snapped(_POINT_SNAP);
+ //faceaux.vertex[j]=facesr[i*3+j];//facesr[i*3+j].snapped(_POINT_SNAP);
+ }
+
+ ERR_CONTINUE( faceaux.is_degenerate() );
+
+ Face face;
+
+ for(int j=0;j<3;j++) {
+
+
+ Map<Vector3,int>::Element *E=point_map.find(faceaux.vertex[j]);
+ if (E) {
+
+ face.indices[j]=E->value();
+ } else {
+
+ face.indices[j]=point_map.size();
+ point_map.insert(faceaux.vertex[j],point_map.size());
+
+ }
+ }
+
+ face_list.push_back(face);
+ }
+
+ vertices.resize( point_map.size() );
+
+ DVector<Vector3>::Write vw = vertices.write();
+ Vector3 *verticesw=vw.ptr();
+
+ AABB _aabb;
+
+ for( Map<Vector3,int>::Element *E=point_map.front();E;E=E->next()) {
+
+ if (E==point_map.front()) {
+ _aabb.pos=E->key();
+ } else {
+
+ _aabb.expand_to(E->key());
+ }
+ verticesw[E->value()]=E->key();
+ }
+
+ point_map.clear(); // not needed anymore
+
+ faces.resize(face_list.size());
+ DVector<Face>::Write w = faces.write();
+ Face *facesw=w.ptr();
+
+ int fc=0;
+
+ for( List<Face>::Element *E=face_list.front();E;E=E->next()) {
+
+ facesw[fc++]=E->get();
+ }
+
+ face_list.clear();
+
+
+ DVector<_VolumeSW_BVH_Element> bvh_array;
+ bvh_array.resize( fc );
+
+ DVector<_VolumeSW_BVH_Element>::Write bvhw = bvh_array.write();
+ _VolumeSW_BVH_Element *bvh_arrayw=bvhw.ptr();
+
+
+ for(int i=0;i<fc;i++) {
+
+ AABB face_aabb;
+ face_aabb.pos=verticesw[facesw[i].indices[0]];
+ face_aabb.expand_to( verticesw[facesw[i].indices[1]] );
+ face_aabb.expand_to( verticesw[facesw[i].indices[2]] );
+
+ bvh_arrayw[i].face_index=i;
+ bvh_arrayw[i].aabb=face_aabb;
+ bvh_arrayw[i].center=face_aabb.pos+face_aabb.size*0.5;
+
+ }
+
+ w=DVector<Face>::Write();
+ vw=DVector<Vector3>::Write();
+
+
+ int count=0;
+ _VolumeSW_BVH *bvh_tree=_volume_sw_build_bvh(bvh_arrayw,fc,count);
+
+ ERR_FAIL_COND(count==0);
+
+ bvhw=DVector<_VolumeSW_BVH_Element>::Write();
+
+ bvh.resize( count+1 );
+
+ DVector<BVH>::Write bvhw2 = bvh.write();
+ BVH*bvh_arrayw2=bvhw2.ptr();
+
+ int idx=0;
+ _fill_bvh(bvh_tree,bvh_arrayw2,idx);
+
+ set_aabb(_aabb);
+
+#else
+ DVector<_VolumeSW_BVH_Element> bvh_array;
+ bvh_array.resize( src_face_count );
+
+ DVector<_VolumeSW_BVH_Element>::Write bvhw = bvh_array.write();
+ _VolumeSW_BVH_Element *bvh_arrayw=bvhw.ptr();
+
+ faces.resize(src_face_count);
+ DVector<Face>::Write w = faces.write();
+ Face *facesw=w.ptr();
+
+ vertices.resize( src_face_count*3 );
+
+ DVector<Vector3>::Write vw = vertices.write();
+ Vector3 *verticesw=vw.ptr();
+
+ AABB _aabb;
+
+
+ for(int i=0;i<src_face_count;i++) {
+
+ Face3 face( facesr[i*3+0], facesr[i*3+1], facesr[i*3+2] );
+
+ bvh_arrayw[i].aabb=face.get_aabb();
+ bvh_arrayw[i].center = bvh_arrayw[i].aabb.pos + bvh_arrayw[i].aabb.size * 0.5;
+ bvh_arrayw[i].face_index=i;
+ facesw[i].indices[0]=i*3+0;
+ facesw[i].indices[1]=i*3+1;
+ facesw[i].indices[2]=i*3+2;
+ facesw[i].normal=face.get_plane().normal;
+ verticesw[i*3+0]=face.vertex[0];
+ verticesw[i*3+1]=face.vertex[1];
+ verticesw[i*3+2]=face.vertex[2];
+ if (i==0)
+ _aabb=bvh_arrayw[i].aabb;
+ else
+ _aabb.merge_with(bvh_arrayw[i].aabb);
+
+ }
+
+ w=DVector<Face>::Write();
+ vw=DVector<Vector3>::Write();
+
+ int count=0;
+ _VolumeSW_BVH *bvh_tree=_volume_sw_build_bvh(bvh_arrayw,src_face_count,count);
+
+ bvh.resize( count+1 );
+
+ DVector<BVH>::Write bvhw2 = bvh.write();
+ BVH*bvh_arrayw2=bvhw2.ptr();
+
+ int idx=0;
+ _fill_bvh(bvh_tree,bvh_arrayw2,idx);
+
+ configure(_aabb); // this type of shape has no margin
+
+
+#endif
+}
+
+
+void ConcavePolygonShapeSW::set_data(const Variant& p_data) {
+
+
+ _setup(p_data);
+}
+
+Variant ConcavePolygonShapeSW::get_data() const {
+
+ return get_faces();
+}
+
+ConcavePolygonShapeSW::ConcavePolygonShapeSW() {
+
+
+}
+
+
+
+/* HEIGHT MAP SHAPE */
+
+DVector<float> HeightMapShapeSW::get_heights() const {
+
+ return heights;
+}
+int HeightMapShapeSW::get_width() const {
+
+ return width;
+}
+int HeightMapShapeSW::get_depth() const {
+
+ return depth;
+}
+float HeightMapShapeSW::get_cell_size() const {
+
+ return cell_size;
+}
+
+
+void HeightMapShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
+
+ //not very useful, but not very used either
+ p_transform.xform(get_aabb()).project_range_in_plane( Plane(p_normal,0),r_min,r_max );
+
+}
+
+Vector3 HeightMapShapeSW::get_support(const Vector3& p_normal) const {
+
+
+ //not very useful, but not very used either
+ return get_aabb().get_support(p_normal);
+
+}
+
+bool HeightMapShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_point, Vector3 &r_normal) const {
+
+
+ return false;
+}
+
+
+void HeightMapShapeSW::cull(const AABB& p_local_aabb,Callback p_callback,void* p_userdata) const {
+
+
+
+}
+
+
+Vector3 HeightMapShapeSW::get_moment_of_inertia(float p_mass) const {
+
+
+ // use crappy AABB approximation
+ Vector3 extents=get_aabb().size*0.5;
+
+ return Vector3(
+ (p_mass/3.0) * (extents.y*extents.y + extents.z*extents.z),
+ (p_mass/3.0) * (extents.x*extents.x + extents.z*extents.z),
+ (p_mass/3.0) * (extents.y*extents.y + extents.y*extents.y)
+ );
+}
+
+
+void HeightMapShapeSW::_setup(DVector<real_t> p_heights,int p_width,int p_depth,real_t p_cell_size) {
+
+ heights=p_heights;
+ width=p_width;
+ depth=p_depth;;
+ cell_size=p_cell_size;
+
+ DVector<real_t>::Read r = heights. read();
+
+ AABB aabb;
+
+ for(int i=0;i<depth;i++) {
+
+ for(int j=0;j<width;j++) {
+
+ float h = r[i*width+j];
+
+ Vector3 pos( j*cell_size, h, i*cell_size );
+ if (i==0 || j==0)
+ aabb.pos=pos;
+ else
+ aabb.expand_to(pos);
+
+ }
+ }
+
+
+ configure(aabb);
+}
+
+void HeightMapShapeSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND( p_data.get_type()!=Variant::DICTIONARY );
+ Dictionary d=p_data;
+ ERR_FAIL_COND( !d.has("width") );
+ ERR_FAIL_COND( !d.has("depth") );
+ ERR_FAIL_COND( !d.has("cell_size") );
+ ERR_FAIL_COND( !d.has("heights") );
+
+ int width=d["width"];
+ int depth=d["depth"];
+ float cell_size=d["cell_size"];
+ DVector<float> heights=d["heights"];
+
+ ERR_FAIL_COND( width<= 0);
+ ERR_FAIL_COND( depth<= 0);
+ ERR_FAIL_COND( cell_size<= CMP_EPSILON);
+ ERR_FAIL_COND( heights.size() != (width*depth) );
+ _setup(heights, width, depth, cell_size );
+
+}
+
+Variant HeightMapShapeSW::get_data() const {
+
+ ERR_FAIL_V(Variant());
+
+}
+
+HeightMapShapeSW::HeightMapShapeSW() {
+
+ width=0;
+ depth=0;
+ cell_size=0;
+}
+
+
+
diff --git a/servers/physics/shape_sw.h b/servers/physics/shape_sw.h
new file mode 100644
index 0000000000..890d6d8741
--- /dev/null
+++ b/servers/physics/shape_sw.h
@@ -0,0 +1,430 @@
+/*************************************************************************/
+/* shape_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SHAPE_SW_H
+#define SHAPE_SW_H
+
+#include "servers/physics_server.h"
+#include "bsp_tree.h"
+#include "geometry.h"
+/*
+
+SHAPE_LINE, ///< plane:"plane"
+SHAPE_SEGMENT, ///< float:"length"
+SHAPE_CIRCLE, ///< float:"radius"
+SHAPE_RECTANGLE, ///< vec3:"extents"
+SHAPE_CONVEX_POLYGON, ///< array of planes:"planes"
+SHAPE_CONCAVE_POLYGON, ///< Vector3 array:"triangles" , or Dictionary with "indices" (int array) and "triangles" (Vector3 array)
+SHAPE_CUSTOM, ///< Server-Implementation based custom shape, calling shape_create() with this value will result in an error
+
+*/
+
+class ShapeSW;
+
+class ShapeOwnerSW {
+public:
+
+ virtual void _shape_changed()=0;
+ virtual void remove_shape(ShapeSW *p_shape)=0;
+
+ virtual ~ShapeOwnerSW() {}
+};
+
+
+class ShapeSW {
+
+ RID self;
+ AABB aabb;
+ bool configured;
+ real_t custom_bias;
+
+ Map<ShapeOwnerSW*,int> owners;
+protected:
+
+ void configure(const AABB& p_aabb);
+public:
+
+ enum {
+ MAX_SUPPORTS=8
+ };
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const {return self; }
+
+ virtual PhysicsServer::ShapeType get_type() const=0;
+
+ _FORCE_INLINE_ AABB get_aabb() const { return aabb; }
+ _FORCE_INLINE_ bool is_configured() const { return configured; }
+
+ virtual bool is_concave() const { return false; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const=0;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const=0;
+
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_point, Vector3 &r_normal) const=0;
+ virtual Vector3 get_moment_of_inertia(float p_mass) const=0;
+
+ virtual void set_data(const Variant& p_data)=0;
+ virtual Variant get_data() const=0;
+
+ _FORCE_INLINE_ void set_custom_bias(real_t p_bias) { custom_bias=p_bias; }
+ _FORCE_INLINE_ real_t get_custom_bias() const { return custom_bias; }
+
+ void add_owner(ShapeOwnerSW *p_owner);
+ void remove_owner(ShapeOwnerSW *p_owner);
+ bool is_owner(ShapeOwnerSW *p_owner) const;
+ const Map<ShapeOwnerSW*,int>& get_owners() const;
+
+ ShapeSW();
+ virtual ~ShapeSW();
+};
+
+
+class ConcaveShapeSW : public ShapeSW {
+
+public:
+
+ virtual bool is_concave() const { return true; }
+ typedef void (*Callback)(void* p_userdata,ShapeSW *p_convex);
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const { r_amount=0; }
+
+ virtual void cull(const AABB& p_local_aabb,Callback p_callback,void* p_userdata) const=0;
+
+ ConcaveShapeSW() {}
+};
+
+class PlaneShapeSW : public ShapeSW {
+
+ Plane plane;
+
+ void _setup(const Plane& p_plane);
+public:
+
+ Plane get_plane() const;
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_PLANE; }
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const { r_amount=0; }
+
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ PlaneShapeSW();
+};
+
+class RayShapeSW : public ShapeSW {
+
+ float length;
+
+ void _setup(float p_length);
+public:
+
+ float get_length() const;
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_RAY; }
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ RayShapeSW();
+};
+
+class SphereShapeSW : public ShapeSW {
+
+ real_t radius;
+
+ void _setup(real_t p_radius);
+public:
+
+ real_t get_radius() const;
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_SPHERE; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const;
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ SphereShapeSW();
+};
+
+class BoxShapeSW : public ShapeSW {
+
+ Vector3 half_extents;
+ void _setup(const Vector3& p_half_extents);
+public:
+
+ _FORCE_INLINE_ Vector3 get_half_extents() const { return half_extents; }
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_BOX; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const;
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ BoxShapeSW();
+};
+
+class CapsuleShapeSW : public ShapeSW {
+
+ real_t height;
+ real_t radius;
+
+
+ void _setup(real_t p_height,real_t p_radius);
+public:
+
+ _FORCE_INLINE_ real_t get_height() const { return height; }
+ _FORCE_INLINE_ real_t get_radius() const { return radius; }
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_CAPSULE; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const;
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ CapsuleShapeSW();
+};
+
+struct ConvexPolygonShapeSW : public ShapeSW {
+
+ Geometry::MeshData mesh;
+
+ void _setup(const Vector<Vector3>& p_vertices);
+public:
+
+ const Geometry::MeshData& get_mesh() const { return mesh; }
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_CONVEX_POLYGON; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const;
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ ConvexPolygonShapeSW();
+
+};
+
+
+struct _VolumeSW_BVH;
+struct FaceShapeSW;
+
+struct ConcavePolygonShapeSW : public ConcaveShapeSW {
+ // always a trimesh
+
+ struct Face {
+
+ Vector3 normal;
+ int indices[3];
+ };
+
+ DVector<Face> faces;
+ DVector<Vector3> vertices;
+
+ struct BVH {
+
+ AABB aabb;
+ int left;
+ int right;
+
+ int face_index;
+ };
+
+ DVector<BVH> bvh;
+
+ struct _CullParams {
+
+ AABB aabb;
+ Callback callback;
+ void *userdata;
+ const Face *faces;
+ const Vector3 *vertices;
+ const BVH *bvh;
+ FaceShapeSW *face;
+ };
+
+ struct _SegmentCullParams {
+
+ Vector3 from;
+ Vector3 to;
+ const Face *faces;
+ const Vector3 *vertices;
+ const BVH *bvh;
+
+ Vector3 result;
+ Vector3 normal;
+ real_t min_d;
+ int collisions;
+
+ };
+
+ void _cull_segment(int p_idx,_SegmentCullParams *p_params) const;
+ void _cull(int p_idx,_CullParams *p_params) const;
+
+ void _fill_bvh(_VolumeSW_BVH* p_bvh_tree,BVH* p_bvh_array,int& p_idx);
+
+
+ void _setup(DVector<Vector3> p_faces);
+public:
+
+ DVector<Vector3> get_faces() const;
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_CONCAVE_POLYGON; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual void cull(const AABB& p_local_aabb,Callback p_callback,void* p_userdata) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ ConcavePolygonShapeSW();
+
+};
+
+
+struct HeightMapShapeSW : public ConcaveShapeSW {
+
+ DVector<real_t> heights;
+ int width;
+ int depth;
+ float cell_size;
+
+// void _cull_segment(int p_idx,_SegmentCullParams *p_params) const;
+// void _cull(int p_idx,_CullParams *p_params) const;
+
+ void _setup(DVector<float> p_heights,int p_width,int p_depth,float p_cell_size);
+public:
+
+ DVector<real_t> get_heights() const;
+ int get_width() const;
+ int get_depth() const;
+ float get_cell_size() const;
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_HEIGHTMAP; }
+
+ virtual void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ virtual Vector3 get_support(const Vector3& p_normal) const;
+ virtual bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ virtual void cull(const AABB& p_local_aabb,Callback p_callback,void* p_userdata) const;
+
+ virtual Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ HeightMapShapeSW();
+
+};
+
+//used internally
+struct FaceShapeSW : public ShapeSW {
+
+ Vector3 normal; //cache
+ Vector3 vertex[3];
+
+ virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_CONCAVE_POLYGON; }
+
+ const Vector3& get_vertex(int p_idx) const { return vertex[p_idx]; }
+
+ void project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const;
+ Vector3 get_support(const Vector3& p_normal) const;
+ virtual void get_supports(const Vector3& p_normal,int p_max,Vector3 *r_supports,int & r_amount) const;
+ bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const;
+
+ Vector3 get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data) {}
+ virtual Variant get_data() const { return Variant(); }
+
+ FaceShapeSW();
+};
+
+
+
+
+
+struct _ShapeTestConvexBSPSW {
+
+ const BSP_Tree *bsp;
+ const ShapeSW *shape;
+ Transform transform;
+
+ _FORCE_INLINE_ void project_range(const Vector3& p_normal, real_t& r_min, real_t& r_max) const {
+
+ shape->project_range(p_normal,transform,r_min,r_max);
+ }
+
+};
+
+
+
+
+#endif // SHAPESW_H
diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp
new file mode 100644
index 0000000000..ca3aa7e1f5
--- /dev/null
+++ b/servers/physics/space_sw.cpp
@@ -0,0 +1,429 @@
+/*************************************************************************/
+/* space_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "space_sw.h"
+#include "collision_solver_sw.h"
+#include "physics_server_sw.h"
+
+
+bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3& p_from, const Vector3& p_to,RayResult &r_result,const Set<RID>& p_exclude,uint32_t p_user_mask) {
+
+
+ ERR_FAIL_COND_V(space->locked,false);
+
+ Vector3 begin,end;
+ Vector3 normal;
+ begin=p_from;
+ end=p_to;
+ normal=(end-begin).normalized();
+
+
+ int amount = space->broadphase->cull_segment(begin,end,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
+
+
+ //todo, create another array tha references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision
+
+ bool collided=false;
+ Vector3 res_point,res_normal;
+ int res_shape;
+ const CollisionObjectSW *res_obj;
+ real_t min_d=1e10;
+
+
+ for(int i=0;i<amount;i++) {
+
+ if (space->intersection_query_results[i]->get_type()==CollisionObjectSW::TYPE_AREA)
+ continue; //ignore area
+
+ if (p_exclude.has( space->intersection_query_results[i]->get_self()))
+ continue;
+
+ const CollisionObjectSW *col_obj=space->intersection_query_results[i];
+
+ int shape_idx=space->intersection_query_subindex_results[i];
+ Transform inv_xform = col_obj->get_shape_inv_transform(shape_idx) * col_obj->get_inv_transform();
+
+ Vector3 local_from = inv_xform.xform(begin);
+ Vector3 local_to = inv_xform.xform(end);
+
+ const ShapeSW *shape = col_obj->get_shape(shape_idx);
+
+ Vector3 shape_point,shape_normal;
+
+ if (shape->intersect_segment(local_from,local_to,shape_point,shape_normal)) {
+
+ Transform xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
+ shape_point=xform.xform(shape_point);
+
+ real_t ld = normal.dot(shape_point);
+
+
+ if (ld<min_d) {
+
+ min_d=ld;
+ res_point=shape_point;
+ res_normal=inv_xform.basis.xform_inv(shape_normal).normalized();
+ res_shape=shape_idx;
+ res_obj=col_obj;
+ collided=true;
+ }
+ }
+
+ }
+
+ if (!collided)
+ return false;
+
+
+ r_result.collider_id=res_obj->get_instance_id();
+ if (r_result.collider_id!=0)
+ r_result.collider=ObjectDB::get_instance(r_result.collider_id);
+ r_result.normal=res_normal;
+ r_result.position=res_point;
+ r_result.rid=res_obj->get_self();
+ r_result.shape=res_shape;
+
+ return true;
+
+}
+
+
+int PhysicsDirectSpaceStateSW::intersect_shape(const RID& p_shape, const Transform& p_xform,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude,uint32_t p_user_mask) {
+
+ if (p_result_max<=0)
+ return 0;
+
+ ShapeSW *shape = static_cast<PhysicsServerSW*>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,0);
+
+ AABB aabb = p_xform.xform(shape->get_aabb());
+
+ int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
+
+ bool collided=false;
+ int cc=0;
+
+ //Transform ai = p_xform.affine_inverse();
+
+ for(int i=0;i<amount;i++) {
+
+ if (cc>=p_result_max)
+ break;
+
+ if (space->intersection_query_results[i]->get_type()==CollisionObjectSW::TYPE_AREA)
+ continue; //ignore area
+
+ if (p_exclude.has( space->intersection_query_results[i]->get_self()))
+ continue;
+
+
+ const CollisionObjectSW *col_obj=space->intersection_query_results[i];
+ int shape_idx=space->intersection_query_subindex_results[i];
+
+ if (!CollisionSolverSW::solve_static(shape,p_xform,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), NULL,NULL,NULL))
+ continue;
+
+ r_results[cc].collider_id=col_obj->get_instance_id();
+ if (r_results[cc].collider_id!=0)
+ r_results[cc].collider=ObjectDB::get_instance(r_results[cc].collider_id);
+ r_results[cc].rid=col_obj->get_self();
+ r_results[cc].shape=shape_idx;
+
+ cc++;
+
+ }
+
+ return cc;
+
+}
+
+PhysicsDirectSpaceStateSW::PhysicsDirectSpaceStateSW() {
+
+
+ space=NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+
+
+
+void* SpaceSW::_broadphase_pair(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_self) {
+
+ CollisionObjectSW::Type type_A=A->get_type();
+ CollisionObjectSW::Type type_B=B->get_type();
+ if (type_A>type_B) {
+
+ SWAP(A,B);
+ SWAP(p_subindex_A,p_subindex_B);
+ SWAP(type_A,type_B);
+ }
+
+ SpaceSW *self = (SpaceSW*)p_self;
+
+ if (type_A==CollisionObjectSW::TYPE_AREA) {
+
+
+ ERR_FAIL_COND_V(type_B!=CollisionObjectSW::TYPE_BODY,NULL);
+ AreaSW *area=static_cast<AreaSW*>(A);
+ BodySW *body=static_cast<BodySW*>(B);
+
+
+ AreaPairSW *area_pair = memnew(AreaPairSW(body,p_subindex_B,area,p_subindex_A) );
+
+ return area_pair;
+ } else {
+
+
+ BodyPairSW *b = memnew( BodyPairSW((BodySW*)A,p_subindex_A,(BodySW*)B,p_subindex_B) );
+ return b;
+
+ }
+
+ return NULL;
+}
+
+void SpaceSW::_broadphase_unpair(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_data,void *p_self) {
+
+
+
+ SpaceSW *self = (SpaceSW*)p_self;
+ ConstraintSW *c = (ConstraintSW*)p_data;
+ memdelete(c);
+}
+
+
+const SelfList<BodySW>::List& SpaceSW::get_active_body_list() const {
+
+ return active_list;
+}
+void SpaceSW::body_add_to_active_list(SelfList<BodySW>* p_body) {
+
+ active_list.add(p_body);
+}
+void SpaceSW::body_remove_from_active_list(SelfList<BodySW>* p_body) {
+
+ active_list.remove(p_body);
+
+}
+
+void SpaceSW::body_add_to_inertia_update_list(SelfList<BodySW>* p_body) {
+
+
+ inertia_update_list.add(p_body);
+}
+
+void SpaceSW::body_remove_from_inertia_update_list(SelfList<BodySW>* p_body) {
+
+ inertia_update_list.remove(p_body);
+}
+
+BroadPhaseSW *SpaceSW::get_broadphase() {
+
+ return broadphase;
+}
+
+void SpaceSW::add_object(CollisionObjectSW *p_object) {
+
+ ERR_FAIL_COND( objects.has(p_object) );
+ objects.insert(p_object);
+}
+
+void SpaceSW::remove_object(CollisionObjectSW *p_object) {
+
+ ERR_FAIL_COND( !objects.has(p_object) );
+ objects.erase(p_object);
+}
+
+const Set<CollisionObjectSW*> &SpaceSW::get_objects() const {
+
+ return objects;
+}
+
+void SpaceSW::body_add_to_state_query_list(SelfList<BodySW>* p_body) {
+
+ state_query_list.add(p_body);
+}
+void SpaceSW::body_remove_from_state_query_list(SelfList<BodySW>* p_body) {
+
+ state_query_list.remove(p_body);
+}
+
+void SpaceSW::area_add_to_monitor_query_list(SelfList<AreaSW>* p_area) {
+
+ monitor_query_list.add(p_area);
+}
+void SpaceSW::area_remove_from_monitor_query_list(SelfList<AreaSW>* p_area) {
+
+ monitor_query_list.remove(p_area);
+}
+
+void SpaceSW::area_add_to_moved_list(SelfList<AreaSW>* p_area) {
+
+ area_moved_list.add(p_area);
+}
+
+void SpaceSW::area_remove_from_moved_list(SelfList<AreaSW>* p_area) {
+
+ area_moved_list.remove(p_area);
+}
+
+const SelfList<AreaSW>::List& SpaceSW::get_moved_area_list() const {
+
+ return area_moved_list;
+}
+
+
+
+
+void SpaceSW::call_queries() {
+
+ while(state_query_list.first()) {
+
+ BodySW * b = state_query_list.first()->self();
+ b->call_queries();
+ state_query_list.remove(state_query_list.first());
+ }
+
+ while(monitor_query_list.first()) {
+
+ AreaSW * a = monitor_query_list.first()->self();
+ a->call_queries();
+ monitor_query_list.remove(monitor_query_list.first());
+ }
+
+}
+
+void SpaceSW::setup() {
+
+
+ while(inertia_update_list.first()) {
+ inertia_update_list.first()->self()->update_inertias();
+ inertia_update_list.remove(inertia_update_list.first());
+ }
+
+
+}
+
+void SpaceSW::update() {
+
+ broadphase->update();
+
+}
+
+
+void SpaceSW::set_param(PhysicsServer::SpaceParameter p_param, real_t p_value) {
+
+ switch(p_param) {
+
+ case PhysicsServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: contact_recycle_radius=p_value; break;
+ case PhysicsServer::SPACE_PARAM_CONTACT_MAX_SEPARATION: contact_max_separation=p_value; break;
+ case PhysicsServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION: contact_max_allowed_penetration=p_value; break;
+ case PhysicsServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD: body_linear_velocity_sleep_treshold=p_value; break;
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD: body_angular_velocity_sleep_treshold=p_value; break;
+ case PhysicsServer::SPACE_PARAM_BODY_TIME_TO_SLEEP: body_time_to_sleep=p_value; break;
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO: body_angular_velocity_damp_ratio=p_value; break;
+ case PhysicsServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: constraint_bias=p_value; break;
+ }
+}
+
+real_t SpaceSW::get_param(PhysicsServer::SpaceParameter p_param) const {
+
+ switch(p_param) {
+
+ case PhysicsServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: return contact_recycle_radius;
+ case PhysicsServer::SPACE_PARAM_CONTACT_MAX_SEPARATION: return contact_max_separation;
+ case PhysicsServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION: return contact_max_allowed_penetration;
+ case PhysicsServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD: return body_linear_velocity_sleep_treshold;
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD: return body_angular_velocity_sleep_treshold;
+ case PhysicsServer::SPACE_PARAM_BODY_TIME_TO_SLEEP: return body_time_to_sleep;
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO: return body_angular_velocity_damp_ratio;
+ case PhysicsServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: return constraint_bias;
+ }
+ return 0;
+}
+
+void SpaceSW::lock() {
+
+ locked=true;
+}
+
+void SpaceSW::unlock() {
+
+ locked=false;
+}
+
+bool SpaceSW::is_locked() const {
+
+ return locked;
+}
+
+PhysicsDirectSpaceStateSW *SpaceSW::get_direct_state() {
+
+ return direct_access;
+}
+
+SpaceSW::SpaceSW() {
+
+
+ locked=false;
+ contact_recycle_radius=0.01;
+ contact_max_separation=0.05;
+ contact_max_allowed_penetration= 0.01;
+
+ constraint_bias = 0.01;
+ body_linear_velocity_sleep_treshold=0.01;
+ body_angular_velocity_sleep_treshold=(8.0 / 180.0 * Math_PI);
+ body_time_to_sleep=0.5;
+ body_angular_velocity_damp_ratio=10;
+
+
+ broadphase = BroadPhaseSW::create_func();
+ broadphase->set_pair_callback(_broadphase_pair,this);
+ broadphase->set_unpair_callback(_broadphase_unpair,this);
+ area=NULL;
+
+ direct_access = memnew( PhysicsDirectSpaceStateSW );
+ direct_access->space=this;
+}
+
+SpaceSW::~SpaceSW() {
+
+ memdelete(broadphase);
+ memdelete( direct_access );
+}
+
+
+
diff --git a/servers/physics/space_sw.h b/servers/physics/space_sw.h
new file mode 100644
index 0000000000..202c7ccbd2
--- /dev/null
+++ b/servers/physics/space_sw.h
@@ -0,0 +1,157 @@
+/*************************************************************************/
+/* space_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SPACE_SW_H
+#define SPACE_SW_H
+
+#include "typedefs.h"
+#include "hash_map.h"
+#include "body_sw.h"
+#include "area_sw.h"
+#include "body_pair_sw.h"
+#include "area_pair_sw.h"
+#include "broad_phase_sw.h"
+#include "collision_object_sw.h"
+
+
+class PhysicsDirectSpaceStateSW : public PhysicsDirectSpaceState {
+
+ OBJ_TYPE( PhysicsDirectSpaceStateSW, PhysicsDirectSpaceState );
+public:
+
+ SpaceSW *space;
+
+ bool intersect_ray(const Vector3& p_from, const Vector3& p_to,RayResult &r_result,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0);
+ int intersect_shape(const RID& p_shape, const Transform& p_xform,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0);
+
+ PhysicsDirectSpaceStateSW();
+};
+
+
+
+class SpaceSW {
+
+
+ PhysicsDirectSpaceStateSW *direct_access;
+ RID self;
+
+ BroadPhaseSW *broadphase;
+ SelfList<BodySW>::List active_list;
+ SelfList<BodySW>::List inertia_update_list;
+ SelfList<BodySW>::List state_query_list;
+ SelfList<AreaSW>::List monitor_query_list;
+ SelfList<AreaSW>::List area_moved_list;
+
+ static void* _broadphase_pair(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_self);
+ static void _broadphase_unpair(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_data,void *p_self);
+
+ Set<CollisionObjectSW*> objects;
+
+ AreaSW *area;
+
+ real_t contact_recycle_radius;
+ real_t contact_max_separation;
+ real_t contact_max_allowed_penetration;
+ real_t constraint_bias;
+
+ enum {
+
+ INTERSECTION_QUERY_MAX=2048
+ };
+
+ CollisionObjectSW *intersection_query_results[INTERSECTION_QUERY_MAX];
+ int intersection_query_subindex_results[INTERSECTION_QUERY_MAX];
+
+ float body_linear_velocity_sleep_treshold;
+ float body_angular_velocity_sleep_treshold;
+ float body_time_to_sleep;
+ float body_angular_velocity_damp_ratio;
+
+ bool locked;
+
+friend class PhysicsDirectSpaceStateSW;
+
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ void set_default_area(AreaSW *p_area) { area=p_area; }
+ AreaSW *get_default_area() const { return area; }
+
+ const SelfList<BodySW>::List& get_active_body_list() const;
+ void body_add_to_active_list(SelfList<BodySW>* p_body);
+ void body_remove_from_active_list(SelfList<BodySW>* p_body);
+ void body_add_to_inertia_update_list(SelfList<BodySW>* p_body);
+ void body_remove_from_inertia_update_list(SelfList<BodySW>* p_body);
+
+ void body_add_to_state_query_list(SelfList<BodySW>* p_body);
+ void body_remove_from_state_query_list(SelfList<BodySW>* p_body);
+
+ void area_add_to_monitor_query_list(SelfList<AreaSW>* p_area);
+ void area_remove_from_monitor_query_list(SelfList<AreaSW>* p_area);
+ void area_add_to_moved_list(SelfList<AreaSW>* p_area);
+ void area_remove_from_moved_list(SelfList<AreaSW>* p_area);
+ const SelfList<AreaSW>::List& get_moved_area_list() const;
+
+ BroadPhaseSW *get_broadphase();
+
+ void add_object(CollisionObjectSW *p_object);
+ void remove_object(CollisionObjectSW *p_object);
+ const Set<CollisionObjectSW*> &get_objects() const;
+
+ _FORCE_INLINE_ real_t get_contact_recycle_radius() const { return contact_recycle_radius; }
+ _FORCE_INLINE_ real_t get_contact_max_separation() const { return contact_max_separation; }
+ _FORCE_INLINE_ real_t get_contact_max_allowed_penetration() const { return contact_max_allowed_penetration; }
+ _FORCE_INLINE_ real_t get_constraint_bias() const { return constraint_bias; }
+ _FORCE_INLINE_ real_t get_body_linear_velocity_sleep_treshold() const { return body_linear_velocity_sleep_treshold; }
+ _FORCE_INLINE_ real_t get_body_angular_velocity_sleep_treshold() const { return body_angular_velocity_sleep_treshold; }
+ _FORCE_INLINE_ real_t get_body_time_to_sleep() const { return body_time_to_sleep; }
+ _FORCE_INLINE_ real_t get_body_angular_velocity_damp_ratio() const { return body_angular_velocity_damp_ratio; }
+
+
+ void update();
+ void setup();
+ void call_queries();
+
+
+ bool is_locked() const;
+ void lock();
+ void unlock();
+
+ void set_param(PhysicsServer::SpaceParameter p_param, real_t p_value);
+ real_t get_param(PhysicsServer::SpaceParameter p_param) const;
+
+ PhysicsDirectSpaceStateSW *get_direct_state();
+
+ SpaceSW();
+ ~SpaceSW();
+};
+
+
+#endif // SPACE__SW_H
diff --git a/servers/physics/step_sw.cpp b/servers/physics/step_sw.cpp
new file mode 100644
index 0000000000..b7815d2250
--- /dev/null
+++ b/servers/physics/step_sw.cpp
@@ -0,0 +1,237 @@
+/*************************************************************************/
+/* step_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "step_sw.h"
+
+
+void StepSW::_populate_island(BodySW* p_body,BodySW** p_island,ConstraintSW **p_constraint_island) {
+
+ p_body->set_island_step(_step);
+ p_body->set_island_next(*p_island);
+ *p_island=p_body;
+
+ for(Map<ConstraintSW*,int>::Element *E=p_body->get_constraint_map().front();E;E=E->next()) {
+
+ ConstraintSW *c=(ConstraintSW*)E->key();
+ if (c->get_island_step()==_step)
+ continue; //already processed
+ c->set_island_step(_step);
+ c->set_island_next(*p_constraint_island);
+ *p_constraint_island=c;
+
+
+ for(int i=0;i<c->get_body_count();i++) {
+ if (i==E->get())
+ continue;
+ BodySW *b = c->get_body_ptr()[i];
+ if (b->get_island_step()==_step || b->get_mode()==PhysicsServer::BODY_MODE_STATIC)
+ continue; //no go
+ _populate_island(c->get_body_ptr()[i],p_island,p_constraint_island);
+ }
+ }
+}
+
+void StepSW::_setup_island(ConstraintSW *p_island,float p_delta) {
+
+ ConstraintSW *ci=p_island;
+ while(ci) {
+ bool process = ci->setup(p_delta);
+ //todo remove from island if process fails
+ ci=ci->get_island_next();
+ }
+}
+
+void StepSW::_solve_island(ConstraintSW *p_island,int p_iterations,float p_delta){
+
+ for(int i=0;i<p_iterations;i++) {
+
+ ConstraintSW *ci=p_island;
+ while(ci) {
+ ci->solve(p_delta);
+ ci=ci->get_island_next();
+ }
+ }
+}
+
+void StepSW::_check_suspend(BodySW *p_island,float p_delta) {
+
+
+ bool can_sleep=true;
+
+ BodySW *b = p_island;
+ while(b) {
+
+ if (b->get_mode()==PhysicsServer::BODY_MODE_STATIC)
+ continue; //ignore for static
+
+ if (!b->sleep_test(p_delta))
+ can_sleep=false;
+
+ b=b->get_island_next();
+ }
+
+ //put all to sleep or wake up everyoen
+
+ b = p_island;
+ while(b) {
+
+ if (b->get_mode()==PhysicsServer::BODY_MODE_STATIC)
+ continue; //ignore for static
+
+ bool active = b->is_active();
+
+ if (active==can_sleep)
+ b->set_active(!can_sleep);
+
+ b=b->get_island_next();
+ }
+}
+
+void StepSW::step(SpaceSW* p_space,float p_delta,int p_iterations) {
+
+ p_space->lock(); // can't access space during this
+
+ p_space->setup(); //update inertias, etc
+
+ const SelfList<BodySW>::List * body_list = &p_space->get_active_body_list();
+
+ /* INTEGRATE FORCES */
+ int active_count=0;
+
+ const SelfList<BodySW>*b = body_list->first();
+ while(b) {
+
+ b->self()->integrate_forces(p_delta);
+ b=b->next();
+ active_count++;
+ }
+
+
+
+ /* GENERATE CONSTRAINT ISLANDS */
+
+ BodySW *island_list=NULL;
+ ConstraintSW *constraint_island_list=NULL;
+ b = body_list->first();
+
+ int island_count=0;
+
+ while(b) {
+ BodySW *body = b->self();
+
+
+ if (body->get_island_step()!=_step) {
+
+ BodySW *island=NULL;
+ ConstraintSW *constraint_island=NULL;
+ _populate_island(body,&island,&constraint_island);
+
+ island->set_island_list_next(island_list);
+ island_list=island;
+
+ if (constraint_island) {
+ constraint_island->set_island_list_next(constraint_island_list);
+ constraint_island_list=constraint_island;
+ island_count++;
+ }
+
+ }
+ b=b->next();
+ }
+
+ const SelfList<AreaSW>::List &aml = p_space->get_moved_area_list();
+
+ while(aml.first()) {
+ for(const Set<ConstraintSW*>::Element *E=aml.first()->self()->get_constraints().front();E;E=E->next()) {
+
+ ConstraintSW*c=E->get();
+ if (c->get_island_step()==_step)
+ continue;
+ c->set_island_step(_step);
+ c->set_island_next(NULL);
+ c->set_island_list_next(constraint_island_list);
+ constraint_island_list=c;
+ }
+ p_space->area_remove_from_moved_list((SelfList<AreaSW>*)aml.first()); //faster to remove here
+ }
+
+// print_line("island count: "+itos(island_count)+" active count: "+itos(active_count));
+ /* SETUP CONSTRAINT ISLANDS */
+
+ {
+ ConstraintSW *ci=constraint_island_list;
+ while(ci) {
+
+ _setup_island(ci,p_delta);
+ ci=ci->get_island_list_next();
+ }
+ }
+
+ /* SOLVE CONSTRAINT ISLANDS */
+
+ {
+ ConstraintSW *ci=constraint_island_list;
+ while(ci) {
+ //iterating each island separatedly improves cache efficiency
+ _solve_island(ci,p_iterations,p_delta);
+ ci=ci->get_island_list_next();
+ }
+ }
+
+ /* INTEGRATE VELOCITIES */
+
+ b = body_list->first();
+ while(b) {
+
+ b->self()->integrate_velocities(p_delta);
+ b=b->next();
+ }
+
+ /* SLEEP / WAKE UP ISLANDS */
+
+ {
+ BodySW *bi=island_list;
+ while(bi) {
+
+ _check_suspend(bi,p_delta);
+ bi=bi->get_island_list_next();
+ }
+ }
+
+ p_space->update();
+ p_space->unlock();
+ _step++;
+
+
+
+}
+
+StepSW::StepSW() {
+
+ _step=1;
+}
diff --git a/servers/physics/step_sw.h b/servers/physics/step_sw.h
new file mode 100644
index 0000000000..9aaef97920
--- /dev/null
+++ b/servers/physics/step_sw.h
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* step_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 STEP_SW_H
+#define STEP_SW_H
+
+#include "space_sw.h"
+
+class StepSW {
+
+ uint64_t _step;
+
+ void _populate_island(BodySW* p_body,BodySW** p_island,ConstraintSW **p_constraint_island);
+ void _setup_island(ConstraintSW *p_island,float p_delta);
+ void _solve_island(ConstraintSW *p_island,int p_iterations,float p_delta);
+ void _check_suspend(BodySW *p_island,float p_delta);
+public:
+
+ void step(SpaceSW* p_space,float p_delta,int p_iterations);
+ StepSW();
+};
+
+#endif // STEP__SW_H
diff --git a/servers/physics_2d/SCsub b/servers/physics_2d/SCsub
new file mode 100644
index 0000000000..a2c2b51a61
--- /dev/null
+++ b/servers/physics_2d/SCsub
@@ -0,0 +1,4 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
diff --git a/servers/physics_2d/area_2d_sw.cpp b/servers/physics_2d/area_2d_sw.cpp
new file mode 100644
index 0000000000..6c391a7aa0
--- /dev/null
+++ b/servers/physics_2d/area_2d_sw.cpp
@@ -0,0 +1,193 @@
+/*************************************************************************/
+/* area_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "area_2d_sw.h"
+#include "space_2d_sw.h"
+#include "body_2d_sw.h"
+
+Area2DSW::BodyKey::BodyKey(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) { rid=p_body->get_self(); instance_id=p_body->get_instance_id(); body_shape=p_body_shape; area_shape=p_area_shape; }
+
+void Area2DSW::_shapes_changed() {
+
+
+}
+
+void Area2DSW::set_transform(const Matrix32& p_transform) {
+
+ if (!moved_list.in_list() && get_space())
+ get_space()->area_add_to_moved_list(&moved_list);
+
+ _set_transform(p_transform);
+}
+
+void Area2DSW::set_space(Space2DSW *p_space) {
+
+ if (get_space()) {
+ if (monitor_query_list.in_list())
+ get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
+ if (moved_list.in_list())
+ get_space()->area_remove_from_moved_list(&moved_list);
+
+ }
+
+ monitored_bodies.clear();
+
+ _set_space(p_space);
+}
+
+
+void Area2DSW::set_monitor_callback(ObjectID p_id, const StringName& p_method) {
+
+
+ if (p_id==monitor_callback_id) {
+ monitor_callback_method=p_method;
+ return;
+ }
+
+ _unregister_shapes();
+
+ monitor_callback_id=p_id;
+ monitor_callback_method=p_method;
+
+ monitored_bodies.clear();
+
+ _shape_changed();
+
+}
+
+
+
+void Area2DSW::set_space_override_mode(Physics2DServer::AreaSpaceOverrideMode p_mode) {
+ bool do_override=p_mode!=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED;
+ if (do_override==space_override_mode!=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED)
+ return;
+ _unregister_shapes();
+ space_override_mode=p_mode;
+ _shape_changed();
+}
+
+void Area2DSW::set_param(Physics2DServer::AreaParameter p_param, const Variant& p_value) {
+
+ switch(p_param) {
+ case Physics2DServer::AREA_PARAM_GRAVITY: gravity=p_value; ; break;
+ case Physics2DServer::AREA_PARAM_GRAVITY_VECTOR: gravity_vector=p_value; ; break;
+ case Physics2DServer::AREA_PARAM_GRAVITY_IS_POINT: gravity_is_point=p_value; ; break;
+ case Physics2DServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION: point_attenuation=p_value; ; break;
+ case Physics2DServer::AREA_PARAM_DENSITY: density=p_value; ; break;
+ case Physics2DServer::AREA_PARAM_PRIORITY: priority=p_value; ; break;
+ }
+
+
+}
+
+Variant Area2DSW::get_param(Physics2DServer::AreaParameter p_param) const {
+
+
+ switch(p_param) {
+ case Physics2DServer::AREA_PARAM_GRAVITY: return gravity;
+ case Physics2DServer::AREA_PARAM_GRAVITY_VECTOR: return gravity_vector;
+ case Physics2DServer::AREA_PARAM_GRAVITY_IS_POINT: return gravity_is_point;
+ case Physics2DServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION: return point_attenuation;
+ case Physics2DServer::AREA_PARAM_DENSITY: return density;
+ case Physics2DServer::AREA_PARAM_PRIORITY: return priority;
+ }
+
+ return Variant();
+}
+
+
+void Area2DSW::_queue_monitor_update() {
+
+ ERR_FAIL_COND(!get_space());
+
+ if (!monitor_query_list.in_list())
+ get_space()->area_add_to_monitor_query_list(&monitor_query_list);
+
+
+}
+
+void Area2DSW::call_queries() {
+
+ if (monitor_callback_id && !monitored_bodies.empty()) {
+
+ Variant res[5];
+ Variant *resptr[5];
+ for(int i=0;i<5;i++)
+ resptr[i]=&res[i];
+
+ Object *obj = ObjectDB::get_instance(monitor_callback_id);
+ if (!obj) {
+ monitored_bodies.clear();
+ monitor_callback_id=0;
+ return;
+ }
+
+
+
+ for (Map<BodyKey,BodyState>::Element *E=monitored_bodies.front();E;E=E->next()) {
+
+ if (E->get().state==0)
+ continue; //nothing happened
+
+ res[0]=E->get().state>0 ? Physics2DServer::AREA_BODY_ADDED : Physics2DServer::AREA_BODY_REMOVED;
+ res[1]=E->key().rid;
+ res[2]=E->key().instance_id;
+ res[3]=E->key().body_shape;
+ res[4]=E->key().area_shape;
+
+ Variant::CallError ce;
+ obj->call(monitor_callback_method,(const Variant**)resptr,5,ce);
+ }
+ }
+
+ monitored_bodies.clear();
+
+ //get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
+
+}
+
+Area2DSW::Area2DSW() : CollisionObject2DSW(TYPE_AREA), monitor_query_list(this), moved_list(this) {
+
+ _set_static(true); //areas are never active
+ space_override_mode=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED;
+ gravity=9.80665;
+ gravity_vector=Vector2(0,-1);
+ gravity_is_point=false;
+ point_attenuation=1;
+ density=0.1;
+ priority=0;
+ monitor_callback_id=0;
+
+
+}
+
+Area2DSW::~Area2DSW() {
+
+
+}
+
diff --git a/servers/physics_2d/area_2d_sw.h b/servers/physics_2d/area_2d_sw.h
new file mode 100644
index 0000000000..51e6ccd166
--- /dev/null
+++ b/servers/physics_2d/area_2d_sw.h
@@ -0,0 +1,172 @@
+/*************************************************************************/
+/* area_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 AREA_2D_SW_H
+#define AREA_2D_SW_H
+
+#include "servers/physics_2d_server.h"
+#include "collision_object_2d_sw.h"
+#include "self_list.h"
+//#include "servers/physics/query_sw.h"
+
+class Space2DSW;
+class Body2DSW;
+class Constraint2DSW;
+
+
+class Area2DSW : public CollisionObject2DSW{
+
+
+ Physics2DServer::AreaSpaceOverrideMode space_override_mode;
+ float gravity;
+ Vector2 gravity_vector;
+ bool gravity_is_point;
+ float point_attenuation;
+ float density;
+ int priority;
+
+ ObjectID monitor_callback_id;
+ StringName monitor_callback_method;
+
+ SelfList<Area2DSW> monitor_query_list;
+ SelfList<Area2DSW> moved_list;
+
+ struct BodyKey {
+
+ RID rid;
+ ObjectID instance_id;
+ uint32_t body_shape;
+ uint32_t area_shape;
+
+ _FORCE_INLINE_ bool operator<( const BodyKey& p_key) const {
+
+ if (rid==p_key.rid) {
+
+ if (body_shape==p_key.body_shape) {
+
+ return area_shape < p_key.area_shape;
+ } else
+ return body_shape < p_key.area_shape;
+ } else
+ return rid < p_key.rid;
+
+ }
+
+ _FORCE_INLINE_ BodyKey() {}
+ BodyKey(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+ };
+
+ struct BodyState {
+
+ int state;
+ _FORCE_INLINE_ void inc() { state++; }
+ _FORCE_INLINE_ void dec() { state--; }
+ _FORCE_INLINE_ BodyState() { state=0; }
+ };
+
+ Map<BodyKey,BodyState> monitored_bodies;
+
+ //virtual void shape_changed_notify(Shape2DSW *p_shape);
+ //virtual void shape_deleted_notify(Shape2DSW *p_shape);
+ Set<Constraint2DSW*> constraints;
+
+
+ virtual void _shapes_changed();
+ void _queue_monitor_update();
+
+public:
+
+ //_FORCE_INLINE_ const Matrix32& get_inverse_transform() const { return inverse_transform; }
+ //_FORCE_INLINE_ SpaceSW* get_owner() { return owner; }
+
+ void set_monitor_callback(ObjectID p_id, const StringName& p_method);
+ _FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback_id; }
+
+ _FORCE_INLINE_ void add_body_to_query(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+ _FORCE_INLINE_ void remove_body_from_query(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+
+ void set_param(Physics2DServer::AreaParameter p_param, const Variant& p_value);
+ Variant get_param(Physics2DServer::AreaParameter p_param) const;
+
+ void set_space_override_mode(Physics2DServer::AreaSpaceOverrideMode p_mode);
+ Physics2DServer::AreaSpaceOverrideMode get_space_override_mode() const { return space_override_mode; }
+
+ _FORCE_INLINE_ void set_gravity(float p_gravity) { gravity=p_gravity; }
+ _FORCE_INLINE_ float get_gravity() const { return gravity; }
+
+ _FORCE_INLINE_ void set_gravity_vector(const Vector2& p_gravity) { gravity_vector=p_gravity; }
+ _FORCE_INLINE_ Vector2 get_gravity_vector() const { return gravity_vector; }
+
+ _FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point=p_enable; }
+ _FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; }
+
+ _FORCE_INLINE_ void set_point_attenuation(float p_point_attenuation) { point_attenuation=p_point_attenuation; }
+ _FORCE_INLINE_ float get_point_attenuation() const { return point_attenuation; }
+
+ _FORCE_INLINE_ void set_density(float p_density) { density=p_density; }
+ _FORCE_INLINE_ float get_density() const { return density; }
+
+ _FORCE_INLINE_ void set_priority(int p_priority) { priority=p_priority; }
+ _FORCE_INLINE_ int get_priority() const { return priority; }
+
+ _FORCE_INLINE_ void add_constraint( Constraint2DSW* p_constraint) { constraints.insert(p_constraint); }
+ _FORCE_INLINE_ void remove_constraint( Constraint2DSW* p_constraint) { constraints.erase(p_constraint); }
+ _FORCE_INLINE_ const Set<Constraint2DSW*>& get_constraints() const { return constraints; }
+
+ void set_transform(const Matrix32& p_transform);
+
+ void set_space(Space2DSW *p_space);
+
+
+ void call_queries();
+
+ Area2DSW();
+ ~Area2DSW();
+};
+
+void Area2DSW::add_body_to_query(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) {
+
+ BodyKey bk(p_body,p_body_shape,p_area_shape);
+ monitored_bodies[bk].inc();
+ if (!monitor_query_list.in_list())
+ _queue_monitor_update();
+}
+void Area2DSW::remove_body_from_query(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) {
+
+ BodyKey bk(p_body,p_body_shape,p_area_shape);
+ monitored_bodies[bk].dec();
+ if (!monitor_query_list.in_list())
+ _queue_monitor_update();
+}
+
+
+
+
+
+
+#endif // AREA_2D_SW_H
diff --git a/servers/physics_2d/area_pair_2d_sw.cpp b/servers/physics_2d/area_pair_2d_sw.cpp
new file mode 100644
index 0000000000..da9a02922c
--- /dev/null
+++ b/servers/physics_2d/area_pair_2d_sw.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* area_pair_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "area_pair_2d_sw.h"
+#include "collision_solver_2d_sw.h"
+
+
+bool AreaPair2DSW::setup(float p_step) {
+
+ bool result = CollisionSolver2DSW::solve_static(body->get_shape(body_shape),body->get_transform() * body->get_shape_transform(body_shape),body->get_shape_inv_transform(body_shape) * body->get_inv_transform(),area->get_shape(area_shape),area->get_transform() * area->get_shape_transform(area_shape),area->get_shape_inv_transform(area_shape) * area->get_inv_transform(),NULL,this);
+
+ if (result!=colliding) {
+
+ if (result) {
+
+ if (area->get_space_override_mode()!=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED)
+ body->add_area(area);
+ if (area->has_monitor_callback())
+ area->add_body_to_query(body,body_shape,area_shape);
+
+ } else {
+
+ if (area->get_space_override_mode()!=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED)
+ body->remove_area(area);
+ if (area->has_monitor_callback())
+ area->remove_body_from_query(body,body_shape,area_shape);
+
+ }
+
+ colliding=result;
+
+ }
+
+ return false; //never do any post solving
+}
+
+void AreaPair2DSW::solve(float p_step) {
+
+
+}
+
+
+AreaPair2DSW::AreaPair2DSW(Body2DSW *p_body,int p_body_shape, Area2DSW *p_area,int p_area_shape) {
+
+
+ body=p_body;
+ area=p_area;
+ body_shape=p_body_shape;
+ area_shape=p_area_shape;
+ colliding=false;
+ body->add_constraint(this,0);
+ area->add_constraint(this);
+
+}
+
+AreaPair2DSW::~AreaPair2DSW() {
+
+ if (colliding) {
+
+ if (area->get_space_override_mode()!=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED)
+ body->remove_area(area);
+ if (area->has_monitor_callback())
+ area->remove_body_from_query(body,body_shape,area_shape);
+
+
+ }
+ body->remove_constraint(this);
+ area->remove_constraint(this);
+}
diff --git a/servers/physics_2d/area_pair_2d_sw.h b/servers/physics_2d/area_pair_2d_sw.h
new file mode 100644
index 0000000000..5dad8c7a12
--- /dev/null
+++ b/servers/physics_2d/area_pair_2d_sw.h
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* area_pair_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 AREA_PAIR_2D_SW_H
+#define AREA_PAIR_2D_SW_H
+
+#include "constraint_2d_sw.h"
+#include "body_2d_sw.h"
+#include "area_2d_sw.h"
+
+class AreaPair2DSW : public Constraint2DSW {
+
+ Body2DSW *body;
+ Area2DSW *area;
+ int body_shape;
+ int area_shape;
+ bool colliding;
+public:
+
+ bool setup(float p_step);
+ void solve(float p_step);
+
+ AreaPair2DSW(Body2DSW *p_body,int p_body_shape, Area2DSW *p_area,int p_area_shape);
+ ~AreaPair2DSW();
+};
+
+#endif // AREA_PAIR_2D_SW_H
+
diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp
new file mode 100644
index 0000000000..f0e96ae5a6
--- /dev/null
+++ b/servers/physics_2d/body_2d_sw.cpp
@@ -0,0 +1,609 @@
+/*************************************************************************/
+/* body_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "body_2d_sw.h"
+#include "space_2d_sw.h"
+#include "area_2d_sw.h"
+
+void Body2DSW::_update_inertia() {
+
+ if (get_space() && !inertia_update_list.in_list())
+ get_space()->body_add_to_inertia_update_list(&inertia_update_list);
+
+}
+
+
+
+void Body2DSW::update_inertias() {
+
+ //update shapes and motions
+
+ switch(mode) {
+
+ case Physics2DServer::BODY_MODE_RIGID: {
+
+ //update tensor for allshapes, not the best way but should be somehow OK. (inspired from bullet)
+ float total_area=0;
+
+ for (int i=0;i<get_shape_count();i++) {
+
+ total_area+=get_shape_aabb(i).get_area();
+ }
+
+ real_t _inertia=0;
+
+ for (int i=0;i<get_shape_count();i++) {
+
+ const Shape2DSW* shape=get_shape(i);
+
+ float area=get_shape_aabb(i).get_area();
+
+ float mass = area * this->mass / total_area;
+
+ _inertia += shape->get_moment_of_inertia(mass) + mass * get_shape_transform(i).get_origin().length_squared();
+
+ }
+
+ if (_inertia!=0)
+ _inv_inertia=1.0/_inertia;
+ else
+ _inv_inertia=0.0; //wathever
+
+ if (mass)
+ _inv_mass=1.0/mass;
+ else
+ _inv_mass=0;
+
+ } break;
+ case Physics2DServer::BODY_MODE_STATIC_ACTIVE:
+ case Physics2DServer::BODY_MODE_STATIC: {
+
+ _inv_inertia=0;
+ _inv_mass=0;
+ } break;
+ case Physics2DServer::BODY_MODE_CHARACTER: {
+
+ _inv_inertia=0;
+ _inv_mass=1.0/mass;
+
+ } break;
+ }
+ //_update_inertia_tensor();
+
+ //_update_shapes();
+
+}
+
+
+
+void Body2DSW::set_active(bool p_active) {
+
+ if (active==p_active)
+ return;
+
+ active=p_active;
+ if (!p_active) {
+ if (get_space())
+ get_space()->body_remove_from_active_list(&active_list);
+ } else {
+ if (mode==Physics2DServer::BODY_MODE_STATIC)
+ return; //static bodies can't become active
+ if (get_space())
+ get_space()->body_add_to_active_list(&active_list);
+
+ //still_time=0;
+ }
+/*
+ if (!space)
+ return;
+
+ for(int i=0;i<get_shape_count();i++) {
+ Shape &s=shapes[i];
+ if (s.bpid>0) {
+ get_space()->get_broadphase()->set_active(s.bpid,active);
+ }
+ }
+*/
+}
+
+
+
+void Body2DSW::set_param(Physics2DServer::BodyParameter p_param, float p_value) {
+
+ switch(p_param) {
+ case Physics2DServer::BODY_PARAM_BOUNCE: {
+
+ bounce=p_value;
+ } break;
+ case Physics2DServer::BODY_PARAM_FRICTION: {
+
+ friction=p_value;
+ } break;
+ case Physics2DServer::BODY_PARAM_MASS: {
+ ERR_FAIL_COND(p_value<=0);
+ mass=p_value;
+ _update_inertia();
+
+ } break;
+ default:{}
+ }
+}
+
+float Body2DSW::get_param(Physics2DServer::BodyParameter p_param) const {
+
+ switch(p_param) {
+ case Physics2DServer::BODY_PARAM_BOUNCE: {
+
+ return bounce;
+ } break;
+ case Physics2DServer::BODY_PARAM_FRICTION: {
+
+ return friction;
+ } break;
+ case Physics2DServer::BODY_PARAM_MASS: {
+ return mass;
+ } break;
+ default:{}
+ }
+
+ return 0;
+}
+
+void Body2DSW::set_mode(Physics2DServer::BodyMode p_mode) {
+
+ mode=p_mode;
+
+ switch(p_mode) {
+ //CLEAR UP EVERYTHING IN CASE IT NOT WORKS!
+ case Physics2DServer::BODY_MODE_STATIC:
+ case Physics2DServer::BODY_MODE_STATIC_ACTIVE: {
+
+ _set_inv_transform(get_transform().affine_inverse());
+ _inv_mass=0;
+ _set_static(p_mode==Physics2DServer::BODY_MODE_STATIC);
+ set_active(p_mode==Physics2DServer::BODY_MODE_STATIC_ACTIVE);
+ linear_velocity=Vector2();
+ angular_velocity=0;
+ } break;
+ case Physics2DServer::BODY_MODE_RIGID: {
+
+ _inv_mass=mass>0?(1.0/mass):0;
+ _set_static(false);
+ simulated_motion=false; //jic
+
+ } break;
+ case Physics2DServer::BODY_MODE_CHARACTER: {
+
+ _inv_mass=mass>0?(1.0/mass):0;
+ _set_static(false);
+ simulated_motion=false; //jic
+ } break;
+ }
+
+ _update_inertia();
+ //if (get_space())
+// _update_queries();
+
+}
+Physics2DServer::BodyMode Body2DSW::get_mode() const {
+
+ return mode;
+}
+
+void Body2DSW::_shapes_changed() {
+
+ _update_inertia();
+ wakeup_neighbours();
+}
+
+void Body2DSW::set_state(Physics2DServer::BodyState p_state, const Variant& p_variant) {
+
+ switch(p_state) {
+ case Physics2DServer::BODY_STATE_TRANSFORM: {
+
+
+ if (mode==Physics2DServer::BODY_MODE_STATIC || mode==Physics2DServer::BODY_MODE_STATIC_ACTIVE) {
+ _set_transform(p_variant);
+ _set_inv_transform(get_transform().affine_inverse());
+ wakeup_neighbours();
+ } else {
+ Matrix32 t = p_variant;
+ t.orthonormalize();
+ _set_transform(t);
+ _set_inv_transform(get_transform().inverse());
+
+ }
+
+ } break;
+ case Physics2DServer::BODY_STATE_LINEAR_VELOCITY: {
+
+ //if (mode==Physics2DServer::BODY_MODE_STATIC)
+ // break;
+ linear_velocity=p_variant;
+
+ } break;
+ case Physics2DServer::BODY_STATE_ANGULAR_VELOCITY: {
+ //if (mode!=Physics2DServer::BODY_MODE_RIGID)
+ // break;
+ angular_velocity=p_variant;
+
+ } break;
+ case Physics2DServer::BODY_STATE_SLEEPING: {
+ //?
+ if (mode==Physics2DServer::BODY_MODE_STATIC || mode==Physics2DServer::BODY_MODE_STATIC_ACTIVE)
+ break;
+ bool do_sleep=p_variant;
+ if (do_sleep) {
+ linear_velocity=Vector2();
+ //biased_linear_velocity=Vector3();
+ angular_velocity=0;
+ //biased_angular_velocity=Vector3();
+ set_active(false);
+ } else {
+ if (mode!=Physics2DServer::BODY_MODE_STATIC)
+ set_active(true);
+ }
+ } break;
+ case Physics2DServer::BODY_STATE_CAN_SLEEP: {
+ can_sleep=p_variant;
+ if (mode==Physics2DServer::BODY_MODE_RIGID && !active && !can_sleep)
+ set_active(true);
+
+ } break;
+ }
+
+}
+Variant Body2DSW::get_state(Physics2DServer::BodyState p_state) const {
+
+ switch(p_state) {
+ case Physics2DServer::BODY_STATE_TRANSFORM: {
+ return get_transform();
+ } break;
+ case Physics2DServer::BODY_STATE_LINEAR_VELOCITY: {
+ return linear_velocity;
+ } break;
+ case Physics2DServer::BODY_STATE_ANGULAR_VELOCITY: {
+ return angular_velocity;
+ } break;
+ case Physics2DServer::BODY_STATE_SLEEPING: {
+ return !is_active();
+ } break;
+ case Physics2DServer::BODY_STATE_CAN_SLEEP: {
+ return can_sleep;
+ } break;
+ }
+
+ return Variant();
+}
+
+
+void Body2DSW::set_space(Space2DSW *p_space){
+
+ if (get_space()) {
+
+ wakeup_neighbours();
+
+ if (inertia_update_list.in_list())
+ get_space()->body_remove_from_inertia_update_list(&inertia_update_list);
+ if (active_list.in_list())
+ get_space()->body_remove_from_active_list(&active_list);
+ if (direct_state_query_list.in_list())
+ get_space()->body_remove_from_state_query_list(&direct_state_query_list);
+
+ }
+
+ _set_space(p_space);
+
+ if (get_space()) {
+
+ _update_inertia();
+ if (active)
+ get_space()->body_add_to_active_list(&active_list);
+// _update_queries();
+ //if (is_active()) {
+ // active=false;
+ // set_active(true);
+ //}
+
+ }
+
+}
+
+void Body2DSW::_compute_area_gravity(const Area2DSW *p_area) {
+
+ if (p_area->is_gravity_point()) {
+
+ gravity = (p_area->get_transform().get_origin()+p_area->get_gravity_vector() - get_transform().get_origin()).normalized() * p_area->get_gravity();
+
+ } else {
+ gravity = p_area->get_gravity_vector() * p_area->get_gravity();
+ }
+}
+
+void Body2DSW::integrate_forces(real_t p_step) {
+
+ if (mode==Physics2DServer::BODY_MODE_STATIC || mode==Physics2DServer::BODY_MODE_STATIC_ACTIVE)
+ return;
+
+ Area2DSW *current_area = get_space()->get_default_area();
+ ERR_FAIL_COND(!current_area);
+
+ int prio = current_area->get_priority();
+ int ac = areas.size();
+ if (ac) {
+ const AreaCMP *aa = &areas[0];
+ for(int i=0;i<ac;i++) {
+ if (aa[i].area->get_priority() > prio) {
+ current_area=aa[i].area;
+ prio=current_area->get_priority();
+ }
+ }
+ }
+
+ _compute_area_gravity(current_area);
+ density=current_area->get_density();
+
+ if (!omit_force_integration) {
+ //overriden by direct state query
+
+ Vector2 force=gravity*mass;
+ force+=applied_force;
+ real_t torque=applied_torque;
+
+ real_t damp = 1.0 - p_step * density;
+
+ if (damp<0) // reached zero in the given time
+ damp=0;
+
+ real_t angular_damp = 1.0 - p_step * density * get_space()->get_body_angular_velocity_damp_ratio();
+
+ if (angular_damp<0) // reached zero in the given time
+ angular_damp=0;
+
+ linear_velocity*=damp;
+ angular_velocity*=angular_damp;
+
+ linear_velocity+=_inv_mass * force * p_step;
+ angular_velocity+=_inv_inertia * torque * p_step;
+ }
+
+
+ //motion=linear_velocity*p_step;
+
+ biased_angular_velocity=0;
+ biased_linear_velocity=Vector2();
+
+ if (continuous_cd) //shapes temporarily extend for raycast
+ _update_shapes_with_motion(linear_velocity*p_step);
+
+ current_area=NULL; // clear the area, so it is set in the next frame
+ contact_count=0;
+
+}
+
+void Body2DSW::integrate_velocities(real_t p_step) {
+
+ if (mode==Physics2DServer::BODY_MODE_STATIC)
+ return;
+ if (mode==Physics2DServer::BODY_MODE_STATIC_ACTIVE) {
+ if (fi_callback)
+ get_space()->body_add_to_state_query_list(&direct_state_query_list);
+ return;
+ }
+
+ real_t total_angular_velocity = angular_velocity+biased_angular_velocity;
+ Vector2 total_linear_velocity=linear_velocity+biased_linear_velocity;
+
+ real_t angle = get_transform().get_rotation() - total_angular_velocity * p_step;
+ Vector2 pos = get_transform().get_origin() + total_linear_velocity * p_step;
+
+
+ _set_transform(Matrix32(angle,pos));
+ _set_inv_transform(get_transform().inverse());
+
+
+ if (fi_callback) {
+
+ get_space()->body_add_to_state_query_list(&direct_state_query_list);
+ }
+
+ //_update_inertia_tensor();
+}
+
+
+void Body2DSW::simulate_motion(const Matrix32& p_xform,real_t p_step) {
+
+ Matrix32 inv_xform = p_xform.affine_inverse();
+ if (!get_space()) {
+ _set_transform(p_xform);
+ _set_inv_transform(inv_xform);
+
+ return;
+ }
+
+
+
+ linear_velocity = (p_xform.elements[2] - get_transform().elements[2])/p_step;
+
+ real_t rot = inv_xform.basis_xform(get_transform().elements[1]).atan2();
+ angular_velocity = rot / p_step;
+
+ if (!direct_state_query_list.in_list())// - callalways, so lv and av are cleared && (state_query || direct_state_query))
+ get_space()->body_add_to_state_query_list(&direct_state_query_list);
+ simulated_motion=true;
+ _set_transform(p_xform);
+
+
+}
+
+void Body2DSW::wakeup_neighbours() {
+
+
+
+ for(Map<Constraint2DSW*,int>::Element *E=constraint_map.front();E;E=E->next()) {
+
+ const Constraint2DSW *c=E->key();
+ Body2DSW **n = c->get_body_ptr();
+ int bc=c->get_body_count();
+
+ for(int i=0;i<bc;i++) {
+
+ if (i==E->get())
+ continue;
+ Body2DSW *b = n[i];
+ if (b->mode!=Physics2DServer::BODY_MODE_RIGID)
+ continue;
+
+ if (!b->is_active())
+ b->set_active(true);
+ }
+ }
+}
+
+void Body2DSW::call_queries() {
+
+
+ if (fi_callback) {
+
+ Physics2DDirectBodyStateSW *dbs = Physics2DDirectBodyStateSW::singleton;
+ dbs->body=this;
+
+ Variant v=dbs;
+ const Variant *vp[2]={&v,&fi_callback->callback_udata};
+
+ Object *obj = ObjectDB::get_instance(fi_callback->id);
+ if (!obj) {
+
+ set_force_integration_callback(NULL,StringName());
+ } else {
+ Variant::CallError ce;
+ if (fi_callback->callback_udata.get_type()) {
+
+ obj->call(fi_callback->method,vp,2,ce);
+
+ } else {
+ obj->call(fi_callback->method,vp,1,ce);
+ }
+ }
+
+
+ }
+
+ if (simulated_motion) {
+
+ // linear_velocity=Vector2();
+ // angular_velocity=0;
+ simulated_motion=false;
+ }
+}
+
+
+bool Body2DSW::sleep_test(real_t p_step) {
+
+ if (mode==Physics2DServer::BODY_MODE_STATIC || mode==Physics2DServer::BODY_MODE_STATIC_ACTIVE)
+ return true; //
+ else if (mode==Physics2DServer::BODY_MODE_CHARACTER)
+ return !active; // characters don't sleep unless asked to sleep
+ else if (!can_sleep)
+ return false;
+
+
+
+
+ if (Math::abs(angular_velocity)<get_space()->get_body_angular_velocity_sleep_treshold() && Math::abs(linear_velocity.length_squared()) < get_space()->get_body_linear_velocity_sleep_treshold()*get_space()->get_body_linear_velocity_sleep_treshold()) {
+
+ still_time+=p_step;
+
+ return still_time > get_space()->get_body_time_to_sleep();
+ } else {
+
+ still_time=0; //maybe this should be set to 0 on set_active?
+ return false;
+ }
+}
+
+
+void Body2DSW::set_force_integration_callback(ObjectID p_id,const StringName& p_method,const Variant& p_udata) {
+
+ if (fi_callback) {
+
+ memdelete(fi_callback);
+ fi_callback=NULL;
+ }
+
+
+ if (p_id!=0) {
+
+ fi_callback=memnew(ForceIntegrationCallback);
+ fi_callback->id=p_id;
+ fi_callback->method=p_method;
+ fi_callback->callback_udata=p_udata;
+ }
+
+}
+
+Body2DSW::Body2DSW() : CollisionObject2DSW(TYPE_BODY), active_list(this), inertia_update_list(this), direct_state_query_list(this) {
+
+
+ mode=Physics2DServer::BODY_MODE_RIGID;
+ active=true;
+ angular_velocity=0;
+ biased_angular_velocity=0;
+ mass=1;
+ _inv_inertia=0;
+ _inv_mass=1;
+ bounce=0;
+ friction=1;
+ omit_force_integration=false;
+ applied_torque=0;
+ island_step=0;
+ island_next=NULL;
+ island_list_next=NULL;
+ _set_static(false);
+ density=0;
+ contact_count=0;
+ simulated_motion=false;
+ still_time=0;
+ continuous_cd=false;
+ can_sleep=false;
+ fi_callback=NULL;
+
+}
+
+Body2DSW::~Body2DSW() {
+
+ if (fi_callback)
+ memdelete(fi_callback);
+}
+
+Physics2DDirectBodyStateSW *Physics2DDirectBodyStateSW::singleton=NULL;
+
+Physics2DDirectSpaceState* Physics2DDirectBodyStateSW::get_space_state() {
+
+ return body->get_space()->get_direct_state();
+}
diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h
new file mode 100644
index 0000000000..55b84ce7a7
--- /dev/null
+++ b/servers/physics_2d/body_2d_sw.h
@@ -0,0 +1,334 @@
+/*************************************************************************/
+/* body_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef BODY_2D_SW_H
+#define BODY_2D_SW_H
+
+#include "collision_object_2d_sw.h"
+#include "vset.h"
+#include "area_2d_sw.h"
+
+class Constraint2DSW;
+
+
+class Body2DSW : public CollisionObject2DSW {
+
+
+ Physics2DServer::BodyMode mode;
+
+ Vector2 biased_linear_velocity;
+ real_t biased_angular_velocity;
+
+ Vector2 linear_velocity;
+ real_t angular_velocity;
+
+ real_t mass;
+ real_t bounce;
+ real_t friction;
+
+ real_t _inv_mass;
+ real_t _inv_inertia;
+
+ Vector2 gravity;
+ real_t density;
+
+ real_t still_time;
+
+ Vector2 applied_force;
+ real_t applied_torque;
+
+ SelfList<Body2DSW> active_list;
+ SelfList<Body2DSW> inertia_update_list;
+ SelfList<Body2DSW> direct_state_query_list;
+
+ VSet<RID> exceptions;
+ bool omit_force_integration;
+ bool active;
+ bool simulated_motion;
+ bool continuous_cd;
+ bool can_sleep;
+ void _update_inertia();
+ virtual void _shapes_changed();
+
+
+ Map<Constraint2DSW*,int> constraint_map;
+
+ struct AreaCMP {
+
+ Area2DSW *area;
+ _FORCE_INLINE_ bool operator<(const AreaCMP& p_cmp) const { return area->get_self() < p_cmp.area->get_self() ; }
+ _FORCE_INLINE_ AreaCMP() {}
+ _FORCE_INLINE_ AreaCMP(Area2DSW *p_area) { area=p_area;}
+ };
+
+
+ VSet<AreaCMP> areas;
+
+ struct Contact {
+
+
+ Vector2 local_pos;
+ Vector2 local_normal;
+ float depth;
+ int local_shape;
+ Vector2 collider_pos;
+ int collider_shape;
+ ObjectID collider_instance_id;
+ RID collider;
+ Vector2 collider_velocity_at_pos;
+ };
+
+ Vector<Contact> contacts; //no contacts by default
+ int contact_count;
+
+ struct ForceIntegrationCallback {
+
+ ObjectID id;
+ StringName method;
+ Variant callback_udata;
+ };
+
+ ForceIntegrationCallback *fi_callback;
+
+
+ uint64_t island_step;
+ Body2DSW *island_next;
+ Body2DSW *island_list_next;
+
+ _FORCE_INLINE_ void _compute_area_gravity(const Area2DSW *p_area);
+
+friend class Physics2DDirectBodyStateSW; // i give up, too many functions to expose
+
+public:
+
+
+ void set_force_integration_callback(ObjectID p_id, const StringName& p_method, const Variant &p_udata=Variant());
+
+
+ _FORCE_INLINE_ void add_area(Area2DSW *p_area) { areas.insert(AreaCMP(p_area)); }
+ _FORCE_INLINE_ void remove_area(Area2DSW *p_area) { areas.erase(AreaCMP(p_area)); }
+
+ _FORCE_INLINE_ void set_max_contacts_reported(int p_size) { contacts.resize(p_size); contact_count=0; }
+ _FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); }
+
+ _FORCE_INLINE_ bool can_report_contacts() const { return !contacts.empty(); }
+ _FORCE_INLINE_ void add_contact(const Vector2& p_local_pos,const Vector2& p_local_normal, float p_depth, int p_local_shape, const Vector2& p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID& p_collider,const Vector2& p_collider_velocity_at_pos);
+
+
+ _FORCE_INLINE_ void add_exception(const RID& p_exception) { exceptions.insert(p_exception);}
+ _FORCE_INLINE_ void remove_exception(const RID& p_exception) { exceptions.erase(p_exception);}
+ _FORCE_INLINE_ bool has_exception(const RID& p_exception) const { return exceptions.has(p_exception);}
+ _FORCE_INLINE_ const VSet<RID>& get_exceptions() const { return exceptions;}
+
+ _FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
+ _FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step=p_step; }
+
+ _FORCE_INLINE_ Body2DSW* get_island_next() const { return island_next; }
+ _FORCE_INLINE_ void set_island_next(Body2DSW* p_next) { island_next=p_next; }
+
+ _FORCE_INLINE_ Body2DSW* get_island_list_next() const { return island_list_next; }
+ _FORCE_INLINE_ void set_island_list_next(Body2DSW* p_next) { island_list_next=p_next; }
+
+ _FORCE_INLINE_ void add_constraint(Constraint2DSW* p_constraint, int p_pos) { constraint_map[p_constraint]=p_pos; }
+ _FORCE_INLINE_ void remove_constraint(Constraint2DSW* p_constraint) { constraint_map.erase(p_constraint); }
+ const Map<Constraint2DSW*,int>& get_constraint_map() const { return constraint_map; }
+
+ _FORCE_INLINE_ void set_omit_force_integration(bool p_omit_force_integration) { omit_force_integration=p_omit_force_integration; }
+ _FORCE_INLINE_ bool get_omit_force_integration() const { return omit_force_integration; }
+
+ _FORCE_INLINE_ void set_linear_velocity(const Vector2& p_velocity) {linear_velocity=p_velocity; }
+ _FORCE_INLINE_ Vector2 get_linear_velocity() const { return linear_velocity; }
+
+ _FORCE_INLINE_ void set_angular_velocity(real_t p_velocity) { angular_velocity=p_velocity; }
+ _FORCE_INLINE_ real_t get_angular_velocity() const { return angular_velocity; }
+
+ _FORCE_INLINE_ void set_biased_linear_velocity(const Vector2& p_velocity) {biased_linear_velocity=p_velocity; }
+ _FORCE_INLINE_ Vector2 get_biased_linear_velocity() const { return biased_linear_velocity; }
+
+ _FORCE_INLINE_ void set_biased_angular_velocity(real_t p_velocity) { biased_angular_velocity=p_velocity; }
+ _FORCE_INLINE_ real_t get_biased_angular_velocity() const { return biased_angular_velocity; }
+
+ _FORCE_INLINE_ void apply_impulse(const Vector2& p_pos, const Vector2& p_j) {
+
+ linear_velocity += p_j * _inv_mass;
+ angular_velocity += _inv_inertia * p_pos.cross(p_j);
+ }
+
+ _FORCE_INLINE_ void apply_bias_impulse(const Vector2& p_pos, const Vector2& p_j) {
+
+ biased_linear_velocity += p_j * _inv_mass;
+ biased_angular_velocity += _inv_inertia * p_pos.cross(p_j);
+ }
+
+ void set_active(bool p_active);
+ _FORCE_INLINE_ bool is_active() const { return active; }
+
+ void set_param(Physics2DServer::BodyParameter p_param, float);
+ float get_param(Physics2DServer::BodyParameter p_param) const;
+
+ void set_mode(Physics2DServer::BodyMode p_mode);
+ Physics2DServer::BodyMode get_mode() const;
+
+ void set_state(Physics2DServer::BodyState p_state, const Variant& p_variant);
+ Variant get_state(Physics2DServer::BodyState p_state) const;
+
+ void set_applied_force(const Vector2& p_force) { applied_force=p_force; }
+ Vector2 get_applied_force() const { return applied_force; }
+
+ void set_applied_torque(real_t p_torque) { applied_torque=p_torque; }
+ real_t get_applied_torque() const { return applied_torque; }
+
+ _FORCE_INLINE_ void set_continuous_collision_detection(bool p_enable) { continuous_cd=p_enable; }
+ _FORCE_INLINE_ bool is_continuous_collision_detection_enabled() const { return continuous_cd; }
+
+ void set_space(Space2DSW *p_space);
+
+ void update_inertias();
+
+ _FORCE_INLINE_ real_t get_inv_mass() const { return _inv_mass; }
+ _FORCE_INLINE_ real_t get_inv_inertia() const { return _inv_inertia; }
+ _FORCE_INLINE_ real_t get_friction() const { return friction; }
+ _FORCE_INLINE_ Vector2 get_gravity() const { return gravity; }
+ _FORCE_INLINE_ real_t get_density() const { return density; }
+
+ void integrate_forces(real_t p_step);
+ void integrate_velocities(real_t p_step);
+
+ void simulate_motion(const Matrix32& p_xform,real_t p_step);
+ void call_queries();
+ void wakeup_neighbours();
+
+ bool sleep_test(real_t p_step);
+
+ Body2DSW();
+ ~Body2DSW();
+
+};
+
+
+//add contact inline
+
+void Body2DSW::add_contact(const Vector2& p_local_pos,const Vector2& p_local_normal, float p_depth, int p_local_shape, const Vector2& p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID& p_collider,const Vector2& p_collider_velocity_at_pos) {
+
+ int c_max=contacts.size();
+
+ if (c_max==0)
+ return;
+
+ Contact *c = &contacts[0];
+
+
+ int idx=-1;
+
+ if (contact_count<c_max) {
+ idx=contact_count++;
+ } else {
+
+ float least_depth=1e20;
+ int least_deep=-1;
+ for(int i=0;i<c_max;i++) {
+
+ if (i==0 || c[i].depth<least_depth) {
+ least_deep=i;
+ least_depth=c[i].depth;
+ }
+ }
+
+ if (least_deep>=0 && least_depth<p_depth) {
+
+ idx=least_deep;
+ }
+ if (idx==-1)
+ return; //none least deepe than this
+ }
+
+ c[idx].local_pos=p_local_pos;
+ c[idx].local_normal=p_local_normal;
+ c[idx].depth=p_depth;
+ c[idx].local_shape=p_local_shape;
+ c[idx].collider_pos=p_collider_pos;
+ c[idx].collider_shape=p_collider_shape;
+ c[idx].collider_instance_id=p_collider_instance_id;
+ c[idx].collider=p_collider;
+ c[idx].collider_velocity_at_pos=p_collider_velocity_at_pos;
+
+}
+
+
+class Physics2DDirectBodyStateSW : public Physics2DDirectBodyState {
+
+ OBJ_TYPE( Physics2DDirectBodyStateSW, Physics2DDirectBodyState );
+
+public:
+
+ static Physics2DDirectBodyStateSW *singleton;
+ Body2DSW *body;
+ real_t step;
+
+ virtual Vector2 get_total_gravity() const { return body->get_gravity(); } // get gravity vector working on this body space/area
+ virtual float get_total_density() const { return body->get_density(); } // get density of this body space/area
+
+ virtual float get_inverse_mass() const { return body->get_inv_mass(); } // get the mass
+ virtual real_t get_inverse_inertia() const { return body->get_inv_inertia(); } // get density of this body space
+
+ virtual void set_linear_velocity(const Vector2& p_velocity) { body->set_linear_velocity(p_velocity); }
+ virtual Vector2 get_linear_velocity() const { return body->get_linear_velocity(); }
+
+ virtual void set_angular_velocity(real_t p_velocity) { body->set_angular_velocity(p_velocity); }
+ virtual real_t get_angular_velocity() const { return body->get_angular_velocity(); }
+
+ virtual void set_transform(const Matrix32& p_transform) { body->set_state(Physics2DServer::BODY_STATE_TRANSFORM,p_transform); }
+ virtual Matrix32 get_transform() const { return body->get_transform(); }
+
+ virtual void set_sleep_state(bool p_enable) { body->set_active(!p_enable); }
+ virtual bool is_sleeping() const { return !body->is_active(); }
+
+ virtual int get_contact_count() const { return body->contact_count; }
+
+ virtual Vector2 get_contact_local_pos(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector2());
+ return body->contacts[p_contact_idx].local_pos;
+ }
+ virtual Vector2 get_contact_local_normal(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector2()); return body->contacts[p_contact_idx].local_normal; }
+ virtual int get_contact_local_shape(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,-1); return body->contacts[p_contact_idx].local_shape; }
+
+ virtual RID get_contact_collider(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,RID()); return body->contacts[p_contact_idx].collider; }
+ virtual Vector2 get_contact_collider_pos(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector2()); return body->contacts[p_contact_idx].collider_pos; }
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,0); return body->contacts[p_contact_idx].collider_instance_id; }
+ virtual int get_contact_collider_shape(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,0); return body->contacts[p_contact_idx].collider_shape; }
+ virtual Vector2 get_contact_collider_velocity_at_pos(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx,body->contact_count,Vector2()); return body->contacts[p_contact_idx].collider_velocity_at_pos; }
+
+ virtual Physics2DDirectSpaceState* get_space_state();
+
+
+ virtual real_t get_step() const { return step; }
+ Physics2DDirectBodyStateSW() { singleton=this; body=NULL; }
+};
+
+
+#endif // BODY_2D_SW_H
diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp
new file mode 100644
index 0000000000..6d8215840a
--- /dev/null
+++ b/servers/physics_2d/body_pair_2d_sw.cpp
@@ -0,0 +1,435 @@
+/*************************************************************************/
+/* body_pair_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "body_pair_2d_sw.h"
+#include "collision_solver_2d_sw.h"
+#include "space_2d_sw.h"
+
+
+#define POSITION_CORRECTION
+#define ACCUMULATE_IMPULSES
+
+void BodyPair2DSW::_add_contact(const Vector2& p_point_A,const Vector2& p_point_B,void *p_self) {
+
+ BodyPair2DSW *self = (BodyPair2DSW *)p_self;
+
+ self->_contact_added_callback(p_point_A,p_point_B);
+
+}
+
+void BodyPair2DSW::_contact_added_callback(const Vector2& p_point_A,const Vector2& p_point_B) {
+
+ // check if we already have the contact
+
+
+ Vector2 local_A = A->get_inv_transform().basis_xform(p_point_A);
+ Vector2 local_B = B->get_inv_transform().basis_xform(p_point_B-offset_B);
+
+ int new_index = contact_count;
+
+ ERR_FAIL_COND( new_index >= (MAX_CONTACTS+1) );
+
+ Contact contact;
+
+ contact.acc_normal_impulse=0;
+ contact.acc_bias_impulse=0;
+ contact.acc_tangent_impulse=0;
+ contact.local_A=local_A;
+ contact.local_B=local_B;
+ contact.normal=(p_point_A-p_point_B).normalized();
+
+ // attempt to determine if the contact will be reused
+
+ real_t recycle_radius_2 = space->get_contact_recycle_radius() * space->get_contact_recycle_radius();
+
+ for (int i=0;i<contact_count;i++) {
+
+ Contact& c = contacts[i];
+ if (
+ c.local_A.distance_squared_to( local_A ) < (recycle_radius_2) &&
+ c.local_B.distance_squared_to( local_B ) < (recycle_radius_2) ) {
+
+ contact.acc_normal_impulse=c.acc_normal_impulse;
+ contact.acc_tangent_impulse=c.acc_tangent_impulse;
+ contact.acc_bias_impulse=c.acc_bias_impulse;
+ new_index=i;
+ break;
+ }
+ }
+
+ // figure out if the contact amount must be reduced to fit the new contact
+
+ if (new_index == MAX_CONTACTS) {
+
+ // remove the contact with the minimum depth
+
+ int least_deep=-1;
+ real_t min_depth=1e10;
+
+
+ for (int i=0;i<=contact_count;i++) {
+
+ Contact& c = (i==contact_count)?contact:contacts[i];
+ Vector2 global_A = A->get_transform().basis_xform(c.local_A);
+ Vector2 global_B = B->get_transform().basis_xform(c.local_B)+offset_B;
+
+ Vector2 axis = global_A - global_B;
+ float depth = axis.dot( c.normal );
+
+
+ if (depth<min_depth) {
+
+ min_depth=depth;
+ least_deep=i;
+ }
+ }
+
+ ERR_FAIL_COND(least_deep==-1);
+
+ if (least_deep < contact_count) { //replace the last deep contact by the new one
+
+ contacts[least_deep]=contact;
+ }
+
+ return;
+ }
+
+ contacts[new_index]=contact;
+
+ if (new_index==contact_count) {
+
+ contact_count++;
+ }
+
+}
+
+void BodyPair2DSW::_validate_contacts() {
+
+ //make sure to erase contacts that are no longer valid
+
+ real_t max_separation = space->get_contact_max_separation();
+ real_t max_separation2 = max_separation*max_separation;
+
+ for (int i=0;i<contact_count;i++) {
+
+ Contact& c = contacts[i];
+
+ Vector2 global_A = A->get_transform().basis_xform(c.local_A);
+ Vector2 global_B = B->get_transform().basis_xform(c.local_B)+offset_B;
+ Vector2 axis = global_A - global_B;
+ float depth = axis.dot( c.normal );
+
+ if (depth < -max_separation || (global_B + c.normal * depth - global_A).length_squared() > max_separation2) {
+ // contact no longer needed, remove
+
+
+ if ((i+1) < contact_count) {
+ // swap with the last one
+ SWAP( contacts[i], contacts[ contact_count-1 ] );
+
+ }
+
+ i--;
+ contact_count--;
+ }
+ }
+}
+
+
+bool BodyPair2DSW::_test_ccd(float p_step,Body2DSW *p_A, int p_shape_A,const Matrix32& p_xform_A,const Matrix32& p_xform_inv_A,Body2DSW *p_B, int p_shape_B,const Matrix32& p_xform_B,const Matrix32& p_xform_inv_B,bool p_swap_result) {
+
+ Vector2 motion = p_A->get_linear_velocity()*p_step;
+ real_t mlen = motion.length();
+ if (mlen<CMP_EPSILON)
+ return false;
+
+ Vector2 mnormal = motion / mlen;
+
+ real_t min,max;
+ p_A->get_shape(p_shape_A)->project_rangev(mnormal,p_xform_A,min,max);
+ if (mlen < (max-min)*0.3) //did it move enough in this direction to even attempt raycast? let's say it should move more than 1/3 the size of the object in that axis
+ return false;
+
+ //cast a segment from support in motion normal, in the same direction of motion by motion length
+ int a;
+ Vector2 s[2];
+ p_A->get_shape(p_shape_A)->get_supports(p_xform_A.basis_xform(mnormal).normalized(),s,a);
+ Vector2 from = p_xform_A.xform(s[0]);
+ Vector2 to = from + motion;
+
+ Vector2 local_from = p_xform_inv_B.xform(from);
+ Vector2 local_to = p_xform_inv_B.xform(to);
+
+ Vector2 rpos,rnorm;
+ if (!p_B->get_shape(p_shape_B)->intersect_segment(local_from,local_to,rpos,rnorm))
+ return false;
+
+ //ray hit something
+
+ Vector2 contact_A = to;
+ Vector2 contact_B = p_xform_B.xform(rpos);
+
+ //create a contact
+
+ if (p_swap_result)
+ _contact_added_callback(contact_B,contact_A);
+ else
+ _contact_added_callback(contact_A,contact_B);
+
+
+ return true;
+}
+
+bool BodyPair2DSW::setup(float p_step) {
+
+
+ //use local A coordinates to avoid numerical issues on collision detection
+ offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
+
+ _validate_contacts();
+
+ //cannot collide
+ if (A->is_shape_set_as_trigger(shape_A) || B->is_shape_set_as_trigger(shape_B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self()) || (A->get_mode()<=Physics2DServer::BODY_MODE_STATIC_ACTIVE && B->get_mode()<=Physics2DServer::BODY_MODE_STATIC_ACTIVE)) {
+ collided=false;
+
+ return false;
+ }
+ Vector2 offset_A = A->get_transform().get_origin();
+ Matrix32 xform_Au = A->get_transform().untranslated();
+ Matrix32 xform_A = xform_Au * A->get_shape_transform(shape_A);
+ Matrix32 xform_inv_A = xform_A.affine_inverse();
+
+ Matrix32 xform_Bu = B->get_transform();
+ xform_Bu.elements[2]-=A->get_transform().get_origin();
+ Matrix32 xform_B = xform_Bu * B->get_shape_transform(shape_B);
+ Matrix32 xform_inv_B = xform_B.affine_inverse();
+
+ Shape2DSW *shape_A_ptr=A->get_shape(shape_A);
+ Shape2DSW *shape_B_ptr=B->get_shape(shape_B);
+
+ collided = CollisionSolver2DSW::solve_static(shape_A_ptr,xform_A,xform_inv_A,shape_B_ptr,xform_B,xform_inv_B,_add_contact,this,&sep_axis);
+ if (!collided) {
+
+ //test ccd (currently just a raycast)
+ if (A->is_continuous_collision_detection_enabled() && A->get_type()>Physics2DServer::BODY_MODE_STATIC_ACTIVE) {
+ if (_test_ccd(p_step,A,shape_A,xform_A,xform_inv_A,B,shape_B,xform_B,xform_inv_B))
+ collided=true;
+ }
+
+ if (B->is_continuous_collision_detection_enabled() && B->get_type()>Physics2DServer::BODY_MODE_STATIC_ACTIVE) {
+ if (_test_ccd(p_step,B,shape_B,xform_B,xform_inv_B,A,shape_A,xform_A,xform_inv_A,true))
+ collided=true;
+ }
+
+ if (!collided)
+ return false;
+
+ }
+
+
+
+ real_t max_penetration = space->get_contact_max_allowed_penetration();
+
+ float bias = 0.3f;
+ if (shape_A_ptr->get_custom_bias() || shape_B_ptr->get_custom_bias()) {
+
+ if (shape_A_ptr->get_custom_bias()==0)
+ bias=shape_B_ptr->get_custom_bias();
+ else if (shape_B_ptr->get_custom_bias()==0)
+ bias=shape_A_ptr->get_custom_bias();
+ else
+ bias=(shape_B_ptr->get_custom_bias()+shape_A_ptr->get_custom_bias())*0.5;
+ }
+
+
+ cc=0;
+
+
+ real_t inv_dt = 1.0/p_step;
+ for (int i = 0; i < contact_count; i++) {
+
+ Contact& c = contacts[i];
+
+ Vector2 global_A = xform_Au.xform(c.local_A);
+ Vector2 global_B = xform_Bu.xform(c.local_B);
+
+ real_t depth = c.normal.dot(global_A - global_B);
+
+ if (depth<=0) {
+ c.active=false;
+ continue;
+ }
+
+ c.active=true;
+
+ int gather_A = A->can_report_contacts();
+ int gather_B = B->can_report_contacts();
+
+ c.rA = global_A;
+ c.rB = global_B-offset_B;
+
+ if (gather_A | gather_B) {
+
+ //Vector2 crB( -B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x );
+
+ global_A+=offset_A;
+ global_B+=offset_A;
+
+ if (gather_A) {
+ Vector2 crB( -B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x );
+ A->add_contact(global_A,-c.normal,depth,shape_A,global_B,shape_B,B->get_instance_id(),B->get_self(),crB+B->get_linear_velocity());
+ }
+ if (gather_B) {
+
+ Vector2 crA( -A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x );
+ B->add_contact(global_B,c.normal,depth,shape_B,global_A,shape_A,A->get_instance_id(),A->get_self(),crA+A->get_linear_velocity());
+ }
+ }
+
+
+ // Precompute normal mass, tangent mass, and bias.
+ real_t rnA = c.rA.dot(c.normal);
+ real_t rnB = c.rB.dot(c.normal);
+ real_t kNormal = A->get_inv_mass() + B->get_inv_mass();
+ kNormal += A->get_inv_inertia() * (c.rA.dot(c.rA) - rnA * rnA) + B->get_inv_inertia() * (c.rB.dot(c.rB) - rnB * rnB);
+ c.mass_normal = 1.0f / kNormal;
+
+ Vector2 tangent = c.normal.tangent();
+ real_t rtA = c.rA.dot(tangent);
+ real_t rtB = c.rB.dot(tangent);
+ real_t kTangent = A->get_inv_mass() + B->get_inv_mass();
+ kTangent += A->get_inv_inertia() * (c.rA.dot(c.rA) - rtA * rtA) + B->get_inv_inertia() * (c.rB.dot(c.rB) - rtB * rtB);
+ c.mass_tangent = 1.0f / kTangent;
+
+
+
+ c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration);
+ c.depth=depth;
+ //c.acc_bias_impulse=0;
+
+
+#ifdef ACCUMULATE_IMPULSES
+ {
+ // Apply normal + friction impulse
+ Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent;
+
+
+ A->apply_impulse(c.rA,-P);
+ B->apply_impulse(c.rB, P);
+ }
+
+#endif
+ }
+
+ return true;
+}
+
+void BodyPair2DSW::solve(float p_step) {
+
+ if (!collided)
+ return;
+
+ for (int i = 0; i < contact_count; ++i) {
+
+ Contact& c = contacts[i];
+ cc++;
+
+ if (!c.active)
+ continue;
+
+
+ // Relative velocity at contact
+
+ Vector2 crA( -A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x );
+ Vector2 crB( -B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x );
+ Vector2 dv = B->get_linear_velocity() + crB - A->get_linear_velocity() - crA;
+
+ Vector2 crbA( -A->get_biased_angular_velocity() * c.rA.y, A->get_biased_angular_velocity() * c.rA.x );
+ Vector2 crbB( -B->get_biased_angular_velocity() * c.rB.y, B->get_biased_angular_velocity() * c.rB.x );
+ Vector2 dbv = B->get_biased_linear_velocity() + crbB - A->get_biased_linear_velocity() - crbA;
+
+ real_t vn = dv.dot(c.normal);
+ real_t vbn = dbv.dot(c.normal);
+ Vector2 tangent = c.normal.tangent();
+ real_t vt = dv.dot(tangent);
+
+
+ real_t jbn = (c.bias - vbn)*c.mass_normal;
+ real_t jbnOld = c.acc_bias_impulse;
+ c.acc_bias_impulse = MAX(jbnOld + jbn, 0.0f);
+
+ Vector2 jb = c.normal * (c.acc_bias_impulse - jbnOld);
+
+ A->apply_bias_impulse(c.rA,-jb);
+ B->apply_bias_impulse(c.rB, jb);
+
+ real_t bounce=0;
+ real_t jn = -(bounce + vn)*c.mass_normal;
+ real_t jnOld = c.acc_normal_impulse;
+ c.acc_normal_impulse = MAX(jnOld + jn, 0.0f);
+
+
+ real_t friction = A->get_friction() * B->get_friction();
+
+ real_t jtMax = friction*c.acc_normal_impulse;
+ real_t jt = -vt*c.mass_tangent;
+ real_t jtOld = c.acc_tangent_impulse;
+ c.acc_tangent_impulse = CLAMP(jtOld + jt, -jtMax, jtMax);
+
+ Vector2 j =c.normal * (c.acc_normal_impulse - jnOld) + tangent * ( c.acc_tangent_impulse - jtOld );
+
+
+ A->apply_impulse(c.rA,-j);
+ B->apply_impulse(c.rB, j);
+
+
+ }
+}
+
+
+BodyPair2DSW::BodyPair2DSW(Body2DSW *p_A, int p_shape_A,Body2DSW *p_B, int p_shape_B) : Constraint2DSW(_arr,2) {
+
+ A=p_A;
+ B=p_B;
+ shape_A=p_shape_A;
+ shape_B=p_shape_B;
+ space=A->get_space();
+ A->add_constraint(this,0);
+ B->add_constraint(this,1);
+ contact_count=0;
+ collided=false;
+
+}
+
+
+BodyPair2DSW::~BodyPair2DSW() {
+
+ A->remove_constraint(this);
+ B->remove_constraint(this);
+
+}
diff --git a/servers/physics_2d/body_pair_2d_sw.h b/servers/physics_2d/body_pair_2d_sw.h
new file mode 100644
index 0000000000..26278f87cd
--- /dev/null
+++ b/servers/physics_2d/body_pair_2d_sw.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* body_pair_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef BODY_PAIR_2D_SW_H
+#define BODY_PAIR_2D_SW_H
+
+#include "body_2d_sw.h"
+#include "constraint_2d_sw.h"
+
+class BodyPair2DSW : public Constraint2DSW {
+
+ enum {
+ MAX_CONTACTS=2
+ };
+ union {
+ struct {
+ Body2DSW *A;
+ Body2DSW *B;
+ };
+
+ Body2DSW *_arr[2];
+ };
+
+ int shape_A;
+ int shape_B;
+
+ Space2DSW *space;
+
+ struct Contact {
+
+ Vector2 position;
+ Vector2 normal;
+ Vector2 local_A, local_B;
+ real_t acc_normal_impulse; // accumulated normal impulse (Pn)
+ real_t acc_tangent_impulse; // accumulated tangent impulse (Pt)
+ real_t acc_bias_impulse; // accumulated normal impulse for position bias (Pnb)
+ real_t mass_normal, mass_tangent;
+ real_t bias;
+
+ real_t depth;
+ bool active;
+ Vector2 rA,rB;
+ };
+
+ Vector2 offset_B; //use local A coordinates to avoid numerical issues on collision detection
+
+ Vector2 sep_axis;
+ Contact contacts[MAX_CONTACTS];
+ int contact_count;
+ bool collided;
+ int cc;
+
+
+ bool _test_ccd(float p_step,Body2DSW *p_A, int p_shape_A,const Matrix32& p_xform_A,const Matrix32& p_xform_inv_A,Body2DSW *p_B, int p_shape_B,const Matrix32& p_xform_B,const Matrix32& p_xform_inv_B,bool p_swap_result=false);
+ void _validate_contacts();
+ static void _add_contact(const Vector2& p_point_A,const Vector2& p_point_B,void *p_self);
+ _FORCE_INLINE_ void _contact_added_callback(const Vector2& p_point_A,const Vector2& p_point_B);
+
+public:
+
+ bool setup(float p_step);
+ void solve(float p_step);
+
+ BodyPair2DSW(Body2DSW *p_A, int p_shape_A,Body2DSW *p_B, int p_shape_B);
+ ~BodyPair2DSW();
+
+};
+
+#endif // BODY_PAIR_2D_SW_H
diff --git a/servers/physics_2d/broad_phase_2d_basic.cpp b/servers/physics_2d/broad_phase_2d_basic.cpp
new file mode 100644
index 0000000000..9641a986e8
--- /dev/null
+++ b/servers/physics_2d/broad_phase_2d_basic.cpp
@@ -0,0 +1,192 @@
+/*************************************************************************/
+/* broad_phase_2d_basic.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "broad_phase_2d_basic.h"
+
+ID BroadPhase2DBasic::create(CollisionObject2DSW *p_object_, int p_subindex) {
+
+
+ current++;
+
+ Element e;
+ e.owner=p_object_;
+ e._static=false;
+ e.subindex=p_subindex;
+
+ element_map[current]=e;
+ return current;
+}
+
+void BroadPhase2DBasic::move(ID p_id, const Rect2& p_aabb) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ E->get().aabb=p_aabb;
+
+}
+void BroadPhase2DBasic::set_static(ID p_id, bool p_static) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ E->get()._static=p_static;
+
+}
+void BroadPhase2DBasic::remove(ID p_id) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ element_map.erase(E);
+
+}
+
+CollisionObject2DSW *BroadPhase2DBasic::get_object(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,NULL);
+ return E->get().owner;
+
+}
+bool BroadPhase2DBasic::is_static(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,false);
+ return E->get()._static;
+
+}
+int BroadPhase2DBasic::get_subindex(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,-1);
+ return E->get().subindex;
+}
+
+int BroadPhase2DBasic::cull_segment(const Vector2& p_from, const Vector2& p_to,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices) {
+
+ int rc=0;
+
+ for (Map<ID,Element>::Element *E=element_map.front();E;E=E->next()) {
+
+ const Rect2 aabb=E->get().aabb;
+ if (aabb.intersects_segment(p_from,p_to)) {
+
+ p_results[rc]=E->get().owner;
+ p_result_indices[rc]=E->get().subindex;
+ rc++;
+ if (rc>=p_max_results)
+ break;
+ }
+ }
+
+ return rc;
+
+}
+int BroadPhase2DBasic::cull_aabb(const Rect2& p_aabb,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices) {
+
+ int rc=0;
+
+ for (Map<ID,Element>::Element *E=element_map.front();E;E=E->next()) {
+
+ const Rect2 aabb=E->get().aabb;
+ if (aabb.intersects(p_aabb)) {
+
+ p_results[rc]=E->get().owner;
+ p_result_indices[rc]=E->get().subindex;
+ rc++;
+ if (rc>=p_max_results)
+ break;
+ }
+ }
+
+ return rc;
+}
+
+void BroadPhase2DBasic::set_pair_callback(PairCallback p_pair_callback,void *p_userdata) {
+
+ pair_userdata=p_userdata;
+ pair_callback=p_pair_callback;
+
+}
+void BroadPhase2DBasic::set_unpair_callback(UnpairCallback p_pair_callback,void *p_userdata) {
+
+ unpair_userdata=p_userdata;
+ unpair_callback=p_pair_callback;
+
+}
+
+void BroadPhase2DBasic::update() {
+
+ // recompute pairs
+ for(Map<ID,Element>::Element *I=element_map.front();I;I=I->next()) {
+
+ for(Map<ID,Element>::Element *J=I->next();J;J=J->next()) {
+
+ Element *elem_A=&I->get();
+ Element *elem_B=&J->get();
+
+ if (elem_A->owner == elem_B->owner)
+ continue;
+
+
+ bool pair_ok=elem_A->aabb.intersects( elem_B->aabb ) && (!elem_A->_static || !elem_B->_static );
+
+ PairKey key(I->key(),J->key());
+
+ Map<PairKey,void*>::Element *E=pair_map.find(key);
+
+ if (!pair_ok && E) {
+ if (unpair_callback)
+ unpair_callback(elem_A->owner,elem_A->subindex,elem_B->owner,elem_B->subindex,E->get(),unpair_userdata);
+ pair_map.erase(key);
+ }
+
+ if (pair_ok && !E) {
+
+ void *data=NULL;
+ if (pair_callback)
+ data=pair_callback(elem_A->owner,elem_A->subindex,elem_B->owner,elem_B->subindex,unpair_userdata);
+ pair_map.insert(key,data);
+ }
+ }
+ }
+
+}
+
+BroadPhase2DSW *BroadPhase2DBasic::_create() {
+
+ return memnew( BroadPhase2DBasic );
+}
+
+BroadPhase2DBasic::BroadPhase2DBasic() {
+
+ current=1;
+ unpair_callback=NULL;
+ unpair_userdata=NULL;
+ pair_callback=NULL;
+ pair_userdata=NULL;
+
+}
diff --git a/servers/physics_2d/broad_phase_2d_basic.h b/servers/physics_2d/broad_phase_2d_basic.h
new file mode 100644
index 0000000000..ce15752251
--- /dev/null
+++ b/servers/physics_2d/broad_phase_2d_basic.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* broad_phase_2d_basic.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 BROAD_PHASE_2D_BASIC_H
+#define BROAD_PHASE_2D_BASIC_H
+
+#include "space_2d_sw.h"
+#include "map.h"
+class BroadPhase2DBasic : public BroadPhase2DSW {
+
+ struct Element {
+
+ CollisionObject2DSW *owner;
+ bool _static;
+ Rect2 aabb;
+ int subindex;
+ };
+
+
+ Map<ID,Element> element_map;
+
+ ID current;
+
+ struct PairKey {
+
+ union {
+ struct {
+ ID a;
+ ID b;
+ };
+ uint64_t key;
+ };
+
+ _FORCE_INLINE_ bool operator<(const PairKey& p_key) const {
+ return key < p_key.key;
+ }
+
+ PairKey() { key=0; }
+ PairKey(ID p_a, ID p_b) { if (p_a>p_b) { a=p_b; b=p_a; } else { a=p_a; b=p_b; }}
+
+ };
+
+ Map<PairKey,void*> pair_map;
+
+
+ PairCallback pair_callback;
+ void *pair_userdata;
+ UnpairCallback unpair_callback;
+ void *unpair_userdata;
+
+public:
+
+ // 0 is an invalid ID
+ virtual ID create(CollisionObject2DSW *p_object_, int p_subindex=0);
+ virtual void move(ID p_id, const Rect2& p_aabb);
+ virtual void set_static(ID p_id, bool p_static);
+ virtual void remove(ID p_id);
+
+ virtual CollisionObject2DSW *get_object(ID p_id) const;
+ virtual bool is_static(ID p_id) const;
+ virtual int get_subindex(ID p_id) const;
+
+ virtual int cull_segment(const Vector2& p_from, const Vector2& p_to,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices=NULL);
+ virtual int cull_aabb(const Rect2& p_aabb,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices=NULL);
+
+ virtual void set_pair_callback(PairCallback p_pair_callback,void *p_userdata);
+ virtual void set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata);
+
+ virtual void update();
+
+ static BroadPhase2DSW *_create();
+ BroadPhase2DBasic();
+};
+
+#endif // BROAD_PHASE_2D_BASIC_H
diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
new file mode 100644
index 0000000000..10da376dfd
--- /dev/null
+++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
@@ -0,0 +1,665 @@
+/*************************************************************************/
+/* broad_phase_2d_hash_grid.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "broad_phase_2d_hash_grid.h"
+#include "globals.h"
+
+void BroadPhase2DHashGrid::_pair_attempt(Element *p_elem, Element* p_with) {
+
+ Map<Element*,PairData*>::Element *E=p_elem->paired.find(p_with);
+
+ ERR_FAIL_COND(p_elem->_static && p_with->_static);
+
+ if (!E) {
+
+ PairData *pd = memnew( PairData );
+ p_elem->paired[p_with]=pd;
+ p_with->paired[p_elem]=pd;
+ } else {
+ E->get()->rc++;
+ }
+
+}
+
+void BroadPhase2DHashGrid::_unpair_attempt(Element *p_elem, Element* p_with) {
+
+ Map<Element*,PairData*>::Element *E=p_elem->paired.find(p_with);
+
+ ERR_FAIL_COND(!E); //this should really be paired..
+
+ E->get()->rc--;
+
+ if (E->get()->rc==0) {
+
+ if (E->get()->colliding) {
+ //uncollide
+ if (unpair_callback) {
+ unpair_callback(p_elem->owner,p_elem->subindex,p_with->owner,p_with->subindex,E->get()->ud,unpair_userdata);
+ }
+
+
+ }
+
+ memdelete(E->get());
+ p_elem->paired.erase(E);
+ p_with->paired.erase(p_elem);
+ }
+
+
+}
+
+void BroadPhase2DHashGrid::_check_motion(Element *p_elem) {
+
+ for (Map<Element*,PairData*>::Element *E=p_elem->paired.front();E;E=E->next()) {
+
+ bool pairing = p_elem->aabb.intersects( E->key()->aabb );
+
+ if (pairing!=E->get()->colliding) {
+
+ if (pairing) {
+
+ if (pair_callback) {
+ E->get()->ud=pair_callback(p_elem->owner,p_elem->subindex,E->key()->owner,E->key()->subindex,pair_userdata);
+ }
+ } else {
+
+ if (unpair_callback) {
+ unpair_callback(p_elem->owner,p_elem->subindex,E->key()->owner,E->key()->subindex,E->get()->ud,unpair_userdata);
+ }
+
+ }
+
+ E->get()->colliding=pairing;
+ }
+ }
+}
+
+void BroadPhase2DHashGrid::_enter_grid( Element* p_elem, const Rect2& p_rect,bool p_static) {
+
+
+ Point2i from = (p_rect.pos/cell_size).floor();
+ Point2i to = ((p_rect.pos+p_rect.size)/cell_size).floor();
+
+ for(int i=from.x;i<=to.x;i++) {
+
+
+ for(int j=from.y;j<=to.y;j++) {
+
+ PosKey pk;
+ pk.x=i;
+ pk.y=j;
+
+ uint32_t idx = pk.hash() % hash_table_size;
+ PosBin *pb = hash_table[idx];
+
+ while (pb) {
+
+ if (pb->key == pk) {
+ break;
+ }
+
+ pb=pb->next;
+ }
+
+
+ bool entered=false;
+
+ if (!pb) {
+ //does not exist, create!
+ pb = memnew( PosBin );
+ pb->key=pk;
+ pb->next=hash_table[idx];
+ hash_table[idx]=pb;
+ }
+
+
+
+ if (p_static) {
+ if (pb->static_object_set[p_elem].inc()==1) {
+ entered=true;
+ }
+ } else {
+ if (pb->object_set[p_elem].inc()==1) {
+
+ entered=true;
+ }
+ }
+
+ if (entered) {
+
+ for(Map<Element*,RC>::Element *E=pb->object_set.front();E;E=E->next()) {
+
+ if (E->key()->owner==p_elem->owner)
+ continue;
+ _pair_attempt(p_elem,E->key());
+ }
+
+ if (!p_static) {
+
+ for(Map<Element*,RC>::Element *E=pb->static_object_set.front();E;E=E->next()) {
+
+ if (E->key()->owner==p_elem->owner)
+ continue;
+ _pair_attempt(p_elem,E->key());
+ }
+ }
+ }
+
+ }
+
+ }
+
+
+}
+
+
+void BroadPhase2DHashGrid::_exit_grid( Element* p_elem, const Rect2& p_rect,bool p_static) {
+
+
+ Point2i from = (p_rect.pos/cell_size).floor();
+ Point2i to = ((p_rect.pos+p_rect.size)/cell_size).floor();
+
+ for(int i=from.x;i<=to.x;i++) {
+
+ for(int j=from.y;j<=to.y;j++) {
+
+ PosKey pk;
+ pk.x=i;
+ pk.y=j;
+
+ uint32_t idx = pk.hash() % hash_table_size;
+ PosBin *pb = hash_table[idx];
+
+ while (pb) {
+
+ if (pb->key == pk) {
+ break;
+ }
+
+ pb=pb->next;
+ }
+
+ ERR_CONTINUE(!pb); //should exist!!
+
+ bool exited=false;
+
+
+ if (p_static) {
+ if (pb->static_object_set[p_elem].dec()==0) {
+
+ pb->static_object_set.erase(p_elem);
+ exited=true;
+
+ }
+ } else {
+ if (pb->object_set[p_elem].dec()==0) {
+
+ pb->object_set.erase(p_elem);
+ exited=true;
+
+ }
+ }
+
+ if (exited) {
+
+ for(Map<Element*,RC>::Element *E=pb->object_set.front();E;E=E->next()) {
+
+ if (E->key()->owner==p_elem->owner)
+ continue;
+ _unpair_attempt(p_elem,E->key());
+
+ }
+
+ if (!p_static) {
+
+ for(Map<Element*,RC>::Element *E=pb->static_object_set.front();E;E=E->next()) {
+
+ if (E->key()->owner==p_elem->owner)
+ continue;
+ _unpair_attempt(p_elem,E->key());
+ }
+ }
+ }
+
+ if (pb->object_set.empty() && pb->static_object_set.empty()) {
+
+ if (hash_table[idx]==pb) {
+ hash_table[idx]=pb->next;
+ } else {
+
+ PosBin *px = hash_table[idx];
+
+ while (px) {
+
+ if (px->next==pb) {
+ px->next=pb->next;
+ break;
+ }
+
+ px=px->next;
+ }
+
+ ERR_CONTINUE(!px);
+ }
+
+ memdelete(pb);
+
+ }
+ }
+
+ }
+
+}
+
+
+BroadPhase2DHashGrid::ID BroadPhase2DHashGrid::create(CollisionObject2DSW *p_object, int p_subindex) {
+
+ current++;
+
+ Element e;
+ e.owner=p_object;
+ e._static=false;
+ e.subindex=p_subindex;
+ e.self=current;
+ e.pass=0;
+
+ element_map[current]=e;
+ return current;
+
+}
+
+void BroadPhase2DHashGrid::move(ID p_id, const Rect2& p_aabb) {
+
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+
+ Element &e=E->get();
+
+ if (p_aabb==e.aabb)
+ return;
+
+
+ if (p_aabb!=Rect2()) {
+
+ _enter_grid(&e,p_aabb,e._static);
+ }
+
+ if (e.aabb!=Rect2()) {
+
+ _exit_grid(&e,e.aabb,e._static);
+ }
+
+ e.aabb=p_aabb;
+
+ _check_motion(&e);
+
+ e.aabb=p_aabb;
+
+}
+void BroadPhase2DHashGrid::set_static(ID p_id, bool p_static) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+
+ Element &e=E->get();
+
+ if (e._static==p_static)
+ return;
+
+ if (e.aabb!=Rect2())
+ _exit_grid(&e,e.aabb,e._static);
+
+ e._static=p_static;
+
+ if (e.aabb!=Rect2()) {
+ _enter_grid(&e,e.aabb,e._static);
+ _check_motion(&e);
+ }
+
+}
+void BroadPhase2DHashGrid::remove(ID p_id) {
+
+ Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND(!E);
+
+ Element &e=E->get();
+
+ if (e.aabb!=Rect2())
+ _exit_grid(&e,e.aabb,e._static);
+
+ element_map.erase(p_id);
+
+}
+
+CollisionObject2DSW *BroadPhase2DHashGrid::get_object(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,NULL);
+ return E->get().owner;
+
+}
+bool BroadPhase2DHashGrid::is_static(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,false);
+ return E->get()._static;
+
+}
+int BroadPhase2DHashGrid::get_subindex(ID p_id) const {
+
+ const Map<ID,Element>::Element *E=element_map.find(p_id);
+ ERR_FAIL_COND_V(!E,-1);
+ return E->get().subindex;
+}
+
+void BroadPhase2DHashGrid::_cull(const Point2i p_cell,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices,int &index) {
+
+
+ PosKey pk;
+ pk.x=p_cell.x;
+ pk.y=p_cell.y;
+
+ uint32_t idx = pk.hash() % hash_table_size;
+ PosBin *pb = hash_table[idx];
+
+ while (pb) {
+
+ if (pb->key == pk) {
+ break;
+ }
+
+ pb=pb->next;
+ }
+
+ if (!pb)
+ return;
+
+
+
+ for(Map<Element*,RC>::Element *E=pb->object_set.front();E;E=E->next()) {
+
+
+ if (index>=p_max_results)
+ break;
+ if (E->key()->pass==pass)
+ continue;
+
+ E->key()->pass=pass;
+ p_results[index]=E->key()->owner;
+ p_result_indices[index]=E->key()->subindex;
+ index++;
+
+ }
+
+ for(Map<Element*,RC>::Element *E=pb->static_object_set.front();E;E=E->next()) {
+
+
+ if (index>=p_max_results)
+ break;
+ if (E->key()->pass==pass)
+ continue;
+
+ E->key()->pass=pass;
+ p_results[index]=E->key()->owner;
+ p_result_indices[index]=E->key()->subindex;
+ index++;
+
+ }
+}
+
+int BroadPhase2DHashGrid::cull_segment(const Vector2& p_from, const Vector2& p_to,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices) {
+
+ pass++;
+
+ Vector2 dir = (p_to-p_from);
+ if (dir==Vector2())
+ return 0;
+ //avoid divisions by zero
+ dir.normalize();
+ if (dir.x==0.0)
+ dir.x=0.000001;
+ if (dir.y==0.0)
+ dir.y=0.000001;
+ Vector2 delta = dir.abs();
+
+ delta.x=cell_size/delta.x;
+ delta.y=cell_size/delta.y;
+
+ Point2i pos = p_from.floor() / cell_size;
+ Point2i end = p_to.floor() / cell_size;
+ Point2i step = Vector2( SGN(dir.x), SGN(dir.y));
+
+ Vector2 max;
+
+ if (dir.x<0)
+ max.x= (Math::floor(pos.x)*cell_size - p_from.x) / dir.x;
+ else
+ max.x= (Math::floor(pos.x + 1)*cell_size - p_from.x) / dir.x;
+
+ if (dir.y<0)
+ max.y= (Math::floor(pos.y)*cell_size - p_from.y) / dir.y;
+ else
+ max.y= (Math::floor(pos.y + 1)*cell_size - p_from.y) / dir.y;
+
+ int cullcount=0;
+ _cull(pos,p_results,p_max_results,p_result_indices,cullcount);
+
+ bool reached_x=false;
+ bool reached_y=false;
+
+ while(true) {
+
+ if (max.x < max.y) {
+
+ max.x+=delta.x;
+ pos.x+=step.x;
+ } else {
+
+ max.y+=delta.y;
+ pos.y+=step.y;
+
+ }
+
+ if (step.x>0) {
+ if (pos.x>=end.x)
+ reached_x=true;
+ } else if (pos.x<=end.x) {
+
+ reached_x=true;
+ }
+
+ if (step.y>0) {
+ if (pos.y>=end.y)
+ reached_y=true;
+ } else if (pos.y<=end.y) {
+
+ reached_y=true;
+ }
+
+ _cull(pos,p_results,p_max_results,p_result_indices,cullcount);
+
+ if (reached_x && reached_y)
+ break;
+
+ }
+
+ return cullcount;
+}
+
+
+int BroadPhase2DHashGrid::cull_aabb(const Rect2& p_aabb,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices) {
+
+
+ return 0;
+}
+
+void BroadPhase2DHashGrid::set_pair_callback(PairCallback p_pair_callback,void *p_userdata) {
+
+ pair_callback=p_pair_callback;
+ pair_userdata=p_userdata;
+
+}
+void BroadPhase2DHashGrid::set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata) {
+
+ unpair_callback=p_unpair_callback;
+ unpair_userdata=p_userdata;
+
+}
+
+void BroadPhase2DHashGrid::update() {
+
+
+}
+
+BroadPhase2DSW *BroadPhase2DHashGrid::_create() {
+
+ return memnew( BroadPhase2DHashGrid );
+}
+
+
+BroadPhase2DHashGrid::BroadPhase2DHashGrid() {
+
+ hash_table_size = GLOBAL_DEF("physics_2d/bp_hash_table_size",4096);
+ hash_table_size = Math::larger_prime(hash_table_size);
+ hash_table = memnew_arr( PosBin*, hash_table_size);
+
+ cell_size = GLOBAL_DEF("physics_2d/cell_size",128);
+
+ for(int i=0;i<hash_table_size;i++)
+ hash_table[i]=NULL;
+ pass=1;
+
+ current=0;
+}
+
+BroadPhase2DHashGrid::~BroadPhase2DHashGrid() {
+
+ for(int i=0;i<hash_table_size;i++) {
+ while(hash_table[i]) {
+ PosBin *pb=hash_table[i];
+ hash_table[i]=pb->next;
+ memdelete(pb);
+ }
+ }
+
+ memdelete_arr( hash_table );
+
+
+}
+
+
+
+/* 3D version of voxel traversal:
+
+public IEnumerable<Point3D> GetCellsOnRay(Ray ray, int maxDepth)
+{
+ // Implementation is based on:
+ // "A Fast Voxel Traversal Algorithm for Ray Tracing"
+ // John Amanatides, Andrew Woo
+ // http://www.cse.yorku.ca/~amana/research/grid.pdf
+ // http://www.devmaster.net/articles/raytracing_series/A%20faster%20voxel%20traversal%20algorithm%20for%20ray%20tracing.pdf
+
+ // NOTES:
+ // * This code assumes that the ray's position and direction are in 'cell coordinates', which means
+ // that one unit equals one cell in all directions.
+ // * When the ray doesn't start within the voxel grid, calculate the first position at which the
+ // ray could enter the grid. If it never enters the grid, there is nothing more to do here.
+ // * Also, it is important to test when the ray exits the voxel grid when the grid isn't infinite.
+ // * The Point3D structure is a simple structure having three integer fields (X, Y and Z).
+
+ // The cell in which the ray starts.
+ Point3D start = GetCellAt(ray.Position); // Rounds the position's X, Y and Z down to the nearest integer values.
+ int x = start.X;
+ int y = start.Y;
+ int z = start.Z;
+
+ // Determine which way we go.
+ int stepX = Math.Sign(ray.Direction.X);
+ int stepY = Math.Sign(ray.Direction.Y);
+ int stepZ = Math.Sign(ray.Direction.Z);
+
+ // Calculate cell boundaries. When the step (i.e. direction sign) is positive,
+ // the next boundary is AFTER our current position, meaning that we have to add 1.
+ // Otherwise, it is BEFORE our current position, in which case we add nothing.
+ Point3D cellBoundary = new Point3D(
+ x + (stepX > 0 ? 1 : 0),
+ y + (stepY > 0 ? 1 : 0),
+ z + (stepZ > 0 ? 1 : 0));
+
+ // NOTE: For the following calculations, the result will be Single.PositiveInfinity
+ // when ray.Direction.X, Y or Z equals zero, which is OK. However, when the left-hand
+ // value of the division also equals zero, the result is Single.NaN, which is not OK.
+
+ // Determine how far we can travel along the ray before we hit a voxel boundary.
+ Vector3 tMax = new Vector3(
+ (cellBoundary.X - ray.Position.X) / ray.Direction.X, // Boundary is a plane on the YZ axis.
+ (cellBoundary.Y - ray.Position.Y) / ray.Direction.Y, // Boundary is a plane on the XZ axis.
+ (cellBoundary.Z - ray.Position.Z) / ray.Direction.Z); // Boundary is a plane on the XY axis.
+ if (Single.IsNaN(tMax.X)) tMax.X = Single.PositiveInfinity;
+ if (Single.IsNaN(tMax.Y)) tMax.Y = Single.PositiveInfinity;
+ if (Single.IsNaN(tMax.Z)) tMax.Z = Single.PositiveInfinity;
+
+ // Determine how far we must travel along the ray before we have crossed a gridcell.
+ Vector3 tDelta = new Vector3(
+ stepX / ray.Direction.X, // Crossing the width of a cell.
+ stepY / ray.Direction.Y, // Crossing the height of a cell.
+ stepZ / ray.Direction.Z); // Crossing the depth of a cell.
+ if (Single.IsNaN(tDelta.X)) tDelta.X = Single.PositiveInfinity;
+ if (Single.IsNaN(tDelta.Y)) tDelta.Y = Single.PositiveInfinity;
+ if (Single.IsNaN(tDelta.Z)) tDelta.Z = Single.PositiveInfinity;
+
+ // For each step, determine which distance to the next voxel boundary is lowest (i.e.
+ // which voxel boundary is nearest) and walk that way.
+ for (int i = 0; i < maxDepth; i++)
+ {
+ // Return it.
+ yield return new Point3D(x, y, z);
+
+ // Do the next step.
+ if (tMax.X < tMax.Y && tMax.X < tMax.Z)
+ {
+ // tMax.X is the lowest, an YZ cell boundary plane is nearest.
+ x += stepX;
+ tMax.X += tDelta.X;
+ }
+ else if (tMax.Y < tMax.Z)
+ {
+ // tMax.Y is the lowest, an XZ cell boundary plane is nearest.
+ y += stepY;
+ tMax.Y += tDelta.Y;
+ }
+ else
+ {
+ // tMax.Z is the lowest, an XY cell boundary plane is nearest.
+ z += stepZ;
+ tMax.Z += tDelta.Z;
+ }
+ }
+
+ */
diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.h b/servers/physics_2d/broad_phase_2d_hash_grid.h
new file mode 100644
index 0000000000..713d960487
--- /dev/null
+++ b/servers/physics_2d/broad_phase_2d_hash_grid.h
@@ -0,0 +1,192 @@
+/*************************************************************************/
+/* broad_phase_2d_hash_grid.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 BROAD_PHASE_2D_HASH_GRID_H
+#define BROAD_PHASE_2D_HASH_GRID_H
+
+#include "broad_phase_2d_sw.h"
+#include "map.h"
+
+class BroadPhase2DHashGrid : public BroadPhase2DSW {
+
+
+ struct PairData {
+
+ bool colliding;
+ int rc;
+ void *ud;
+ PairData() { colliding=false; rc=1; ud=NULL; }
+ };
+
+ struct Element {
+
+ ID self;
+ CollisionObject2DSW *owner;
+ bool _static;
+ Rect2 aabb;
+ int subindex;
+ uint64_t pass;
+ Map<Element*,PairData*> paired;
+
+ };
+
+
+ Map<ID,Element> element_map;
+
+ ID current;
+
+ uint64_t pass;
+
+
+ struct PairKey {
+
+ union {
+ struct {
+ ID a;
+ ID b;
+ };
+ uint64_t key;
+ };
+
+ _FORCE_INLINE_ bool operator<(const PairKey& p_key) const {
+ return key < p_key.key;
+ }
+
+ PairKey() { key=0; }
+ PairKey(ID p_a, ID p_b) { if (p_a>p_b) { a=p_b; b=p_a; } else { a=p_a; b=p_b; }}
+
+ };
+
+
+ Map<PairKey,PairData> pair_map;
+
+ int cell_size;
+
+ PairCallback pair_callback;
+ void *pair_userdata;
+ UnpairCallback unpair_callback;
+ void *unpair_userdata;
+
+ void _enter_grid(Element* p_elem, const Rect2& p_rect,bool p_static);
+ void _exit_grid(Element* p_elem, const Rect2& p_rect,bool p_static);
+ _FORCE_INLINE_ void _cull(const Point2i p_cell,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices,int &index);
+
+
+ struct PosKey {
+
+ union {
+ struct {
+ int32_t x;
+ int32_t y;
+ };
+ uint64_t key;
+ };
+
+
+ _FORCE_INLINE_ uint32_t hash() const {
+ uint64_t k=key;
+ k = (~k) + (k << 18); // k = (k << 18) - k - 1;
+ k = k ^ (k >> 31);
+ k = k * 21; // k = (k + (k << 2)) + (k << 4);
+ k = k ^ (k >> 11);
+ k = k + (k << 6);
+ k = k ^ (k >> 22);
+ return k;
+ }
+
+ bool operator==(const PosKey& p_key) const { return key==p_key.key; }
+ _FORCE_INLINE_ bool operator<(const PosKey& p_key) const {
+ return key < p_key.key;
+ }
+
+ };
+
+ struct RC {
+
+ int ref;
+
+ _FORCE_INLINE_ int inc() {
+ ref++;
+ return ref;
+ }
+ _FORCE_INLINE_ int dec() {
+ ref--;
+ return ref;
+ }
+
+ _FORCE_INLINE_ RC() {
+ ref=0;
+ }
+ };
+
+ struct PosBin {
+
+ PosKey key;
+ Map<Element*,RC> object_set;
+ Map<Element*,RC> static_object_set;
+ PosBin *next;
+ };
+
+
+ uint32_t hash_table_size;
+ PosBin **hash_table;
+
+ void _pair_attempt(Element *p_elem, Element* p_with);
+ void _unpair_attempt(Element *p_elem, Element* p_with);
+ void _check_motion(Element *p_elem);
+
+
+public:
+
+ virtual ID create(CollisionObject2DSW *p_object_, int p_subindex=0);
+ virtual void move(ID p_id, const Rect2& p_aabb);
+ virtual void set_static(ID p_id, bool p_static);
+ virtual void remove(ID p_id);
+
+ virtual CollisionObject2DSW *get_object(ID p_id) const;
+ virtual bool is_static(ID p_id) const;
+ virtual int get_subindex(ID p_id) const;
+
+ virtual int cull_segment(const Vector2& p_from, const Vector2& p_to,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices=NULL);
+ virtual int cull_aabb(const Rect2& p_aabb,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices=NULL);
+
+ virtual void set_pair_callback(PairCallback p_pair_callback,void *p_userdata);
+ virtual void set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata);
+
+ virtual void update();
+
+
+ static BroadPhase2DSW *_create();
+
+ BroadPhase2DHashGrid();
+ ~BroadPhase2DHashGrid();
+
+
+};
+
+#endif // BROAD_PHASE_2D_HASH_GRID_H
diff --git a/servers/physics_2d/broad_phase_2d_sw.cpp b/servers/physics_2d/broad_phase_2d_sw.cpp
new file mode 100644
index 0000000000..7ded6ed01e
--- /dev/null
+++ b/servers/physics_2d/broad_phase_2d_sw.cpp
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* broad_phase_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "broad_phase_2d_sw.h"
+
+BroadPhase2DSW::CreateFunction BroadPhase2DSW::create_func=NULL;
+
+BroadPhase2DSW::~BroadPhase2DSW()
+{
+}
diff --git a/servers/physics_2d/broad_phase_2d_sw.h b/servers/physics_2d/broad_phase_2d_sw.h
new file mode 100644
index 0000000000..510f7db112
--- /dev/null
+++ b/servers/physics_2d/broad_phase_2d_sw.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* broad_phase_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 BROAD_PHASE_2D_SW_H
+#define BROAD_PHASE_2D_SW_H
+
+#include "math_funcs.h"
+#include "math_2d.h"
+
+class CollisionObject2DSW;
+
+
+class BroadPhase2DSW {
+
+public:
+ typedef BroadPhase2DSW* (*CreateFunction)();
+
+ static CreateFunction create_func;
+
+ typedef uint32_t ID;
+
+
+ typedef void* (*PairCallback)(CollisionObject2DSW *A,int p_subindex_A,CollisionObject2DSW *B,int p_subindex_B,void *p_userdata);
+ typedef void (*UnpairCallback)(CollisionObject2DSW *A,int p_subindex_A,CollisionObject2DSW *B,int p_subindex_B,void *p_data,void *p_userdata);
+
+ // 0 is an invalid ID
+ virtual ID create(CollisionObject2DSW *p_object_, int p_subindex=0)=0;
+ virtual void move(ID p_id, const Rect2& p_aabb)=0;
+ virtual void set_static(ID p_id, bool p_static)=0;
+ virtual void remove(ID p_id)=0;
+
+ virtual CollisionObject2DSW *get_object(ID p_id) const=0;
+ virtual bool is_static(ID p_id) const=0;
+ virtual int get_subindex(ID p_id) const=0;
+
+ virtual int cull_segment(const Vector2& p_from, const Vector2& p_to,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices=NULL)=0;
+ virtual int cull_aabb(const Rect2& p_aabb,CollisionObject2DSW** p_results,int p_max_results,int *p_result_indices=NULL)=0;
+
+ virtual void set_pair_callback(PairCallback p_pair_callback,void *p_userdata)=0;
+ virtual void set_unpair_callback(UnpairCallback p_unpair_callback,void *p_userdata)=0;
+
+ virtual void update()=0;
+
+ virtual ~BroadPhase2DSW();
+
+};
+
+#endif // BROAD_PHASE_2D_SW_H
diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp
new file mode 100644
index 0000000000..6e5a703aa2
--- /dev/null
+++ b/servers/physics_2d/collision_object_2d_sw.cpp
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* collision_object_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "collision_object_2d_sw.h"
+#include "space_2d_sw.h"
+
+void CollisionObject2DSW::add_shape(Shape2DSW *p_shape,const Matrix32& p_transform) {
+
+ Shape s;
+ s.shape=p_shape;
+ s.xform=p_transform;
+ s.xform_inv=s.xform.affine_inverse();
+ s.bpid=0; //needs update
+ s.trigger=false;
+ shapes.push_back(s);
+ p_shape->add_owner(this);
+ _update_shapes();
+ _shapes_changed();
+
+}
+
+void CollisionObject2DSW::set_shape(int p_index,Shape2DSW *p_shape){
+
+ ERR_FAIL_INDEX(p_index,shapes.size());
+ shapes[p_index].shape->remove_owner(this);
+ shapes[p_index].shape=p_shape;
+
+ p_shape->add_owner(this);
+ _update_shapes();
+ _shapes_changed();
+
+}
+void CollisionObject2DSW::set_shape_transform(int p_index,const Matrix32& p_transform){
+
+ ERR_FAIL_INDEX(p_index,shapes.size());
+
+ shapes[p_index].xform=p_transform;
+ shapes[p_index].xform_inv=p_transform.affine_inverse();
+ _update_shapes();
+ _shapes_changed();
+}
+
+void CollisionObject2DSW::remove_shape(Shape2DSW *p_shape) {
+
+ //remove a shape, all the times it appears
+ for(int i=0;i<shapes.size();i++) {
+
+ if (shapes[i].shape==p_shape) {
+ remove_shape(i);
+ i--;
+ }
+ }
+}
+
+void CollisionObject2DSW::remove_shape(int p_index){
+
+ //remove anything from shape to be erased to end, so subindices don't change
+ ERR_FAIL_INDEX(p_index,shapes.size());
+ for(int i=p_index;i<shapes.size();i++) {
+
+ if (shapes[i].bpid==0)
+ continue;
+ //should never get here with a null owner
+ space->get_broadphase()->remove(shapes[i].bpid);
+ shapes[i].bpid=0;
+ }
+ shapes[p_index].shape->remove_owner(this);
+ shapes.remove(p_index);
+
+ _shapes_changed();
+
+}
+
+void CollisionObject2DSW::_set_static(bool p_static) {
+ if (_static==p_static)
+ return;
+ _static=p_static;
+
+ if (!space)
+ return;
+ for(int i=0;i<get_shape_count();i++) {
+ Shape &s=shapes[i];
+ if (s.bpid>0) {
+ space->get_broadphase()->set_static(s.bpid,_static);
+ }
+ }
+
+}
+
+void CollisionObject2DSW::_unregister_shapes() {
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid>0) {
+ space->get_broadphase()->remove(s.bpid);
+ s.bpid=0;
+ }
+ }
+
+}
+
+void CollisionObject2DSW::_update_shapes() {
+
+ if (!space)
+ return;
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid==0) {
+ s.bpid=space->get_broadphase()->create(this,i);
+ space->get_broadphase()->set_static(s.bpid,_static);
+ }
+
+ //not quite correct, should compute the next matrix..
+ Rect2 shape_aabb=s.shape->get_aabb();
+ Matrix32 xform = transform * s.xform;
+ shape_aabb=xform.xform(shape_aabb);
+ s.aabb_cache=shape_aabb;
+ s.aabb_cache=s.aabb_cache.grow( (s.aabb_cache.size.x + s.aabb_cache.size.y)*0.5*0.05 );
+
+
+ space->get_broadphase()->move(s.bpid,s.aabb_cache);
+ }
+
+}
+
+void CollisionObject2DSW::_update_shapes_with_motion(const Vector2& p_motion) {
+
+
+ if (!space)
+ return;
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid==0) {
+ s.bpid=space->get_broadphase()->create(this,i);
+ space->get_broadphase()->set_static(s.bpid,_static);
+ }
+
+ //not quite correct, should compute the next matrix..
+ Rect2 shape_aabb=s.shape->get_aabb();
+ Matrix32 xform = transform * s.xform;
+ shape_aabb=xform.xform(shape_aabb);
+ shape_aabb=shape_aabb.merge(Rect2( shape_aabb.pos+p_motion,shape_aabb.size)); //use motion
+ s.aabb_cache=shape_aabb;
+
+ space->get_broadphase()->move(s.bpid,shape_aabb);
+ }
+
+
+}
+
+void CollisionObject2DSW::_set_space(Space2DSW *p_space) {
+
+ if (space) {
+
+ space->remove_object(this);
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Shape &s=shapes[i];
+ if (s.bpid) {
+ space->get_broadphase()->remove(s.bpid);
+ s.bpid=0;
+ }
+ }
+
+ }
+
+ space=p_space;
+
+ if (space) {
+
+ space->add_object(this);
+ _update_shapes();
+ }
+
+}
+
+void CollisionObject2DSW::_shape_changed() {
+
+ _update_shapes();
+ _shapes_changed();
+}
+
+CollisionObject2DSW::CollisionObject2DSW(Type p_type) {
+
+ _static=true;
+ type=p_type;
+ space=NULL;
+ instance_id=0;
+}
diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h
new file mode 100644
index 0000000000..b2d2c25451
--- /dev/null
+++ b/servers/physics_2d/collision_object_2d_sw.h
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* collision_object_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 COLLISION_OBJECT_2D_SW_H
+#define COLLISION_OBJECT_2D_SW_H
+
+#include "shape_2d_sw.h"
+#include "servers/physics_2d_server.h"
+#include "self_list.h"
+#include "broad_phase_2d_sw.h"
+
+class Space2DSW;
+
+class CollisionObject2DSW : public ShapeOwner2DSW {
+public:
+ enum Type {
+ TYPE_AREA,
+ TYPE_BODY
+ };
+private:
+
+ Type type;
+ RID self;
+ ObjectID instance_id;
+
+ struct Shape {
+
+ Matrix32 xform;
+ Matrix32 xform_inv;
+ BroadPhase2DSW::ID bpid;
+ Rect2 aabb_cache; //for rayqueries
+ Shape2DSW *shape;
+ bool trigger;
+ Shape() { trigger=false; }
+ };
+
+ Vector<Shape> shapes;
+ Space2DSW *space;
+ Matrix32 transform;
+ Matrix32 inv_transform;
+ bool _static;
+
+ void _update_shapes();
+
+protected:
+
+
+ void _update_shapes_with_motion(const Vector2& p_motion);
+ void _unregister_shapes();
+
+ _FORCE_INLINE_ void _set_transform(const Matrix32& p_transform) { transform=p_transform; _update_shapes(); }
+ _FORCE_INLINE_ void _set_inv_transform(const Matrix32& p_transform) { inv_transform=p_transform; }
+ void _set_static(bool p_static);
+
+ virtual void _shapes_changed()=0;
+ void _set_space(Space2DSW *space);
+
+ CollisionObject2DSW(Type p_type);
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ _FORCE_INLINE_ void set_instance_id(const ObjectID& p_instance_id) { instance_id=p_instance_id; }
+ _FORCE_INLINE_ ObjectID get_instance_id() const { return instance_id; }
+
+ void _shape_changed();
+
+ _FORCE_INLINE_ Type get_type() const { return type; }
+ void add_shape(Shape2DSW *p_shape,const Matrix32& p_transform=Matrix32());
+ void set_shape(int p_index,Shape2DSW *p_shape);
+ void set_shape_transform(int p_index,const Matrix32& p_transform);
+ _FORCE_INLINE_ int get_shape_count() const { return shapes.size(); }
+ _FORCE_INLINE_ Shape2DSW *get_shape(int p_index) const { return shapes[p_index].shape; }
+ _FORCE_INLINE_ const Matrix32& get_shape_transform(int p_index) const { return shapes[p_index].xform; }
+ _FORCE_INLINE_ const Matrix32& get_shape_inv_transform(int p_index) const { return shapes[p_index].xform_inv; }
+ _FORCE_INLINE_ const Rect2& get_shape_aabb(int p_index) const { return shapes[p_index].aabb_cache; }
+
+ _FORCE_INLINE_ Matrix32 get_transform() const { return transform; }
+ _FORCE_INLINE_ Matrix32 get_inv_transform() const { return inv_transform; }
+ _FORCE_INLINE_ Space2DSW* get_space() const { return space; }
+
+ _FORCE_INLINE_ void set_shape_as_trigger(int p_idx,bool p_enable) { shapes[p_idx].trigger=p_enable; }
+ _FORCE_INLINE_ bool is_shape_set_as_trigger(int p_idx) const { return shapes[p_idx].trigger; }
+
+
+ void remove_shape(Shape2DSW *p_shape);
+ void remove_shape(int p_index);
+
+ virtual void set_space(Space2DSW *p_space)=0;
+
+ _FORCE_INLINE_ bool is_static() const { return _static; }
+
+ virtual ~CollisionObject2DSW() {}
+
+};
+
+#endif // COLLISION_OBJECT_2D_SW_H
diff --git a/servers/physics_2d/collision_solver_2d_sat.cpp b/servers/physics_2d/collision_solver_2d_sat.cpp
new file mode 100644
index 0000000000..4b87ff02a2
--- /dev/null
+++ b/servers/physics_2d/collision_solver_2d_sat.cpp
@@ -0,0 +1,1034 @@
+/*************************************************************************/
+/* collision_solver_2d_sat.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "collision_solver_2d_sat.h"
+#include "geometry.h"
+
+struct _CollectorCallback2D {
+
+ CollisionSolver2DSW::CallbackResult callback;
+ void *userdata;
+ bool swap;
+ bool collided;
+ Vector2 normal;
+ Vector2 *sep_axis;
+
+ _FORCE_INLINE_ void call(const Vector2& p_point_A, const Vector2& p_point_B) {
+
+ //if (normal.dot(p_point_A) >= normal.dot(p_point_B))
+ // return;
+ if (swap)
+ callback(p_point_B,p_point_A,userdata);
+ else
+ callback(p_point_A,p_point_B,userdata);
+ }
+
+};
+
+typedef void (*GenerateContactsFunc)(const Vector2 *,int, const Vector2 *,int ,_CollectorCallback2D *);
+
+
+_FORCE_INLINE_ static void _generate_contacts_point_point(const Vector2 * p_points_A,int p_point_count_A, const Vector2 * p_points_B,int p_point_count_B,_CollectorCallback2D *p_collector) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 1 );
+ ERR_FAIL_COND( p_point_count_B != 1 );
+#endif
+
+ p_collector->call(*p_points_A,*p_points_B);
+}
+
+_FORCE_INLINE_ static void _generate_contacts_point_edge(const Vector2 * p_points_A,int p_point_count_A, const Vector2 * p_points_B,int p_point_count_B,_CollectorCallback2D *p_collector) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 1 );
+ ERR_FAIL_COND( p_point_count_B != 2 );
+#endif
+
+ Vector2 closest_B = Geometry::get_closest_point_to_segment_uncapped_2d(*p_points_A, p_points_B );
+ p_collector->call(*p_points_A,closest_B);
+
+}
+
+
+struct _generate_contacts_Pair {
+ int idx;
+ float d;
+ _FORCE_INLINE_ bool operator <(const _generate_contacts_Pair& l) const { return d< l.d; }
+};
+
+_FORCE_INLINE_ static void _generate_contacts_edge_edge(const Vector2 * p_points_A,int p_point_count_A, const Vector2 * p_points_B,int p_point_count_B,_CollectorCallback2D *p_collector) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A != 2 );
+ ERR_FAIL_COND( p_point_count_B != 2 ); // circle is actually a 4x3 matrix
+#endif
+
+
+ Vector2 rel_A=p_points_A[1]-p_points_A[0];
+ Vector2 rel_B=p_points_B[1]-p_points_B[0];
+
+ Vector2 t = p_collector->normal.tangent();
+
+ real_t dA[2]={t.dot(p_points_A[0]),t.dot(p_points_A[1])};
+ Vector2 pA[2]={p_points_A[0],p_points_A[1]};
+
+ if (dA[0]>dA[1]) {
+ SWAP(dA[0],dA[1]);
+ SWAP(pA[0],pA[1]);
+ }
+
+ float dB[2]={t.dot(p_points_B[0]),t.dot(p_points_B[1])};
+ Vector2 pB[2]={p_points_B[0],p_points_B[1]};
+ if (dB[0]>dB[1]) {
+ SWAP(dB[0],dB[1]);
+ SWAP(pB[0],pB[1]);
+ }
+
+
+ if (dA[0]<dB[0]) {
+
+ Vector2 n = (p_points_A[1]-p_points_A[0]).normalized().tangent();
+ real_t d = n.dot(p_points_A[1]);
+
+ if (dA[1]>dB[1]) {
+ //A contains B
+ for(int i=0;i<2;i++) {
+
+ Vector2 b = p_points_B[i];
+ Vector2 a = n.plane_project(d,b);
+ if (p_collector->normal.dot(a) > p_collector->normal.dot(b)-CMP_EPSILON)
+ continue;
+ p_collector->call(a,b);
+
+ }
+ } else {
+
+ // B0,A1 containment
+
+ Vector2 n_B = (p_points_B[1]-p_points_B[0]).normalized().tangent();
+ real_t d_B = n_B.dot(p_points_B[1]);
+
+ // first, B on A
+
+ {
+ Vector2 b = p_points_B[0];
+ Vector2 a = n.plane_project(d,b);
+ if (p_collector->normal.dot(a) < p_collector->normal.dot(b)-CMP_EPSILON)
+ p_collector->call(a,b);
+ }
+
+ // second, A on B
+
+ {
+ Vector2 a = p_points_A[1];
+ Vector2 b = n_B.plane_project(d_B,a);
+ if (p_collector->normal.dot(a) < p_collector->normal.dot(b)-CMP_EPSILON)
+ p_collector->call(a,b);
+ }
+
+
+
+ }
+
+
+ } else {
+
+ Vector2 n = (p_points_B[1]-p_points_B[0]).normalized().tangent();
+ real_t d = n.dot(p_points_B[1]);
+
+ if (dB[1]>dA[1]) {
+ //B contains A
+ for(int i=0;i<2;i++) {
+
+ Vector2 a = p_points_A[i];
+ Vector2 b = n.plane_project(d,a);
+ if (p_collector->normal.dot(a) > p_collector->normal.dot(b)-CMP_EPSILON)
+ continue;
+ p_collector->call(a,b);
+ }
+ } else {
+
+ // A0,B1 containment
+ Vector2 n_A = (p_points_A[1]-p_points_A[0]).normalized().tangent();
+ real_t d_A = n_A.dot(p_points_A[1]);
+
+ // first A on B
+
+ {
+ Vector2 a = p_points_A[0];
+ Vector2 b = n.plane_project(d,a);
+ if (p_collector->normal.dot(a) < p_collector->normal.dot(b)-CMP_EPSILON)
+ p_collector->call(a,b);
+
+ }
+
+ //second, B on A
+
+ {
+
+ Vector2 b = p_points_B[1];
+ Vector2 a = n_A.plane_project(d_A,b);
+ if (p_collector->normal.dot(a) < p_collector->normal.dot(b)-CMP_EPSILON)
+ p_collector->call(a,b);
+ }
+
+ }
+ }
+
+
+#if 0
+
+ Vector2 axis = rel_A.normalized();
+ Vector2 axis_B = rel_B.normalized();
+ if (axis.dot(axis_B)<0)
+ axis_B=-axis_B;
+ axis=(axis+axis_B)*0.5;
+
+ Vector2 normal_A = axis.tangent();
+ real_t dA = normal_A.dot(p_points_A[0]);
+ Vector2 normal_B = rel_B.tangent().normalized();
+ real_t dB = normal_A.dot(p_points_B[0]);
+
+ Vector2 A[4]={ normal_A.plane_project(dA,p_points_B[0]), normal_A.plane_project(dA,p_points_B[1]), p_points_A[0], p_points_A[1] };
+ Vector2 B[4]={ p_points_B[0], p_points_B[1], normal_B.plane_project(dB,p_points_A[0]), normal_B.plane_project(dB,p_points_A[1]) };
+
+ _generate_contacts_Pair dvec[4];
+ for(int i=0;i<4;i++) {
+ dvec[i].d=axis.dot(p_points_A[0]-A[i]);
+ dvec[i].idx=i;
+ }
+
+ SortArray<_generate_contacts_Pair> sa;
+ sa.sort(dvec,4);
+
+ for(int i=1;i<=2;i++) {
+
+ Vector2 a = A[i];
+ Vector2 b = B[i];
+ if (p_collector->normal.dot(a) > p_collector->normal.dot(b)-CMP_EPSILON)
+ continue;
+ p_collector->call(a,b);
+ }
+
+#elif 0
+ Vector2 axis = rel_A.normalized(); //make an axis
+ Vector2 axis_B = rel_B.normalized();
+ if (axis.dot(axis_B)<0)
+ axis_B=-axis_B;
+ axis=(axis+axis_B)*0.5;
+ Vector2 base_A = p_points_A[0] - axis * axis.dot(p_points_A[0]);
+ Vector2 base_B = p_points_B[0] - axis * axis.dot(p_points_B[0]);
+
+ //sort all 4 points in axis
+ float dvec[4]={ axis.dot(p_points_A[0]), axis.dot(p_points_A[1]), axis.dot(p_points_B[0]), axis.dot(p_points_B[1]) };
+
+ //todo , find max/min and then use 2 central points
+ SortArray<float> sa;
+ sa.sort(dvec,4);
+
+ //use the middle ones as contacts
+ for (int i=1;i<=2;i++) {
+
+ Vector2 a = base_A+axis*dvec[i];
+ Vector2 b = base_B+axis*dvec[i];
+ if (p_collector->normal.dot(a) > p_collector->normal.dot(b)-0.01) {
+ print_line("fail a: "+a);
+ print_line("fail b: "+b);
+ continue;
+ }
+ print_line("res a: "+a);
+ print_line("res b: "+b);
+ p_collector->call(a,b);
+ }
+#endif
+}
+
+static void _generate_contacts_from_supports(const Vector2 * p_points_A,int p_point_count_A, const Vector2 * p_points_B,int p_point_count_B,_CollectorCallback2D *p_collector) {
+
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND( p_point_count_A <1 );
+ ERR_FAIL_COND( p_point_count_B <1 );
+#endif
+
+
+ static const GenerateContactsFunc generate_contacts_func_table[2][2]={
+ {
+ _generate_contacts_point_point,
+ _generate_contacts_point_edge,
+ },{
+ 0,
+ _generate_contacts_edge_edge,
+ }
+ };
+
+ int pointcount_B;
+ int pointcount_A;
+ const Vector2 *points_A;
+ const Vector2 *points_B;
+
+ if (p_point_count_A > p_point_count_B) {
+ //swap
+ p_collector->swap = !p_collector->swap;
+ p_collector->normal = -p_collector->normal;
+
+ pointcount_B = p_point_count_A;
+ pointcount_A = p_point_count_B;
+ points_A=p_points_B;
+ points_B=p_points_A;
+ } else {
+
+ pointcount_B = p_point_count_B;
+ pointcount_A = p_point_count_A;
+ points_A=p_points_A;
+ points_B=p_points_B;
+ }
+
+ int version_A = (pointcount_A > 3 ? 3 : pointcount_A) -1;
+ int version_B = (pointcount_B > 3 ? 3 : pointcount_B) -1;
+
+ GenerateContactsFunc contacts_func = generate_contacts_func_table[version_A][version_B];
+ ERR_FAIL_COND(!contacts_func);
+ contacts_func(points_A,pointcount_A,points_B,pointcount_B,p_collector);
+
+}
+
+
+
+template<class ShapeA, class ShapeB>
+class SeparatorAxisTest2D {
+
+ const ShapeA *shape_A;
+ const ShapeB *shape_B;
+ const Matrix32 *transform_A;
+ const Matrix32 *transform_B;
+ const Matrix32 *transform_inv_A;
+ const Matrix32 *transform_inv_B;
+ real_t best_depth;
+ Vector2 best_axis;
+ int best_axis_count;
+ int best_axis_index;
+ _CollectorCallback2D *callback;
+
+public:
+
+ _FORCE_INLINE_ bool test_previous_axis() {
+
+ if (callback && callback->sep_axis && *callback->sep_axis!=Vector2()) {
+ return test_axis(*callback->sep_axis);
+ } else {
+#ifdef DEBUG_ENABLED
+ best_axis_count++;
+#endif
+
+ }
+ return true;
+ }
+
+ _FORCE_INLINE_ bool test_axis(const Vector2& p_axis) {
+
+ Vector2 axis=p_axis;
+
+
+ if ( Math::abs(axis.x)<CMP_EPSILON &&
+ Math::abs(axis.y)<CMP_EPSILON) {
+ // strange case, try an upwards separator
+ axis=Vector2(0.0,1.0);
+ }
+
+ real_t min_A,max_A,min_B,max_B;
+
+ shape_A->project_range(axis,*transform_A,min_A,max_A);
+ shape_B->project_range(axis,*transform_B,min_B,max_B);
+
+ min_B -= ( max_A - min_A ) * 0.5;
+ max_B += ( max_A - min_A ) * 0.5;
+
+ real_t dmin = min_B - ( min_A + max_A ) * 0.5;
+ real_t dmax = max_B - ( min_A + max_A ) * 0.5;
+
+ if (dmin > 0.0 || dmax < 0.0) {
+ if (callback && callback->sep_axis)
+ *callback->sep_axis=axis;
+#ifdef DEBUG_ENABLED
+ best_axis_count++;
+#endif
+
+ return false; // doesn't contain 0
+ }
+
+ //use the smallest depth
+
+ dmin = Math::abs(dmin);
+
+ if ( dmax < dmin ) {
+ if ( dmax < best_depth ) {
+ best_depth=dmax;
+ best_axis=axis;
+#ifdef DEBUG_ENABLED
+ best_axis_index=best_axis_count;
+#endif
+
+ }
+ } else {
+ if ( dmin < best_depth ) {
+ best_depth=dmin;
+ best_axis=-axis; // keep it as A axis
+#ifdef DEBUG_ENABLED
+ best_axis_index=best_axis_count;
+#endif
+ }
+ }
+
+ // print_line("test axis: "+p_axis+" depth: "+rtos(best_depth));
+#ifdef DEBUG_ENABLED
+ best_axis_count++;
+#endif
+
+ return true;
+ }
+
+
+ _FORCE_INLINE_ void generate_contacts() {
+
+ // nothing to do, don't generate
+ if (best_axis==Vector2(0.0,0.0))
+ return;
+
+ callback->collided=true;
+
+ if (!callback->callback)
+ return; //only collide, no callback
+ static const int max_supports=2;
+
+
+ Vector2 supports_A[max_supports];
+ int support_count_A;
+ shape_A->get_supports(transform_A->basis_xform_inv(-best_axis).normalized(),supports_A,support_count_A);
+ for(int i=0;i<support_count_A;i++) {
+ supports_A[i] = transform_A->xform(supports_A[i]);
+ }
+
+
+
+ Vector2 supports_B[max_supports];
+ int support_count_B;
+ shape_B->get_supports(transform_B->basis_xform_inv(best_axis).normalized(),supports_B,support_count_B);
+ for(int i=0;i<support_count_B;i++) {
+ supports_B[i] = transform_B->xform(supports_B[i]);
+ }
+/*
+
+
+ print_line("**************************");
+ printf("CBK: %p\n",callback->userdata);
+ print_line("type A: "+itos(shape_A->get_type()));
+ print_line("type B: "+itos(shape_B->get_type()));
+ print_line("xform A: "+*transform_A);
+ print_line("xform B: "+*transform_B);
+ print_line("normal: "+best_axis);
+ print_line("depth: "+rtos(best_depth));
+ print_line("index: "+itos(best_axis_index));
+
+ for(int i=0;i<support_count_A;i++) {
+
+ print_line("A-"+itos(i)+": "+supports_A[i]);
+ }
+
+ for(int i=0;i<support_count_B;i++) {
+
+ print_line("B-"+itos(i)+": "+supports_B[i]);
+ }
+//*/
+
+
+
+
+ callback->normal=best_axis;
+ _generate_contacts_from_supports(supports_A,support_count_A,supports_B,support_count_B,callback);
+
+ if (callback && callback->sep_axis && *callback->sep_axis!=Vector2())
+ *callback->sep_axis=Vector2(); //invalidate previous axis (no test)
+ //CollisionSolver2DSW::CallbackResult cbk=NULL;
+ //cbk(Vector2(),Vector2(),NULL);
+
+ }
+
+ _FORCE_INLINE_ SeparatorAxisTest2D(const ShapeA *p_shape_A,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a, const ShapeB *p_shape_B,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+ best_depth=1e15;
+ shape_A=p_shape_A;
+ shape_B=p_shape_B;
+ transform_A=&p_transform_a;
+ transform_B=&p_transform_b;
+ transform_inv_A=&p_transform_inv_a;
+ transform_inv_B=&p_transform_inv_b;
+ callback=p_collector;
+#ifdef DEBUG_ENABLED
+ best_axis_count=0;
+ best_axis_index=-1;
+#endif
+ }
+
+};
+
+/****** SAT TESTS *******/
+/****** SAT TESTS *******/
+/****** SAT TESTS *******/
+/****** SAT TESTS *******/
+
+
+typedef void (*CollisionFunc)(const Shape2DSW*,const Matrix32&,const Matrix32&,const Shape2DSW*,const Matrix32&,const Matrix32&,_CollectorCallback2D *p_collector);
+
+static void _collision_segment_segment(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const SegmentShape2DSW *segment_A = static_cast<const SegmentShape2DSW*>(p_a);
+ const SegmentShape2DSW *segment_B = static_cast<const SegmentShape2DSW*>(p_b);
+
+ SeparatorAxisTest2D<SegmentShape2DSW,SegmentShape2DSW> separator(segment_A,p_transform_a,p_transform_inv_a,segment_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis(p_transform_inv_a.basis_xform_inv(segment_A->get_normal()).normalized()))
+ return;
+ if (!separator.test_axis(p_transform_inv_a.basis_xform_inv(segment_B->get_normal()).normalized()))
+ return;
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_segment_circle(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+
+ const SegmentShape2DSW *segment_A = static_cast<const SegmentShape2DSW*>(p_a);
+ const CircleShape2DSW *circle_B = static_cast<const CircleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<SegmentShape2DSW,CircleShape2DSW> separator(segment_A,p_transform_a,p_transform_inv_a,circle_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis(
+ (p_transform_a.xform(segment_A->get_b())-p_transform_a.xform(segment_A->get_a())).normalized().tangent()
+ ))
+ return;
+
+// if (!separator.test_axis(p_transform_inv_a.basis_xform_inv(segment_A->get_normal()).normalized()))
+// return;
+ if (!separator.test_axis((p_transform_a.xform(segment_A->get_a())-p_transform_b.get_origin()).normalized()))
+ return;
+ if (!separator.test_axis((p_transform_a.xform(segment_A->get_b())-p_transform_b.get_origin()).normalized()))
+ return;
+
+
+ separator.generate_contacts();
+
+
+}
+
+static void _collision_segment_rectangle(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const SegmentShape2DSW *segment_A = static_cast<const SegmentShape2DSW*>(p_a);
+ const RectangleShape2DSW *rectangle_B = static_cast<const RectangleShape2DSW*>(p_b);
+
+ SeparatorAxisTest2D<SegmentShape2DSW,RectangleShape2DSW> separator(segment_A,p_transform_a,p_transform_inv_a,rectangle_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis(p_transform_inv_a.basis_xform_inv(segment_A->get_normal()).normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_b.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_b.elements[1].normalized()))
+ return;
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_segment_capsule(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const SegmentShape2DSW *segment_A = static_cast<const SegmentShape2DSW*>(p_a);
+ const CapsuleShape2DSW *capsule_B = static_cast<const CapsuleShape2DSW*>(p_b);
+
+ SeparatorAxisTest2D<SegmentShape2DSW,CapsuleShape2DSW> separator(segment_A,p_transform_a,p_transform_inv_a,capsule_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis(p_transform_inv_a.basis_xform_inv(segment_A->get_normal()).normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_b.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis((p_transform_a.xform(segment_A->get_a())-(p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*0.5)).normalized()))
+ return;
+ if (!separator.test_axis((p_transform_a.xform(segment_A->get_a())-(p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*-0.5)).normalized()))
+ return;
+ if (!separator.test_axis((p_transform_a.xform(segment_A->get_b())-(p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*0.5)).normalized()))
+ return;
+ if (!separator.test_axis((p_transform_a.xform(segment_A->get_b())-(p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*-0.5)).normalized()))
+ return;
+
+ separator.generate_contacts();
+}
+
+static void _collision_segment_convex_polygon(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const SegmentShape2DSW *segment_A = static_cast<const SegmentShape2DSW*>(p_a);
+ const ConvexPolygonShape2DSW *convex_B = static_cast<const ConvexPolygonShape2DSW*>(p_b);
+
+ SeparatorAxisTest2D<SegmentShape2DSW,ConvexPolygonShape2DSW> separator(segment_A,p_transform_a,p_transform_inv_a,convex_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis(p_transform_inv_a.basis_xform_inv(segment_A->get_normal()).normalized()))
+ return;
+
+ for(int i=0;i<convex_B->get_point_count();i++) {
+
+ if (!separator.test_axis( p_transform_inv_b.basis_xform_inv(convex_B->get_segment_normal(i)).normalized() ))
+ return;
+ }
+
+ separator.generate_contacts();
+
+}
+
+
+/////////
+
+static void _collision_circle_circle(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const CircleShape2DSW *circle_A = static_cast<const CircleShape2DSW*>(p_a);
+ const CircleShape2DSW *circle_B = static_cast<const CircleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<CircleShape2DSW,CircleShape2DSW> separator(circle_A,p_transform_a,p_transform_inv_a,circle_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ if (!separator.test_axis((p_transform_a.get_origin()-p_transform_b.get_origin()).normalized()))
+ return;
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_circle_rectangle(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const CircleShape2DSW *circle_A = static_cast<const CircleShape2DSW*>(p_a);
+ const RectangleShape2DSW *rectangle_B = static_cast<const RectangleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<CircleShape2DSW,RectangleShape2DSW> separator(circle_A,p_transform_a,p_transform_inv_a,rectangle_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ const Vector2 &sphere=p_transform_a.elements[2];
+ const Vector2 *axis=&p_transform_b.elements[0];
+ const Vector2& half_extents = rectangle_B->get_half_extents();
+
+ if (!separator.test_axis(axis[0].normalized()))
+ return;
+
+ if (!separator.test_axis(axis[1].normalized()))
+ return;
+
+ Vector2 local_v = p_transform_inv_b.xform(p_transform_a.get_origin());
+
+ Vector2 he(
+ (local_v.x<0) ? -half_extents.x : half_extents.x,
+ (local_v.y<0) ? -half_extents.y : half_extents.y
+ );
+
+
+ if (!separator.test_axis((p_transform_b.xform(he)-sphere).normalized()))
+ return;
+
+ separator.generate_contacts();
+}
+
+static void _collision_circle_capsule(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const CircleShape2DSW *circle_A = static_cast<const CircleShape2DSW*>(p_a);
+ const CapsuleShape2DSW *capsule_B = static_cast<const CapsuleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<CircleShape2DSW,CapsuleShape2DSW> separator(circle_A,p_transform_a,p_transform_inv_a,capsule_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //capsule axis
+ if (!separator.test_axis(p_transform_b.elements[0].normalized()))
+ return;
+
+ //capsule endpoints
+ if (!separator.test_axis((p_transform_a.get_origin()-(p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*0.5)).normalized()))
+ return;
+ if (!separator.test_axis((p_transform_a.get_origin()-(p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*-0.5)).normalized()))
+ return;
+
+
+ separator.generate_contacts();
+
+
+}
+
+static void _collision_circle_convex_polygon(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const CircleShape2DSW *circle_A = static_cast<const CircleShape2DSW*>(p_a);
+ const ConvexPolygonShape2DSW *convex_B = static_cast<const ConvexPolygonShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<CircleShape2DSW,ConvexPolygonShape2DSW> separator(circle_A,p_transform_a,p_transform_inv_a,convex_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //poly faces and poly points vs circle
+ for(int i=0;i<convex_B->get_point_count();i++) {
+
+ if (!separator.test_axis( (p_transform_b.xform(convex_B->get_point(i))-p_transform_a.get_origin()).normalized() ))
+ return;
+
+ if (!separator.test_axis( p_transform_inv_b.basis_xform_inv(convex_B->get_segment_normal(i)).normalized() ))
+ return;
+ }
+
+ separator.generate_contacts();
+}
+
+
+/////////
+
+static void _collision_rectangle_rectangle(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const RectangleShape2DSW *rectangle_A = static_cast<const RectangleShape2DSW*>(p_a);
+ const RectangleShape2DSW *rectangle_B = static_cast<const RectangleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<RectangleShape2DSW,RectangleShape2DSW> separator(rectangle_A,p_transform_a,p_transform_inv_a,rectangle_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //box faces A
+ if (!separator.test_axis(p_transform_a.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_a.elements[1].normalized()))
+ return;
+
+ //box faces B
+ if (!separator.test_axis(p_transform_b.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_b.elements[1].normalized()))
+ return;
+
+ separator.generate_contacts();
+}
+
+static void _collision_rectangle_capsule(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const RectangleShape2DSW *rectangle_A = static_cast<const RectangleShape2DSW*>(p_a);
+ const CapsuleShape2DSW *capsule_B = static_cast<const CapsuleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<RectangleShape2DSW,CapsuleShape2DSW> separator(rectangle_A,p_transform_a,p_transform_inv_a,capsule_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //box faces
+ if (!separator.test_axis(p_transform_a.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_a.elements[1].normalized()))
+ return;
+
+ //capsule axis
+ if (!separator.test_axis(p_transform_b.elements[0].normalized()))
+ return;
+
+
+ //box endpoints to capsule circles
+
+ for(int i=0;i<2;i++) {
+
+ Vector2 capsule_endpoint = p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*(i==0?0.5:-0.5);
+
+ const Vector2& half_extents = rectangle_A->get_half_extents();
+ Vector2 local_v = p_transform_inv_a.xform(capsule_endpoint);
+
+ Vector2 he(
+ (local_v.x<0) ? -half_extents.x : half_extents.x,
+ (local_v.y<0) ? -half_extents.y : half_extents.y
+ );
+
+
+ if (!separator.test_axis(p_transform_a.xform(he).normalized()))
+ return;
+
+ }
+
+
+ separator.generate_contacts();
+}
+
+static void _collision_rectangle_convex_polygon(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const RectangleShape2DSW *rectangle_A = static_cast<const RectangleShape2DSW*>(p_a);
+ const ConvexPolygonShape2DSW *convex_B = static_cast<const ConvexPolygonShape2DSW*>(p_b);
+
+ SeparatorAxisTest2D<RectangleShape2DSW,ConvexPolygonShape2DSW> separator(rectangle_A,p_transform_a,p_transform_inv_a,convex_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //box faces
+ if (!separator.test_axis(p_transform_a.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_a.elements[1].normalized()))
+ return;
+
+ //convex faces
+ for(int i=0;i<convex_B->get_point_count();i++) {
+
+ if (!separator.test_axis( p_transform_inv_b.basis_xform_inv(convex_B->get_segment_normal(i)).normalized() ))
+ return;
+ }
+
+ separator.generate_contacts();
+
+}
+
+
+/////////
+
+static void _collision_capsule_capsule(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const CapsuleShape2DSW *capsule_A = static_cast<const CapsuleShape2DSW*>(p_a);
+ const CapsuleShape2DSW *capsule_B = static_cast<const CapsuleShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<CapsuleShape2DSW,CapsuleShape2DSW> separator(capsule_A,p_transform_a,p_transform_inv_a,capsule_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //capsule axis
+
+ if (!separator.test_axis(p_transform_b.elements[0].normalized()))
+ return;
+
+ if (!separator.test_axis(p_transform_a.elements[0].normalized()))
+ return;
+
+ //capsule endpoints
+
+ for(int i=0;i<2;i++) {
+
+ Vector2 capsule_endpoint_A = p_transform_a.get_origin()+p_transform_a.elements[1]*capsule_A->get_height()*(i==0?0.5:-0.5);
+
+ for(int j=0;j<2;j++) {
+
+ Vector2 capsule_endpoint_B = p_transform_b.get_origin()+p_transform_b.elements[1]*capsule_B->get_height()*(j==0?0.5:-0.5);
+
+ if (!separator.test_axis( (capsule_endpoint_A-capsule_endpoint_B).normalized() ))
+ return;
+
+ }
+ }
+
+ separator.generate_contacts();
+
+}
+
+static void _collision_capsule_convex_polygon(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+ const CapsuleShape2DSW *capsule_A = static_cast<const CapsuleShape2DSW*>(p_a);
+ const ConvexPolygonShape2DSW *convex_B = static_cast<const ConvexPolygonShape2DSW*>(p_b);
+
+
+ SeparatorAxisTest2D<CapsuleShape2DSW,ConvexPolygonShape2DSW> separator(capsule_A,p_transform_a,p_transform_inv_a,convex_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ //capsule axis
+
+ if (!separator.test_axis(p_transform_a.elements[0].normalized()))
+ return;
+
+
+ //poly vs capsule
+ for(int i=0;i<convex_B->get_point_count();i++) {
+
+ Vector2 cpoint = p_transform_b.xform(convex_B->get_point(i));
+
+ for(int j=0;j<2;j++) {
+
+ Vector2 capsule_endpoint_A = p_transform_a.get_origin()+p_transform_a.elements[1]*capsule_A->get_height()*(j==0?0.5:-0.5);
+
+ if (!separator.test_axis( (cpoint - capsule_endpoint_A).normalized() ))
+ return;
+
+ }
+
+ if (!separator.test_axis( p_transform_inv_b.basis_xform_inv(convex_B->get_segment_normal(i)).normalized() ))
+ return;
+ }
+
+ separator.generate_contacts();
+}
+
+
+/////////
+
+
+static void _collision_convex_polygon_convex_polygon(const Shape2DSW* p_a,const Matrix32& p_transform_a,const Matrix32& p_transform_inv_a,const Shape2DSW* p_b,const Matrix32& p_transform_b,const Matrix32& p_transform_inv_b,_CollectorCallback2D *p_collector) {
+
+
+ const ConvexPolygonShape2DSW *convex_A = static_cast<const ConvexPolygonShape2DSW*>(p_a);
+ const ConvexPolygonShape2DSW *convex_B = static_cast<const ConvexPolygonShape2DSW*>(p_b);
+
+ SeparatorAxisTest2D<ConvexPolygonShape2DSW,ConvexPolygonShape2DSW> separator(convex_A,p_transform_a,p_transform_inv_a,convex_B,p_transform_b,p_transform_inv_b,p_collector);
+
+ if (!separator.test_previous_axis())
+ return;
+
+ for(int i=0;i<convex_A->get_point_count();i++) {
+
+ if (!separator.test_axis( p_transform_inv_a.basis_xform_inv(convex_A->get_segment_normal(i)).normalized() ))
+ return;
+ }
+
+ for(int i=0;i<convex_B->get_point_count();i++) {
+
+ if (!separator.test_axis( p_transform_inv_b.basis_xform_inv(convex_B->get_segment_normal(i)).normalized() ))
+ return;
+ }
+
+ separator.generate_contacts();
+
+}
+
+
+////////
+
+bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Matrix32& p_transform_A, const Matrix32& p_transform_inv_A, const Shape2DSW *p_shape_B, const Matrix32& p_transform_B, const Matrix32& p_transform_inv_B, CollisionSolver2DSW::CallbackResult p_result_callback,void *p_userdata, bool p_swap,Vector2 *sep_axis) {
+
+ Physics2DServer::ShapeType type_A=p_shape_A->get_type();
+
+ ERR_FAIL_COND_V(type_A==Physics2DServer::SHAPE_LINE,false);
+ //ERR_FAIL_COND_V(type_A==Physics2DServer::SHAPE_RAY,false);
+ ERR_FAIL_COND_V(p_shape_A->is_concave(),false);
+
+ Physics2DServer::ShapeType type_B=p_shape_B->get_type();
+
+ ERR_FAIL_COND_V(type_B==Physics2DServer::SHAPE_LINE,false);
+ //ERR_FAIL_COND_V(type_B==Physics2DServer::SHAPE_RAY,false);
+ ERR_FAIL_COND_V(p_shape_B->is_concave(),false);
+
+
+ static const CollisionFunc collision_table[5][5]={
+ {_collision_segment_segment,
+ _collision_segment_circle,
+ _collision_segment_rectangle,
+ _collision_segment_capsule,
+ _collision_segment_convex_polygon},
+ {0,
+ _collision_circle_circle,
+ _collision_circle_rectangle,
+ _collision_circle_capsule,
+ _collision_circle_convex_polygon},
+ {0,
+ 0,
+ _collision_rectangle_rectangle,
+ _collision_rectangle_capsule,
+ _collision_rectangle_convex_polygon},
+ {0,
+ 0,
+ 0,
+ _collision_capsule_capsule,
+ _collision_capsule_convex_polygon},
+ {0,
+ 0,
+ 0,
+ 0,
+ _collision_convex_polygon_convex_polygon}
+
+ };
+
+ _CollectorCallback2D callback;
+ callback.callback=p_result_callback;
+ callback.swap=p_swap;
+ callback.userdata=p_userdata;
+ callback.collided=false;
+ callback.sep_axis=sep_axis;
+
+ const Shape2DSW *A=p_shape_A;
+ const Shape2DSW *B=p_shape_B;
+ const Matrix32 *transform_A=&p_transform_A;
+ const Matrix32 *transform_B=&p_transform_B;
+ const Matrix32 *transform_inv_A=&p_transform_inv_A;
+ const Matrix32 *transform_inv_B=&p_transform_inv_B;
+
+ if (type_A > type_B) {
+ SWAP(A,B);
+ SWAP(transform_A,transform_B);
+ SWAP(transform_inv_A,transform_inv_B);
+ SWAP(type_A,type_B);
+ callback.swap = !callback.swap;
+ }
+
+
+ CollisionFunc collision_func = collision_table[type_A-2][type_B-2];
+ ERR_FAIL_COND_V(!collision_func,false);
+
+
+ collision_func(A,*transform_A,*transform_inv_A,B,*transform_B,*transform_inv_B,&callback);
+
+ return callback.collided;
+
+
+}
diff --git a/servers/physics_2d/collision_solver_2d_sat.h b/servers/physics_2d/collision_solver_2d_sat.h
new file mode 100644
index 0000000000..dc6767d651
--- /dev/null
+++ b/servers/physics_2d/collision_solver_2d_sat.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* collision_solver_2d_sat.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 COLLISION_SOLVER_2D_SAT_H
+#define COLLISION_SOLVER_2D_SAT_H
+
+#include "collision_solver_2d_sw.h"
+
+
+bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Matrix32& p_transform_A, const Matrix32& p_transform_inv_A, const Shape2DSW *p_shape_B, const Matrix32& p_transform_B, const Matrix32& p_transform_inv_B, CollisionSolver2DSW::CallbackResult p_result_callback,void *p_userdata, bool p_swap=false,Vector2 *sep_axis=NULL);
+
+#endif // COLLISION_SOLVER_2D_SAT_H
diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp
new file mode 100644
index 0000000000..cee5582c6f
--- /dev/null
+++ b/servers/physics_2d/collision_solver_2d_sw.cpp
@@ -0,0 +1,309 @@
+/*************************************************************************/
+/* collision_solver_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "collision_solver_2d_sw.h"
+#include "collision_solver_2d_sat.h"
+
+
+#define collision_solver sat_2d_calculate_penetration
+//#define collision_solver gjk_epa_calculate_penetration
+
+
+bool CollisionSolver2DSW::solve_static_line(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result) {
+
+
+ const LineShape2DSW *line = static_cast<const LineShape2DSW*>(p_shape_A);
+ if (p_shape_B->get_type()==Physics2DServer::SHAPE_LINE)
+ return false;
+
+
+ Vector2 n = p_transform_A.basis_xform(line->get_normal()).normalized();
+ Vector2 p = p_transform_A.xform(line->get_normal()*line->get_d());
+ real_t d = n.dot(p);
+
+ Vector2 supports[2];
+ int support_count;
+
+ p_shape_B->get_supports(p_transform_inv_B.basis_xform(-n).normalized(),supports,support_count);
+
+ bool found=false;
+
+
+ for(int i=0;i<support_count;i++) {
+
+ supports[i] = p_transform_B.xform( supports[i] );
+ real_t pd = n.dot(supports[i]);
+ if (pd>=d)
+ continue;
+ found=true;
+
+ Vector2 support_A = supports[i] - n*(pd-d);
+
+ if (p_result_callback) {
+ if (p_swap_result)
+ p_result_callback(supports[i],support_A,p_userdata);
+ else
+ p_result_callback(support_A,supports[i],p_userdata);
+ }
+
+ }
+
+
+ return found;
+}
+
+bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result,Vector2 *sep_axis) {
+
+
+
+ const RayShape2DSW *ray = static_cast<const RayShape2DSW*>(p_shape_A);
+ if (p_shape_B->get_type()==Physics2DServer::SHAPE_RAY)
+ return false;
+
+ Vector2 from = p_transform_A.get_origin();
+ Vector2 to = from+p_transform_A[1]*ray->get_length();
+ Vector2 support_A=to;
+
+ from = p_transform_inv_B.xform(from);
+ to = p_transform_inv_B.xform(to);
+
+ Vector2 p,n;
+ if (!p_shape_B->intersect_segment(from,to,p,n)) {
+
+ if (sep_axis)
+ *sep_axis=p_transform_A[1].normalized();
+ return false;
+ }
+
+
+ Vector2 support_B=p_transform_B.xform(p);
+
+ if (p_result_callback) {
+ if (p_swap_result)
+ p_result_callback(support_B,support_A,p_userdata);
+ else
+ p_result_callback(support_A,support_B,p_userdata);
+ }
+ return true;
+
+}
+
+/*
+bool CollisionSolver2DSW::solve_ray(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_inverse_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result) {
+
+
+ const RayShape2DSW *ray = static_cast<const RayShape2DSW*>(p_shape_A);
+
+ Vector2 from = p_transform_A.origin;
+ Vector2 to = from+p_transform_A.basis.get_axis(2)*ray->get_length();
+ Vector2 support_A=to;
+
+ from = p_inverse_B.xform(from);
+ to = p_inverse_B.xform(to);
+
+ Vector2 p,n;
+ if (!p_shape_B->intersect_segment(from,to,&p,&n))
+ return false;
+
+ Vector2 support_B=p_transform_B.xform(p);
+
+ if (p_result_callback) {
+ if (p_swap_result)
+ p_result_callback(support_B,support_A,p_userdata);
+ else
+ p_result_callback(support_A,support_B,p_userdata);
+ }
+ return true;
+}
+*/
+
+struct _ConcaveCollisionInfo2D {
+
+ const Matrix32 *transform_A;
+ const Matrix32 *transform_inv_A;
+ const Shape2DSW *shape_A;
+ const Matrix32 *transform_B;
+ const Matrix32 *transform_inv_B;
+ CollisionSolver2DSW::CallbackResult result_callback;
+ void *userdata;
+ bool swap_result;
+ bool collided;
+ int aabb_tests;
+ int collisions;
+ Vector2 *sep_axis;
+
+};
+
+void CollisionSolver2DSW::concave_callback(void *p_userdata, Shape2DSW *p_convex) {
+
+
+
+ _ConcaveCollisionInfo2D &cinfo = *(_ConcaveCollisionInfo2D*)(p_userdata);
+ cinfo.aabb_tests++;
+ if (!cinfo.result_callback && cinfo.collided)
+ return; //already collided and no contacts requested, don't test anymore
+
+ bool collided = collision_solver(cinfo.shape_A, *cinfo.transform_A, *cinfo.transform_inv_A, p_convex,*cinfo.transform_B,*cinfo.transform_inv_B, cinfo.result_callback, cinfo.userdata, cinfo.swap_result,cinfo.sep_axis );
+ if (!collided)
+ return;
+
+
+ cinfo.collided=true;
+ cinfo.collisions++;
+
+}
+
+bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result,Vector2 *sep_axis) {
+
+
+ const ConcaveShape2DSW *concave_B=static_cast<const ConcaveShape2DSW*>(p_shape_B);
+
+ _ConcaveCollisionInfo2D cinfo;
+ cinfo.transform_A=&p_transform_A;
+ cinfo.transform_inv_A=&p_transform_inv_A;
+ cinfo.shape_A=p_shape_A;
+ cinfo.transform_B=&p_transform_B;
+ cinfo.transform_inv_B=&p_transform_inv_B;
+ cinfo.result_callback=p_result_callback;
+ cinfo.userdata=p_userdata;
+ cinfo.swap_result=p_swap_result;
+ cinfo.collided=false;
+ cinfo.collisions=0;
+ cinfo.sep_axis=sep_axis;
+
+ cinfo.aabb_tests=0;
+
+ Matrix32 rel_transform = p_transform_A;
+ rel_transform.elements[2]-=p_transform_B.elements[2];
+
+ //quickly compute a local Rect2
+
+ Rect2 local_aabb;
+ for(int i=0;i<2;i++) {
+
+ Vector2 axis( p_transform_B.elements[i] );
+ float axis_scale = 1.0/axis.length();
+ axis*=axis_scale;
+
+ float smin,smax;
+ p_shape_A->project_rangev(axis,rel_transform,smin,smax);
+ smin*=axis_scale;
+ smax*=axis_scale;
+
+ local_aabb.pos[i]=smin;
+ local_aabb.size[i]=smax-smin;
+ }
+
+ concave_B->cull(local_aabb,concave_callback,&cinfo);
+
+
+// print_line("Rect2 TESTS: "+itos(cinfo.aabb_tests));
+ return cinfo.collided;
+}
+
+
+bool CollisionSolver2DSW::solve_static(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,Vector2 *sep_axis) {
+
+
+
+ Physics2DServer::ShapeType type_A=p_shape_A->get_type();
+ Physics2DServer::ShapeType type_B=p_shape_B->get_type();
+ bool concave_A=p_shape_A->is_concave();
+ bool concave_B=p_shape_B->is_concave();
+
+ bool swap = false;
+
+ if (type_A>type_B) {
+ SWAP(type_A,type_B);
+ SWAP(concave_A,concave_B);
+ swap=true;
+ }
+
+ if (type_A==Physics2DServer::SHAPE_LINE) {
+
+ if (type_B==Physics2DServer::SHAPE_LINE || type_B==Physics2DServer::SHAPE_RAY) {
+ return false;
+ //if (type_B==Physics2DServer::SHAPE_RAY) {
+ // return false;
+ }
+
+ if (swap) {
+ return solve_static_line(p_shape_B,p_transform_B,p_transform_inv_B,p_shape_A,p_transform_A,p_transform_inv_A,p_result_callback,p_userdata,true);
+ } else {
+ return solve_static_line(p_shape_A,p_transform_A,p_transform_inv_A,p_shape_B,p_transform_B,p_transform_inv_B,p_result_callback,p_userdata,false);
+ }
+
+ /*} else if (type_A==Physics2DServer::SHAPE_RAY) {
+
+ if (type_B==Physics2DServer::SHAPE_RAY)
+ return false;
+
+ if (swap) {
+ return solve_ray(p_shape_B,p_transform_B,p_shape_A,p_transform_A,p_inverse_A,p_result_callback,p_userdata,true);
+ } else {
+ return solve_ray(p_shape_A,p_transform_A,p_shape_B,p_transform_B,p_inverse_B,p_result_callback,p_userdata,false);
+ }
+*/
+ } else if (type_A==Physics2DServer::SHAPE_RAY) {
+
+ if (type_B==Physics2DServer::SHAPE_RAY) {
+
+ return false; //no ray-ray
+ }
+
+
+ if (swap) {
+ return solve_raycast(p_shape_B,p_transform_B,p_transform_inv_B,p_shape_A,p_transform_A,p_transform_inv_A,p_result_callback,p_userdata,true,sep_axis);
+ } else {
+ return solve_raycast(p_shape_A,p_transform_A,p_transform_inv_A,p_shape_B,p_transform_B,p_transform_inv_B,p_result_callback,p_userdata,false,sep_axis);
+ }
+
+
+ } else if (concave_B) {
+
+
+ if (concave_A)
+ return false;
+
+ if (!swap)
+ return solve_concave(p_shape_A,p_transform_A,p_transform_inv_A,p_shape_B,p_transform_B,p_transform_inv_B,p_result_callback,p_userdata,false,sep_axis);
+ else
+ return solve_concave(p_shape_B,p_transform_B,p_transform_inv_B,p_shape_A,p_transform_A,p_transform_inv_A,p_result_callback,p_userdata,true,sep_axis);
+
+
+
+ } else {
+
+
+ return collision_solver(p_shape_A, p_transform_A, p_transform_inv_A, p_shape_B, p_transform_B, p_transform_inv_B, p_result_callback,p_userdata,false,sep_axis);
+ }
+
+
+ return false;
+}
+
diff --git a/servers/physics_2d/collision_solver_2d_sw.h b/servers/physics_2d/collision_solver_2d_sw.h
new file mode 100644
index 0000000000..fc5500bfb9
--- /dev/null
+++ b/servers/physics_2d/collision_solver_2d_sw.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* collision_solver_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 COLLISION_SOLVER_2D_SW_H
+#define COLLISION_SOLVER_2D_SW_H
+
+#include "shape_2d_sw.h"
+
+class CollisionSolver2DSW {
+public:
+ typedef void (*CallbackResult)(const Vector2& p_point_A,const Vector2& p_point_B,void *p_userdata);
+private:
+ static bool solve_static_line(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result);
+ static void concave_callback(void *p_userdata, Shape2DSW *p_convex);
+ static bool solve_concave(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result,Vector2 *sep_axis=NULL);
+ static bool solve_raycast(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_transform_inv_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_transform_inv_B,CallbackResult p_result_callback,void *p_userdata,bool p_swap_result,Vector2 *sep_axis=NULL);
+
+
+
+public:
+
+ static bool solve_static(const Shape2DSW *p_shape_A,const Matrix32& p_transform_A,const Matrix32& p_inverse_A,const Shape2DSW *p_shape_B,const Matrix32& p_transform_B,const Matrix32& p_inverse_B,CallbackResult p_result_callback,void *p_userdata,Vector2 *sep_axis=NULL);
+
+
+};
+
+#endif // COLLISION_SOLVER_2D_SW_H
diff --git a/servers/physics_2d/constraint_2d_sw.cpp b/servers/physics_2d/constraint_2d_sw.cpp
new file mode 100644
index 0000000000..e97b5d794e
--- /dev/null
+++ b/servers/physics_2d/constraint_2d_sw.cpp
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* constraint_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "constraint_2d_sw.h"
+
diff --git a/servers/physics_2d/constraint_2d_sw.h b/servers/physics_2d/constraint_2d_sw.h
new file mode 100644
index 0000000000..7abe49f5b4
--- /dev/null
+++ b/servers/physics_2d/constraint_2d_sw.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* constraint_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 CONSTRAINT_2D_SW_H
+#define CONSTRAINT_2D_SW_H
+
+#include "body_2d_sw.h"
+
+class Constraint2DSW {
+
+ Body2DSW **_body_ptr;
+ int _body_count;
+ uint64_t island_step;
+ Constraint2DSW *island_next;
+ Constraint2DSW *island_list_next;
+
+
+ RID self;
+
+protected:
+ Constraint2DSW(Body2DSW **p_body_ptr=NULL,int p_body_count=0) { _body_ptr=p_body_ptr; _body_count=p_body_count; island_step=0; }
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ _FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
+ _FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step=p_step; }
+
+
+ _FORCE_INLINE_ Constraint2DSW* get_island_next() const { return island_next; }
+ _FORCE_INLINE_ void set_island_next(Constraint2DSW* p_next) { island_next=p_next; }
+
+ _FORCE_INLINE_ Constraint2DSW* get_island_list_next() const { return island_list_next; }
+ _FORCE_INLINE_ void set_island_list_next(Constraint2DSW* p_next) { island_list_next=p_next; }
+
+ _FORCE_INLINE_ Body2DSW **get_body_ptr() const { return _body_ptr; }
+ _FORCE_INLINE_ int get_body_count() const { return _body_count; }
+
+
+ virtual bool setup(float p_step)=0;
+ virtual void solve(float p_step)=0;
+
+ virtual ~Constraint2DSW() {}
+};
+
+#endif // CONSTRAINT_2D_SW_H
diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp
new file mode 100644
index 0000000000..fea58b6e8d
--- /dev/null
+++ b/servers/physics_2d/joints_2d_sw.cpp
@@ -0,0 +1,574 @@
+/*************************************************************************/
+/* joints_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "joints_2d_sw.h"
+#include "space_2d_sw.h"
+
+//based on chipmunk joint constraints
+
+/* Copyright (c) 2007 Scott Lembcke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+static inline real_t k_scalar(Body2DSW *a,Body2DSW *b,const Vector2& rA, const Vector2& rB, const Vector2& n) {
+
+
+ real_t value=0;
+
+
+ {
+ value+=a->get_inv_mass();
+ real_t rcn = rA.cross(n);
+ value+=a->get_inv_inertia() * rcn * rcn;
+ }
+
+ if (b) {
+
+ value+=b->get_inv_mass();
+ real_t rcn = rB.cross(n);
+ value+=b->get_inv_inertia() * rcn * rcn;
+ }
+
+ return value;
+
+}
+
+static inline Vector2
+relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB){
+ Vector2 sum = a->get_linear_velocity() -rA.tangent() * a->get_angular_velocity();
+ if (b)
+ return (b->get_linear_velocity() -rB.tangent() * b->get_angular_velocity()) - sum;
+ else
+ return -sum;
+}
+
+static inline real_t
+normal_relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB, Vector2 n){
+ return relative_velocity(a, b, rA, rB).dot(n);
+}
+
+#if 0
+
+bool PinJoint2DSW::setup(float p_step) {
+
+ Space2DSW *space = A->get_space();
+ ERR_FAIL_COND_V(!space,false;)
+ rA = A->get_transform().basis_xform(anchor_A);
+ rB = B?B->get_transform().basis_xform(anchor_B):anchor_B;
+
+ Vector2 gA = A->get_transform().get_origin();
+ Vector2 gB = B?B->get_transform().get_origin():Vector2();
+
+ Vector2 delta = gB - gA;
+ delta = (delta+rB) -rA;
+
+ real_t jdist = delta.length();
+ correct=false;
+ if (jdist==0)
+ return false; // do not correct
+
+ correct=true;
+
+ n = delta / jdist;
+
+ // calculate mass normal
+ mass_normal = 1.0f/k_scalar(A, B, rA, rB, n);
+
+ // calculate bias velocity
+ //real_t maxBias = joint->constraint.maxBias;
+ bias = -(get_bias()==0?space->get_constraint_bias():get_bias())*(1.0/p_step)*(jdist-dist);
+ bias = CLAMP(bias, -get_max_bias(), +get_max_bias());
+
+ // compute max impulse
+ jn_max = get_max_force() * p_step;
+
+ // apply accumulated impulse
+ Vector2 j = n * jn_acc;
+ A->apply_impulse(rA,-j);
+ if (B)
+ B->apply_impulse(rB,j);
+
+ print_line("setup");
+ return true;
+}
+
+
+
+void PinJoint2DSW::solve(float p_step){
+
+ if (!correct)
+ return;
+
+ Vector2 ln = n;
+
+ // compute relative velocity
+ real_t vrn = normal_relative_velocity(A,B, rA, rB, ln);
+
+ // compute normal impulse
+ real_t jn = (bias - vrn)*mass_normal;
+ real_t jnOld = jn_acc;
+ jn_acc = CLAMP(jnOld + jn,-jn_max,jn_max); //cpfclamp(jnOld + jn, -joint->jnMax, joint->jnMax);
+ jn = jn_acc - jnOld;
+ print_line("jn_acc: "+rtos(jn_acc));
+ Vector2 j = jn*ln;
+
+ A->apply_impulse(rA,-j);
+ if (B)
+ B->apply_impulse(rB,j);
+
+}
+
+
+PinJoint2DSW::PinJoint2DSW(const Vector2& p_pos,Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,p_body_b?2:1) {
+
+ A=p_body_a;
+ B=p_body_b;
+ anchor_A = p_body_a->get_inv_transform().xform(p_pos);
+ anchor_B = p_body_b?p_body_b->get_inv_transform().xform(p_pos):p_pos;
+
+ jn_acc=0;
+ dist=0;
+
+ p_body_a->add_constraint(this,0);
+ if (p_body_b)
+ p_body_b->add_constraint(this,1);
+
+}
+
+PinJoint2DSW::~PinJoint2DSW() {
+
+ if (A)
+ A->remove_constraint(this);
+ if (B)
+ B->remove_constraint(this);
+
+}
+
+#else
+
+
+bool PinJoint2DSW::setup(float p_step) {
+
+ Space2DSW *space = A->get_space();
+ ERR_FAIL_COND_V(!space,false;)
+ rA = A->get_transform().basis_xform(anchor_A);
+ rB = B?B->get_transform().basis_xform(anchor_B):anchor_B;
+#if 0
+ Vector2 gA = rA+A->get_transform().get_origin();
+ Vector2 gB = B?rB+B->get_transform().get_origin():rB;
+
+ VectorB delta = gB - gA;
+
+ real_t jdist = delta.length();
+ correct=false;
+ if (jdist==0)
+ return false; // do not correct
+#endif
+
+ // deltaV = deltaV0 + K * impulse
+ // invM = [(1/m1 + 1/m2) * eye(2) - skew(rA) * invI1 * skew(rA) - skew(rB) * invI2 * skew(rB)]
+ // = [1/m1+1/m2 0 ] + invI1 * [rA.y*rA.y -rA.x*rA.y] + invI2 * [rA.y*rA.y -rA.x*rA.y]
+ // [ 0 1/m1+1/m2] [-rA.x*rA.y rA.x*rA.x] [-rA.x*rA.y rA.x*rA.x]
+
+ real_t B_inv_mass = B?B->get_inv_mass():0.0;
+
+
+ Matrix32 K1;
+ K1[0].x = A->get_inv_mass() + B_inv_mass; K1[1].x = 0.0f;
+ K1[0].y = 0.0f; K1[1].y = A->get_inv_mass() + B_inv_mass;
+
+ Matrix32 K2;
+ K2[0].x = A->get_inv_inertia() * rA.y * rA.y; K2[1].x = -A->get_inv_inertia() * rA.x * rA.y;
+ K2[0].y = -A->get_inv_inertia() * rA.x * rA.y; K2[1].y = A->get_inv_inertia() * rA.x * rA.x;
+
+ Matrix32 K;
+ K[0]= K1[0] + K2[0];
+ K[1]= K1[1] + K2[1];
+
+ if (B) {
+
+ Matrix32 K3;
+ K3[0].x = B->get_inv_inertia() * rB.y * rB.y; K3[1].x = -B->get_inv_inertia() * rB.x * rB.y;
+ K3[0].y = -B->get_inv_inertia() * rB.x * rB.y; K3[1].y = B->get_inv_inertia() * rB.x * rB.x;
+
+ K[0]+=K3[0];
+ K[1]+=K3[1];
+ }
+
+ K[0].x += softness;
+ K[1].y += softness;
+
+ M = K.affine_inverse();
+
+ Vector2 gA = rA+A->get_transform().get_origin();
+ Vector2 gB = B?rB+B->get_transform().get_origin():rB;
+
+ Vector2 delta = gB - gA;
+
+ bias = delta*-(get_bias()==0?space->get_constraint_bias():get_bias())*(1.0/p_step);
+
+ // apply accumulated impulse
+ A->apply_impulse(rA,-P);
+ if (B)
+ B->apply_impulse(rB,P);
+
+ return true;
+}
+
+void PinJoint2DSW::solve(float p_step){
+
+
+ // compute relative velocity
+ Vector2 vA = A->get_linear_velocity() - rA.cross(A->get_angular_velocity());
+
+ Vector2 rel_vel;
+ if (B)
+ rel_vel = B->get_linear_velocity() - rB.cross(B->get_angular_velocity()) - vA;
+ else
+ rel_vel = -vA;
+
+ Vector2 impulse = M.basis_xform(bias - rel_vel - Vector2(softness,softness) * P);
+
+ A->apply_impulse(rA,-impulse);
+ if (B)
+ B->apply_impulse(rB,impulse);
+
+
+ P += impulse;
+}
+
+
+PinJoint2DSW::PinJoint2DSW(const Vector2& p_pos,Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,p_body_b?2:1) {
+
+ A=p_body_a;
+ B=p_body_b;
+ anchor_A = p_body_a->get_inv_transform().xform(p_pos);
+ anchor_B = p_body_b?p_body_b->get_inv_transform().xform(p_pos):p_pos;
+
+ softness=0;
+
+ p_body_a->add_constraint(this,0);
+ if (p_body_b)
+ p_body_b->add_constraint(this,1);
+
+}
+
+
+
+PinJoint2DSW::~PinJoint2DSW() {
+
+ if (A)
+ A->remove_constraint(this);
+ if (B)
+ B->remove_constraint(this);
+
+}
+
+
+
+#endif
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+
+static inline void
+k_tensor(Body2DSW *a, Body2DSW *b, Vector2 r1, Vector2 r2, Vector2 *k1, Vector2 *k2)
+{
+ // calculate mass matrix
+ // If I wasn't lazy and wrote a proper matrix class, this wouldn't be so gross...
+ real_t k11, k12, k21, k22;
+ real_t m_sum = a->get_inv_mass() + b->get_inv_mass();
+
+ // start with I*m_sum
+ k11 = m_sum; k12 = 0.0f;
+ k21 = 0.0f; k22 = m_sum;
+
+ // add the influence from r1
+ real_t a_i_inv = a->get_inv_inertia();
+ real_t r1xsq = r1.x * r1.x * a_i_inv;
+ real_t r1ysq = r1.y * r1.y * a_i_inv;
+ real_t r1nxy = -r1.x * r1.y * a_i_inv;
+ k11 += r1ysq; k12 += r1nxy;
+ k21 += r1nxy; k22 += r1xsq;
+
+ // add the influnce from r2
+ real_t b_i_inv = b->get_inv_inertia();
+ real_t r2xsq = r2.x * r2.x * b_i_inv;
+ real_t r2ysq = r2.y * r2.y * b_i_inv;
+ real_t r2nxy = -r2.x * r2.y * b_i_inv;
+ k11 += r2ysq; k12 += r2nxy;
+ k21 += r2nxy; k22 += r2xsq;
+
+ // invert
+ real_t determinant = k11*k22 - k12*k21;
+ ERR_FAIL_COND(determinant== 0.0);
+
+ real_t det_inv = 1.0f/determinant;
+ *k1 = Vector2( k22*det_inv, -k12*det_inv);
+ *k2 = Vector2(-k21*det_inv, k11*det_inv);
+}
+
+static _FORCE_INLINE_ Vector2
+mult_k(const Vector2& vr, const Vector2 &k1, const Vector2 &k2)
+{
+ return Vector2(vr.dot(k1), vr.dot(k2));
+}
+
+bool GrooveJoint2DSW::setup(float p_step) {
+
+
+ // calculate endpoints in worldspace
+ Vector2 ta = A->get_transform().xform(A_groove_1);
+ Vector2 tb = A->get_transform().xform(A_groove_2);
+ Space2DSW *space=A->get_space();
+
+ // calculate axis
+ Vector2 n = -(tb - ta).tangent().normalized();
+ real_t d = ta.dot(n);
+
+ xf_normal = n;
+ rB = B->get_transform().basis_xform(B_anchor);
+
+ // calculate tangential distance along the axis of rB
+ real_t td = (B->get_transform().get_origin() + rB).cross(n);
+ // calculate clamping factor and rB
+ if(td <= ta.cross(n)){
+ clamp = 1.0f;
+ rA = ta - A->get_transform().get_origin();
+ } else if(td >= tb.cross(n)){
+ clamp = -1.0f;
+ rA = tb - A->get_transform().get_origin();
+ } else {
+ clamp = 0.0f;
+ //joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p);
+ rA = ((-n.tangent() * -td) + n*d) - A->get_transform().get_origin();
+ }
+
+ // Calculate mass tensor
+ k_tensor(A, B, rA, rB, &k1, &k2);
+
+ // compute max impulse
+ jn_max = get_max_force() * p_step;
+
+ // calculate bias velocity
+// cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
+// joint->bias = cpvclamp(cpvmult(delta, -joint->constraint.biasCoef*dt_inv), joint->constraint.maxBias);
+
+
+ Vector2 delta = (B->get_transform().get_origin() +rB) - (A->get_transform().get_origin() + rA);
+ float _b = get_bias();
+ _b=0.001;
+ gbias=(delta*-(_b==0?space->get_constraint_bias():_b)*(1.0/p_step)).clamped(get_max_bias());
+
+ // apply accumulated impulse
+ A->apply_impulse(rA,-jn_acc);
+ B->apply_impulse(rB,jn_acc);
+
+ correct=true;
+ return true;
+}
+
+void GrooveJoint2DSW::solve(float p_step){
+
+
+ // compute impulse
+ Vector2 vr = relative_velocity(A, B, rA,rB);
+
+ Vector2 j = mult_k(gbias-vr, k1, k2);
+ Vector2 jOld = jn_acc;
+ j+=jOld;
+
+ jn_acc = (((clamp * j.cross(xf_normal)) > 0) ? j : xf_normal.project(j)).clamped(jn_max);
+
+ j = jn_acc - jOld;
+
+ A->apply_impulse(rA,-j);
+ B->apply_impulse(rB,j);
+}
+
+
+GrooveJoint2DSW::GrooveJoint2DSW(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,2) {
+
+ A=p_body_a;
+ B=p_body_b;
+
+ A_groove_1 = A->get_inv_transform().xform(p_a_groove1);
+ A_groove_2 = A->get_inv_transform().xform(p_a_groove2);
+ B_anchor=B->get_inv_transform().xform(p_b_anchor);
+ A_groove_normal = -(A_groove_2 - A_groove_1).normalized().tangent();
+
+ A->add_constraint(this,0);
+ B->add_constraint(this,1);
+
+}
+
+GrooveJoint2DSW::~GrooveJoint2DSW() {
+
+ A->remove_constraint(this);
+ B->remove_constraint(this);
+}
+
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+
+bool DampedSpringJoint2DSW::setup(float p_step) {
+
+ rA = A->get_transform().basis_xform(anchor_A);
+ rB = B->get_transform().basis_xform(anchor_B);
+
+ Vector2 delta = (B->get_transform().get_origin() + rB) - (A->get_transform().get_origin() + rA) ;
+ real_t dist = delta.length();
+
+ if (dist)
+ n=delta/dist;
+ else
+ n=Vector2();
+
+ real_t k = k_scalar(A, B, rA, rB, n);
+ n_mass = 1.0f/k;
+
+ target_vrn = 0.0f;
+ v_coef = 1.0f - Math::exp(-damping*(p_step)*k);
+
+ // apply spring force
+ real_t f_spring = (rest_length - dist) * stiffness;
+ Vector2 j = n * f_spring*(p_step);
+
+ A->apply_impulse(rA,-j);
+ B->apply_impulse(rB,j);
+
+
+ return true;
+}
+
+void DampedSpringJoint2DSW::solve(float p_step) {
+
+ // compute relative velocity
+ real_t vrn = normal_relative_velocity(A, B, rA, rB, n) - target_vrn;
+
+ // compute velocity loss from drag
+ // not 100% certain this is derived correctly, though it makes sense
+ real_t v_damp = -vrn*v_coef;
+ target_vrn = vrn + v_damp;
+ Vector2 j=n*v_damp*n_mass;
+
+ A->apply_impulse(rA,-j);
+ B->apply_impulse(rB,j);
+
+}
+
+void DampedSpringJoint2DSW::set_param(Physics2DServer::DampedStringParam p_param, real_t p_value) {
+
+ switch(p_param) {
+
+ case Physics2DServer::DAMPED_STRING_REST_LENGTH: {
+
+ rest_length=p_value;
+ } break;
+ case Physics2DServer::DAMPED_STRING_DAMPING: {
+
+ damping=p_value;
+ } break;
+ case Physics2DServer::DAMPED_STRING_STIFFNESS: {
+
+ stiffness=p_value;
+ } break;
+ }
+
+}
+
+real_t DampedSpringJoint2DSW::get_param(Physics2DServer::DampedStringParam p_param) const{
+
+ switch(p_param) {
+
+ case Physics2DServer::DAMPED_STRING_REST_LENGTH: {
+
+ return rest_length;
+ } break;
+ case Physics2DServer::DAMPED_STRING_DAMPING: {
+
+ return damping;
+ } break;
+ case Physics2DServer::DAMPED_STRING_STIFFNESS: {
+
+ return stiffness;
+ } break;
+ }
+
+ ERR_FAIL_V(0);
+}
+
+
+DampedSpringJoint2DSW::DampedSpringJoint2DSW(const Vector2& p_anchor_a,const Vector2& p_anchor_b, Body2DSW* p_body_a,Body2DSW* p_body_b) : Joint2DSW(_arr,2) {
+
+
+ A=p_body_a;
+ B=p_body_b;
+ anchor_A = A->get_inv_transform().xform(p_anchor_a);
+ anchor_B = B->get_inv_transform().xform(p_anchor_b);
+
+ rest_length=p_anchor_a.distance_to(p_anchor_b);
+ stiffness=20;
+ damping=1.5;
+
+
+ A->add_constraint(this,0);
+ B->add_constraint(this,1);
+
+}
+
+DampedSpringJoint2DSW::~DampedSpringJoint2DSW() {
+
+ A->remove_constraint(this);
+ B->remove_constraint(this);
+
+}
+
+
diff --git a/servers/physics_2d/joints_2d_sw.h b/servers/physics_2d/joints_2d_sw.h
new file mode 100644
index 0000000000..0a9bf34250
--- /dev/null
+++ b/servers/physics_2d/joints_2d_sw.h
@@ -0,0 +1,210 @@
+/*************************************************************************/
+/* joints_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 JOINTS_2D_SW_H
+#define JOINTS_2D_SW_H
+
+#include "constraint_2d_sw.h"
+#include "body_2d_sw.h"
+
+
+
+class Joint2DSW : public Constraint2DSW {
+
+ real_t max_force;
+ real_t bias;
+ real_t max_bias;
+public:
+
+ _FORCE_INLINE_ void set_max_force(real_t p_force) { max_force=p_force; }
+ _FORCE_INLINE_ real_t get_max_force() const { return max_force; }
+
+ _FORCE_INLINE_ void set_bias(real_t p_bias) { bias=p_bias; }
+ _FORCE_INLINE_ real_t get_bias() const { return bias; }
+
+ _FORCE_INLINE_ void set_max_bias(real_t p_bias) { max_bias=p_bias; }
+ _FORCE_INLINE_ real_t get_max_bias() const { return max_bias; }
+
+ virtual Physics2DServer::JointType get_type() const=0;
+ Joint2DSW(Body2DSW **p_body_ptr=NULL,int p_body_count=0) : Constraint2DSW(p_body_ptr,p_body_count) { bias=0; max_force=max_bias=3.40282e+38; };
+
+};
+#if 0
+
+class PinJoint2DSW : public Joint2DSW {
+
+ union {
+ struct {
+ Body2DSW *A;
+ Body2DSW *B;
+ };
+
+ Body2DSW *_arr[2];
+ };
+
+ Vector2 anchor_A;
+ Vector2 anchor_B;
+ real_t dist;
+ real_t jn_acc;
+ real_t jn_max;
+ real_t max_distance;
+ real_t mass_normal;
+ real_t bias;
+
+ Vector2 rA,rB;
+ Vector2 n; //normal
+ bool correct;
+
+
+public:
+
+ virtual Physics2DServer::JointType get_type() const { return Physics2DServer::JOINT_PIN; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+
+ PinJoint2DSW(const Vector2& p_pos,Body2DSW* p_body_a,Body2DSW* p_body_b=NULL);
+ ~PinJoint2DSW();
+};
+
+#else
+
+class PinJoint2DSW : public Joint2DSW {
+
+ union {
+ struct {
+ Body2DSW *A;
+ Body2DSW *B;
+ };
+
+ Body2DSW *_arr[2];
+ };
+
+ Matrix32 M;
+ Vector2 rA,rB;
+ Vector2 anchor_A;
+ Vector2 anchor_B;
+ Vector2 bias;
+ Vector2 P;
+ real_t softness;
+
+public:
+
+ virtual Physics2DServer::JointType get_type() const { return Physics2DServer::JOINT_PIN; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+
+ PinJoint2DSW(const Vector2& p_pos,Body2DSW* p_body_a,Body2DSW* p_body_b=NULL);
+ ~PinJoint2DSW();
+};
+
+
+#endif
+class GrooveJoint2DSW : public Joint2DSW {
+
+ union {
+ struct {
+ Body2DSW *A;
+ Body2DSW *B;
+ };
+
+ Body2DSW *_arr[2];
+ };
+
+ Vector2 A_groove_1;
+ Vector2 A_groove_2;
+ Vector2 A_groove_normal;
+ Vector2 B_anchor;
+ Vector2 jn_acc;
+ Vector2 gbias;
+ real_t jn_max;
+ real_t clamp;
+ Vector2 xf_normal;
+ Vector2 rA,rB;
+ Vector2 k1,k2;
+
+
+ bool correct;
+
+public:
+
+ virtual Physics2DServer::JointType get_type() const { return Physics2DServer::JOINT_GROOVE; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+
+ GrooveJoint2DSW(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, Body2DSW* p_body_a,Body2DSW* p_body_b);
+ ~GrooveJoint2DSW();
+};
+
+
+class DampedSpringJoint2DSW : public Joint2DSW {
+
+ union {
+ struct {
+ Body2DSW *A;
+ Body2DSW *B;
+ };
+
+ Body2DSW *_arr[2];
+ };
+
+
+ Vector2 anchor_A;
+ Vector2 anchor_B;
+
+ real_t rest_length;
+ real_t damping;
+ real_t stiffness;
+
+ Vector2 rA,rB;
+ Vector2 n;
+ real_t n_mass;
+ real_t target_vrn;
+ real_t v_coef;
+
+public:
+
+ virtual Physics2DServer::JointType get_type() const { return Physics2DServer::JOINT_DAMPED_SPRING; }
+
+ virtual bool setup(float p_step);
+ virtual void solve(float p_step);
+
+ void set_param(Physics2DServer::DampedStringParam p_param, real_t p_value);
+ real_t get_param(Physics2DServer::DampedStringParam p_param) const;
+
+ DampedSpringJoint2DSW(const Vector2& p_anchor_a,const Vector2& p_anchor_b, Body2DSW* p_body_a,Body2DSW* p_body_b);
+ ~DampedSpringJoint2DSW();
+};
+
+
+#endif // JOINTS_2D_SW_H
diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp
new file mode 100644
index 0000000000..70e3d843b4
--- /dev/null
+++ b/servers/physics_2d/physics_2d_server_sw.cpp
@@ -0,0 +1,1046 @@
+/*************************************************************************/
+/* physics_2d_server_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_2d_server_sw.h"
+#include "broad_phase_2d_basic.h"
+#include "broad_phase_2d_hash_grid.h"
+
+RID Physics2DServerSW::shape_create(ShapeType p_shape) {
+
+ Shape2DSW *shape=NULL;
+ switch(p_shape) {
+
+ case SHAPE_LINE: {
+
+ shape=memnew( LineShape2DSW );
+ } break;
+ case SHAPE_RAY: {
+
+ shape=memnew( RayShape2DSW );
+ } break;
+ case SHAPE_SEGMENT: {
+
+ shape=memnew( SegmentShape2DSW);
+ } break;
+ case SHAPE_CIRCLE: {
+
+ shape=memnew( CircleShape2DSW);
+ } break;
+ case SHAPE_RECTANGLE: {
+
+ shape=memnew( RectangleShape2DSW);
+ } break;
+ case SHAPE_CAPSULE: {
+
+ shape=memnew( CapsuleShape2DSW );
+ } break;
+ case SHAPE_CONVEX_POLYGON: {
+
+ shape=memnew( ConvexPolygonShape2DSW );
+ } break;
+ case SHAPE_CONCAVE_POLYGON: {
+
+ shape=memnew( ConcavePolygonShape2DSW );
+ } break;
+ case SHAPE_CUSTOM: {
+
+ ERR_FAIL_V(RID());
+
+ } break;
+
+ }
+
+ RID id = shape_owner.make_rid(shape);
+ shape->set_self(id);
+
+ return id;
+};
+
+void Physics2DServerSW::shape_set_data(RID p_shape, const Variant& p_data) {
+
+ Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_data(p_data);
+
+
+};
+
+
+void Physics2DServerSW::shape_set_custom_solver_bias(RID p_shape, real_t p_bias) {
+
+ Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_custom_bias(p_bias);
+
+}
+
+
+Physics2DServer::ShapeType Physics2DServerSW::shape_get_type(RID p_shape) const {
+
+ const Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,SHAPE_CUSTOM);
+ return shape->get_type();
+
+};
+
+Variant Physics2DServerSW::shape_get_data(RID p_shape) const {
+
+ const Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,Variant());
+ ERR_FAIL_COND_V(!shape->is_configured(),Variant());
+ return shape->get_data();
+
+};
+
+real_t Physics2DServerSW::shape_get_custom_solver_bias(RID p_shape) const {
+
+ const Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,0);
+ return shape->get_custom_bias();
+
+}
+
+
+RID Physics2DServerSW::space_create() {
+
+ Space2DSW *space = memnew( Space2DSW );
+ RID id = space_owner.make_rid(space);
+ space->set_self(id);
+ RID area_id = area_create();
+ Area2DSW *area = area_owner.get(area_id);
+ ERR_FAIL_COND_V(!area,RID());
+ space->set_default_area(area);
+ area->set_space(space);
+ area->set_priority(-1);
+
+ return id;
+};
+
+void Physics2DServerSW::space_set_active(RID p_space,bool p_active) {
+
+ Space2DSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ if (p_active)
+ active_spaces.insert(space);
+ else
+ active_spaces.erase(space);
+}
+
+bool Physics2DServerSW::space_is_active(RID p_space) const {
+
+ const Space2DSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,false);
+
+ return active_spaces.has(space);
+
+}
+
+void Physics2DServerSW::space_set_param(RID p_space,SpaceParameter p_param, real_t p_value) {
+
+ Space2DSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+
+ space->set_param(p_param,p_value);
+
+}
+
+real_t Physics2DServerSW::space_get_param(RID p_space,SpaceParameter p_param) const {
+
+ const Space2DSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,0);
+ return space->get_param(p_param);
+}
+
+Physics2DDirectSpaceState* Physics2DServerSW::space_get_direct_state(RID p_space) {
+
+ Space2DSW *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,NULL);
+ if (/*doing_sync ||*/ space->is_locked()) {
+
+ ERR_EXPLAIN("Space state is inaccesible right now, wait for iteration or fixed process notification.");
+ ERR_FAIL_V(NULL);
+ }
+
+ return space->get_direct_state();
+}
+
+RID Physics2DServerSW::area_create() {
+
+ Area2DSW *area = memnew( Area2DSW );
+ RID rid = area_owner.make_rid(area);
+ area->set_self(rid);
+ return rid;
+};
+
+void Physics2DServerSW::area_set_space(RID p_area, RID p_space) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ Space2DSW *space=NULL;
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+
+ area->set_space(space);
+
+};
+
+RID Physics2DServerSW::area_get_space(RID p_area) const {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,RID());
+
+ Space2DSW *space = area->get_space();
+ if (!space)
+ return RID();
+ return space->get_self();
+};
+
+void Physics2DServerSW::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) {
+
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_space_override_mode(p_mode);
+}
+
+Physics2DServer::AreaSpaceOverrideMode Physics2DServerSW::area_get_space_override_mode(RID p_area) const {
+
+ const Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,AREA_SPACE_OVERRIDE_DISABLED);
+
+ return area->get_space_override_mode();
+}
+
+
+void Physics2DServerSW::area_add_shape(RID p_area, RID p_shape, const Matrix32& p_transform) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ area->add_shape(shape,p_transform);
+
+}
+
+void Physics2DServerSW::area_set_shape(RID p_area, int p_shape_idx,RID p_shape) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ ERR_FAIL_COND(!shape->is_configured());
+
+ area->set_shape(p_shape_idx,shape);
+
+}
+void Physics2DServerSW::area_set_shape_transform(RID p_area, int p_shape_idx, const Matrix32& p_transform) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_shape_transform(p_shape_idx,p_transform);
+}
+
+int Physics2DServerSW::area_get_shape_count(RID p_area) const {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,-1);
+
+ return area->get_shape_count();
+
+}
+RID Physics2DServerSW::area_get_shape(RID p_area, int p_shape_idx) const {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,RID());
+
+ Shape2DSW *shape = area->get_shape(p_shape_idx);
+ ERR_FAIL_COND_V(!shape,RID());
+
+ return shape->get_self();
+}
+Matrix32 Physics2DServerSW::area_get_shape_transform(RID p_area, int p_shape_idx) const {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,Matrix32());
+
+ return area->get_shape_transform(p_shape_idx);
+}
+
+void Physics2DServerSW::area_remove_shape(RID p_area, int p_shape_idx) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->remove_shape(p_shape_idx);
+}
+
+void Physics2DServerSW::area_clear_shapes(RID p_area) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ while(area->get_shape_count())
+ area->remove_shape(0);
+
+}
+
+void Physics2DServerSW::area_attach_object_instance_ID(RID p_area,ObjectID p_ID) {
+
+ if (space_owner.owns(p_area)) {
+ Space2DSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_instance_id(p_ID);
+
+}
+ObjectID Physics2DServerSW::area_get_object_instance_ID(RID p_area) const {
+
+ if (space_owner.owns(p_area)) {
+ Space2DSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,0);
+ return area->get_instance_id();
+
+
+}
+
+
+void Physics2DServerSW::area_set_param(RID p_area,AreaParameter p_param,const Variant& p_value) {
+
+ if (space_owner.owns(p_area)) {
+ Space2DSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_param(p_param,p_value);
+
+};
+
+
+void Physics2DServerSW::area_set_transform(RID p_area, const Matrix32& p_transform) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_transform(p_transform);
+
+};
+
+Variant Physics2DServerSW::area_get_param(RID p_area,AreaParameter p_param) const {
+
+ if (space_owner.owns(p_area)) {
+ Space2DSW *space=space_owner.get(p_area);
+ p_area=space->get_default_area()->get_self();
+ }
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,Variant());
+
+ return area->get_param(p_param);
+};
+
+Matrix32 Physics2DServerSW::area_get_transform(RID p_area) const {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area,Matrix32());
+
+ return area->get_transform();
+};
+
+void Physics2DServerSW::area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_monitor_callback(p_receiver?p_receiver->get_instance_ID():0,p_method);
+
+
+}
+
+
+/* BODY API */
+
+RID Physics2DServerSW::body_create(BodyMode p_mode,bool p_init_sleeping) {
+
+ Body2DSW *body = memnew( Body2DSW );
+ if (p_mode!=BODY_MODE_RIGID)
+ body->set_mode(p_mode);
+ if (p_init_sleeping)
+ body->set_state(BODY_STATE_SLEEPING,p_init_sleeping);
+ RID rid = body_owner.make_rid(body);
+ body->set_self(rid);
+ return rid;
+};
+
+
+void Physics2DServerSW::body_set_space(RID p_body, RID p_space) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ Space2DSW *space=NULL;
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+
+ body->set_space(space);
+
+};
+
+RID Physics2DServerSW::body_get_space(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,RID());
+
+ Space2DSW *space = body->get_space();
+ if (!space)
+ return RID();
+ return space->get_self();
+};
+
+
+void Physics2DServerSW::body_set_mode(RID p_body, BodyMode p_mode) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_mode(p_mode);
+};
+
+Physics2DServer::BodyMode Physics2DServerSW::body_get_mode(RID p_body, BodyMode p_mode) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,BODY_MODE_STATIC);
+
+ return body->get_mode();
+};
+
+void Physics2DServerSW::body_add_shape(RID p_body, RID p_shape, const Matrix32& p_transform) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ body->add_shape(shape,p_transform);
+
+}
+
+void Physics2DServerSW::body_set_shape(RID p_body, int p_shape_idx,RID p_shape) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ Shape2DSW *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ ERR_FAIL_COND(!shape->is_configured());
+
+ body->set_shape(p_shape_idx,shape);
+
+}
+void Physics2DServerSW::body_set_shape_transform(RID p_body, int p_shape_idx, const Matrix32& p_transform) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_shape_transform(p_shape_idx,p_transform);
+}
+
+int Physics2DServerSW::body_get_shape_count(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,-1);
+
+ return body->get_shape_count();
+
+}
+RID Physics2DServerSW::body_get_shape(RID p_body, int p_shape_idx) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,RID());
+
+ Shape2DSW *shape = body->get_shape(p_shape_idx);
+ ERR_FAIL_COND_V(!shape,RID());
+
+ return shape->get_self();
+}
+Matrix32 Physics2DServerSW::body_get_shape_transform(RID p_body, int p_shape_idx) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Matrix32());
+
+ return body->get_shape_transform(p_shape_idx);
+}
+
+void Physics2DServerSW::body_remove_shape(RID p_body, int p_shape_idx) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->remove_shape(p_shape_idx);
+}
+
+void Physics2DServerSW::body_clear_shapes(RID p_body) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ while(body->get_shape_count())
+ body->remove_shape(0);
+
+}
+
+
+void Physics2DServerSW::body_set_shape_as_trigger(RID p_body, int p_shape_idx,bool p_enable) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ ERR_FAIL_INDEX(p_shape_idx,body->get_shape_count());
+
+ body->set_shape_as_trigger(p_shape_idx,p_enable);
+
+}
+
+bool Physics2DServerSW::body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const {
+
+ const Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,false);
+
+ ERR_FAIL_INDEX_V(p_shape_idx,body->get_shape_count(),false);
+
+ return body->is_shape_set_as_trigger(p_shape_idx);
+
+}
+
+
+void Physics2DServerSW::body_set_enable_continuous_collision_detection(RID p_body,bool p_enable) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_continuous_collision_detection(p_enable);
+
+}
+
+bool Physics2DServerSW::body_is_continuous_collision_detection_enabled(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,false);
+
+ return body->is_continuous_collision_detection_enabled();
+}
+
+void Physics2DServerSW::body_attach_object_instance_ID(RID p_body,uint32_t p_ID) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_instance_id(p_ID);
+
+};
+
+uint32_t Physics2DServerSW::body_get_object_instance_ID(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return body->get_instance_id();
+};
+
+
+void Physics2DServerSW::body_set_user_flags(RID p_body, uint32_t p_flags) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+};
+
+uint32_t Physics2DServerSW::body_get_user_flags(RID p_body, uint32_t p_flags) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return 0;
+};
+
+void Physics2DServerSW::body_set_param(RID p_body, BodyParameter p_param, float p_value) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_param(p_param,p_value);
+};
+
+float Physics2DServerSW::body_get_param(RID p_body, BodyParameter p_param) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return body->get_param(p_param);
+};
+
+
+void Physics2DServerSW::body_static_simulate_motion(RID p_body,const Matrix32& p_new_transform) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->simulate_motion(p_new_transform,last_step);
+
+};
+
+void Physics2DServerSW::body_set_state(RID p_body, BodyState p_state, const Variant& p_variant) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_state(p_state,p_variant);
+};
+
+Variant Physics2DServerSW::body_get_state(RID p_body, BodyState p_state) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Variant());
+
+ return body->get_state(p_state);
+};
+
+
+void Physics2DServerSW::body_set_applied_force(RID p_body, const Vector2& p_force) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_applied_force(p_force);
+};
+
+Vector2 Physics2DServerSW::body_get_applied_force(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,Vector2());
+ return body->get_applied_force();
+};
+
+void Physics2DServerSW::body_set_applied_torque(RID p_body, float p_torque) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_applied_torque(p_torque);
+};
+
+float Physics2DServerSW::body_get_applied_torque(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+
+ return body->get_applied_torque();
+};
+
+void Physics2DServerSW::body_apply_impulse(RID p_body, const Vector2& p_pos, const Vector2& p_impulse) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_impulse(p_pos,p_impulse);
+};
+
+void Physics2DServerSW::body_set_axis_velocity(RID p_body, const Vector2& p_axis_velocity) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ Vector2 v = body->get_linear_velocity();
+ Vector2 axis = p_axis_velocity.normalized();
+ v-=axis*axis.dot(v);
+ v+=p_axis_velocity;
+ body->set_linear_velocity(v);
+
+};
+
+void Physics2DServerSW::body_add_collision_exception(RID p_body, RID p_body_b) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->add_exception(p_body_b);
+
+};
+
+void Physics2DServerSW::body_remove_collision_exception(RID p_body, RID p_body_b) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->remove_exception(p_body);
+
+};
+
+void Physics2DServerSW::body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ for(int i=0;i<body->get_exceptions().size();i++) {
+ p_exceptions->push_back(body->get_exceptions()[i]);
+ }
+
+};
+
+void Physics2DServerSW::body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+};
+
+float Physics2DServerSW::body_get_contacts_reported_depth_treshold(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,0);
+ return 0;
+};
+
+void Physics2DServerSW::body_set_omit_force_integration(RID p_body,bool p_omit) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_omit_force_integration(p_omit);
+};
+
+bool Physics2DServerSW::body_is_omitting_force_integration(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,false);
+ return body->get_omit_force_integration();
+};
+
+void Physics2DServerSW::body_set_max_contacts_reported(RID p_body, int p_contacts) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_max_contacts_reported(p_contacts);
+}
+
+int Physics2DServerSW::body_get_max_contacts_reported(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body,-1);
+ return body->get_max_contacts_reported();
+}
+
+void Physics2DServerSW::body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata) {
+
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_force_integration_callback(p_receiver?p_receiver->get_instance_ID():ObjectID(0),p_method,p_udata);
+
+}
+
+
+/* JOINT API */
+
+void Physics2DServerSW::joint_set_param(RID p_joint, JointParam p_param, real_t p_value) {
+
+ Joint2DSW *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+
+ switch(p_param) {
+ case JOINT_PARAM_BIAS: joint->set_bias(p_value); break;
+ case JOINT_PARAM_MAX_BIAS: joint->set_max_bias(p_value); break;
+ case JOINT_PARAM_MAX_FORCE: joint->set_max_force(p_value); break;
+ }
+
+
+}
+
+real_t Physics2DServerSW::joint_get_param(RID p_joint,JointParam p_param) const {
+
+ const Joint2DSW *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint,-1);
+
+ switch(p_param) {
+ case JOINT_PARAM_BIAS: return joint->get_bias(); break;
+ case JOINT_PARAM_MAX_BIAS: return joint->get_max_bias(); break;
+ case JOINT_PARAM_MAX_FORCE: return joint->get_max_force(); break;
+ }
+
+ return 0;
+}
+
+
+RID Physics2DServerSW::pin_joint_create(const Vector2& p_pos,RID p_body_a,RID p_body_b) {
+
+ Body2DSW *A=body_owner.get(p_body_a);
+ ERR_FAIL_COND_V(!A,RID());
+ Body2DSW *B=NULL;
+ if (body_owner.owns(p_body_b)) {
+ B=body_owner.get(p_body_b);
+ ERR_FAIL_COND_V(!B,RID());
+ }
+
+ Joint2DSW *joint = memnew( PinJoint2DSW(p_pos,A,B) );
+ RID self = joint_owner.make_rid(joint);
+ joint->set_self(self);
+
+ return self;
+}
+
+RID Physics2DServerSW::groove_joint_create(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, RID p_body_a,RID p_body_b) {
+
+
+ Body2DSW *A=body_owner.get(p_body_a);
+ ERR_FAIL_COND_V(!A,RID());
+
+ Body2DSW *B=body_owner.get(p_body_b);
+ ERR_FAIL_COND_V(!B,RID());
+
+ Joint2DSW *joint = memnew( GrooveJoint2DSW(p_a_groove1,p_a_groove2,p_b_anchor,A,B) );
+ RID self = joint_owner.make_rid(joint);
+ joint->set_self(self);
+ return self;
+
+
+}
+
+RID Physics2DServerSW::damped_spring_joint_create(const Vector2& p_anchor_a,const Vector2& p_anchor_b,RID p_body_a,RID p_body_b) {
+
+ Body2DSW *A=body_owner.get(p_body_a);
+ ERR_FAIL_COND_V(!A,RID());
+
+ Body2DSW *B=body_owner.get(p_body_b);
+ ERR_FAIL_COND_V(!B,RID());
+
+ Joint2DSW *joint = memnew( DampedSpringJoint2DSW(p_anchor_a,p_anchor_b,A,B) );
+ RID self = joint_owner.make_rid(joint);
+ joint->set_self(self);
+ return self;
+
+}
+
+void Physics2DServerSW::damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value) {
+
+
+ Joint2DSW *j = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!j);
+ ERR_FAIL_COND(j->get_type()!=JOINT_DAMPED_SPRING);
+
+ DampedSpringJoint2DSW *dsj = static_cast<DampedSpringJoint2DSW*>(j);
+ dsj->set_param(p_param,p_value);
+}
+
+real_t Physics2DServerSW::damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const {
+
+ Joint2DSW *j = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!j,0);
+ ERR_FAIL_COND_V(j->get_type()!=JOINT_DAMPED_SPRING,0);
+
+ DampedSpringJoint2DSW *dsj = static_cast<DampedSpringJoint2DSW*>(j);
+ return dsj->get_param(p_param);
+}
+
+Physics2DServer::JointType Physics2DServerSW::joint_get_type(RID p_joint) const {
+
+
+ Joint2DSW *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint,JOINT_PIN);
+
+ return joint->get_type();
+}
+
+
+
+void Physics2DServerSW::free(RID p_rid) {
+
+ if (shape_owner.owns(p_rid)) {
+
+ Shape2DSW *shape = shape_owner.get(p_rid);
+
+ while(shape->get_owners().size()) {
+ ShapeOwner2DSW *so=shape->get_owners().front()->key();
+ so->remove_shape(shape);
+ }
+
+ shape_owner.free(p_rid);
+ memdelete(shape);
+ } else if (body_owner.owns(p_rid)) {
+
+ Body2DSW *body = body_owner.get(p_rid);
+
+// if (body->get_state_query())
+// _clear_query(body->get_state_query());
+
+// if (body->get_direct_state_query())
+// _clear_query(body->get_direct_state_query());
+
+ body->set_space(NULL);
+
+
+ while( body->get_shape_count() ) {
+
+ body->remove_shape(0);
+ }
+
+ while (body->get_constraint_map().size()) {
+ RID self = body->get_constraint_map().front()->key()->get_self();
+ ERR_FAIL_COND(!self.is_valid());
+ free(self);
+ }
+
+ body_owner.free(p_rid);
+ memdelete(body);
+
+ } else if (area_owner.owns(p_rid)) {
+
+ Area2DSW *area = area_owner.get(p_rid);
+
+// if (area->get_monitor_query())
+// _clear_query(area->get_monitor_query());
+
+ area->set_space(NULL);
+
+ while( area->get_shape_count() ) {
+
+ area->remove_shape(0);
+ }
+
+ area_owner.free(p_rid);
+ memdelete(area);
+ } else if (space_owner.owns(p_rid)) {
+
+ Space2DSW *space = space_owner.get(p_rid);
+
+ while(space->get_objects().size()) {
+ CollisionObject2DSW *co = (CollisionObject2DSW *)space->get_objects().front()->get();
+ co->set_space(NULL);
+ }
+
+ active_spaces.erase(space);
+ free(space->get_default_area()->get_self());
+ space_owner.free(p_rid);
+ memdelete(space);
+ } else if (joint_owner.owns(p_rid)) {
+
+ Joint2DSW *joint = joint_owner.get(p_rid);
+
+ joint_owner.free(p_rid);
+ memdelete(joint);
+
+ } else {
+
+ ERR_EXPLAIN("Invalid ID");
+ ERR_FAIL();
+ }
+
+
+};
+
+void Physics2DServerSW::set_active(bool p_active) {
+
+ active=p_active;
+};
+
+void Physics2DServerSW::init() {
+
+ doing_sync=true;
+ last_step=0.001;
+ iterations=8;// 8?
+ stepper = memnew( Step2DSW );
+ direct_state = memnew( Physics2DDirectBodyStateSW );
+};
+
+
+void Physics2DServerSW::step(float p_step) {
+
+
+ if (!active)
+ return;
+
+ doing_sync=false;
+
+ last_step=p_step;
+ Physics2DDirectBodyStateSW::singleton->step=p_step;
+ for( Set<const Space2DSW*>::Element *E=active_spaces.front();E;E=E->next()) {
+
+ stepper->step((Space2DSW*)E->get(),p_step,iterations);
+ }
+};
+
+void Physics2DServerSW::sync() {
+
+};
+
+void Physics2DServerSW::flush_queries() {
+
+ if (!active)
+ return;
+
+ doing_sync=true;
+ for( Set<const Space2DSW*>::Element *E=active_spaces.front();E;E=E->next()) {
+
+ Space2DSW *space=(Space2DSW *)E->get();
+ space->call_queries();
+ }
+
+};
+
+
+
+void Physics2DServerSW::finish() {
+
+ memdelete(stepper);
+ memdelete(direct_state);
+};
+
+
+Physics2DServerSW::Physics2DServerSW() {
+
+ BroadPhase2DSW::create_func=BroadPhase2DHashGrid::_create;
+// BroadPhase2DSW::create_func=BroadPhase2DBasic::_create;
+
+ active=true;
+};
+
+Physics2DServerSW::~Physics2DServerSW() {
+
+};
+
+
diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h
new file mode 100644
index 0000000000..f6665da73f
--- /dev/null
+++ b/servers/physics_2d/physics_2d_server_sw.h
@@ -0,0 +1,215 @@
+/*************************************************************************/
+/* physics_2d_server_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef PHYSICS_2D_SERVER_SW
+#define PHYSICS_2D_SERVER_SW
+
+
+#include "servers/physics_2d_server.h"
+#include "shape_2d_sw.h"
+#include "space_2d_sw.h"
+#include "step_2d_sw.h"
+#include "joints_2d_sw.h"
+
+
+class Physics2DServerSW : public Physics2DServer {
+
+ OBJ_TYPE( Physics2DServerSW, Physics2DServer );
+
+friend class Physics2DDirectSpaceStateSW;
+ bool active;
+ int iterations;
+ bool doing_sync;
+ real_t last_step;
+
+ Step2DSW *stepper;
+ Set<const Space2DSW*> active_spaces;
+
+ Physics2DDirectBodyStateSW *direct_state;
+
+ mutable RID_Owner<Shape2DSW> shape_owner;
+ mutable RID_Owner<Space2DSW> space_owner;
+ mutable RID_Owner<Area2DSW> area_owner;
+ mutable RID_Owner<Body2DSW> body_owner;
+ mutable RID_Owner<Joint2DSW> joint_owner;
+
+// void _clear_query(Query2DSW *p_query);
+public:
+
+ virtual RID shape_create(ShapeType p_shape);
+ virtual void shape_set_data(RID p_shape, const Variant& p_data);
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias);
+
+ virtual ShapeType shape_get_type(RID p_shape) const;
+ virtual Variant shape_get_data(RID p_shape) const;
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const;
+
+ /* SPACE API */
+
+ virtual RID space_create();
+ virtual void space_set_active(RID p_space,bool p_active);
+ virtual bool space_is_active(RID p_space) const;
+
+ virtual void space_set_param(RID p_space,SpaceParameter p_param, real_t p_value);
+ virtual real_t space_get_param(RID p_space,SpaceParameter p_param) const;
+
+ // this function only works on fixed process, errors and returns null otherwise
+ virtual Physics2DDirectSpaceState* space_get_direct_state(RID p_space);
+
+
+ /* AREA API */
+
+ virtual RID area_create();
+
+ virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode);
+ virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const;
+
+ virtual void area_set_space(RID p_area, RID p_space);
+ virtual RID area_get_space(RID p_area) const;
+
+ virtual void area_add_shape(RID p_area, RID p_shape, const Matrix32& p_transform=Matrix32());
+ virtual void area_set_shape(RID p_area, int p_shape_idx,RID p_shape);
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Matrix32& p_transform);
+
+ virtual int area_get_shape_count(RID p_area) const;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const;
+ virtual Matrix32 area_get_shape_transform(RID p_area, int p_shape_idx) const;
+
+ virtual void area_remove_shape(RID p_area, int p_shape_idx);
+ virtual void area_clear_shapes(RID p_area);
+
+ virtual void area_attach_object_instance_ID(RID p_area,ObjectID p_ID);
+ virtual ObjectID area_get_object_instance_ID(RID p_area) const;
+
+ virtual void area_set_param(RID p_area,AreaParameter p_param,const Variant& p_value);
+ virtual void area_set_transform(RID p_area, const Matrix32& p_transform);
+
+ virtual Variant area_get_param(RID p_parea,AreaParameter p_param) const;
+ virtual Matrix32 area_get_transform(RID p_area) const;
+
+ virtual void area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method);
+
+
+ /* BODY API */
+
+ // create a body of a given type
+ virtual RID body_create(BodyMode p_mode=BODY_MODE_RIGID,bool p_init_sleeping=false);
+
+ virtual void body_set_space(RID p_body, RID p_space);
+ virtual RID body_get_space(RID p_body) const;
+
+ virtual void body_set_mode(RID p_body, BodyMode p_mode);
+ virtual BodyMode body_get_mode(RID p_body, BodyMode p_mode) const;
+
+ virtual void body_add_shape(RID p_body, RID p_shape, const Matrix32& p_transform=Matrix32());
+ virtual void body_set_shape(RID p_body, int p_shape_idx,RID p_shape);
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Matrix32& p_transform);
+
+ virtual int body_get_shape_count(RID p_body) const;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const;
+ virtual Matrix32 body_get_shape_transform(RID p_body, int p_shape_idx) const;
+
+ virtual void body_remove_shape(RID p_body, int p_shape_idx);
+ virtual void body_clear_shapes(RID p_body);
+
+ virtual void body_set_shape_as_trigger(RID p_body, int p_shape_idx,bool p_enable);
+ virtual bool body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const;
+
+ virtual void body_attach_object_instance_ID(RID p_body,uint32_t p_ID);
+ virtual uint32_t body_get_object_instance_ID(RID p_body) const;
+
+ virtual void body_set_enable_continuous_collision_detection(RID p_body,bool p_enable);
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const;
+
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags);
+ virtual uint32_t body_get_user_flags(RID p_body, uint32_t p_flags) const;
+
+ virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value);
+ virtual float body_get_param(RID p_body, BodyParameter p_param) const;
+
+ //advanced simulation
+ virtual void body_static_simulate_motion(RID p_body,const Matrix32& p_new_transform);
+
+ virtual void body_set_state(RID p_body, BodyState p_state, const Variant& p_variant);
+ virtual Variant body_get_state(RID p_body, BodyState p_state) const;
+
+ virtual void body_set_applied_force(RID p_body, const Vector2& p_force);
+ virtual Vector2 body_get_applied_force(RID p_body) const;
+
+ virtual void body_set_applied_torque(RID p_body, float p_torque);
+ virtual float body_get_applied_torque(RID p_body) const;
+
+ virtual void body_apply_impulse(RID p_body, const Vector2& p_pos, const Vector2& p_impulse);
+ virtual void body_set_axis_velocity(RID p_body, const Vector2& p_axis_velocity);
+
+ virtual void body_add_collision_exception(RID p_body, RID p_body_b);
+ virtual void body_remove_collision_exception(RID p_body, RID p_body_b);
+ virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions);
+
+ virtual void body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold);
+ virtual float body_get_contacts_reported_depth_treshold(RID p_body) const;
+
+ virtual void body_set_omit_force_integration(RID p_body,bool p_omit);
+ virtual bool body_is_omitting_force_integration(RID p_body) const;
+
+ virtual void body_set_max_contacts_reported(RID p_body, int p_contacts);
+ virtual int body_get_max_contacts_reported(RID p_body) const;
+
+ virtual void body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata=Variant());
+
+ /* JOINT API */
+
+ virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value);
+ virtual real_t joint_get_param(RID p_joint,JointParam p_param) const;
+
+ virtual RID pin_joint_create(const Vector2& p_pos,RID p_body_a,RID p_body_b=RID());
+ virtual RID groove_joint_create(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, RID p_body_a,RID p_body_b);
+ virtual RID damped_spring_joint_create(const Vector2& p_anchor_a,const Vector2& p_anchor_b,RID p_body_a,RID p_body_b=RID());
+ virtual void damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value);
+ virtual real_t damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const;
+
+ virtual JointType joint_get_type(RID p_joint) const;
+
+ /* MISC */
+
+ virtual void free(RID p_rid);
+
+ virtual void set_active(bool p_active);
+ virtual void init();
+ virtual void step(float p_step);
+ virtual void sync();
+ virtual void flush_queries();
+ virtual void finish();
+
+ Physics2DServerSW();
+ ~Physics2DServerSW();
+
+};
+
+#endif
+
diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp
new file mode 100644
index 0000000000..5ad05a18ac
--- /dev/null
+++ b/servers/physics_2d/shape_2d_sw.cpp
@@ -0,0 +1,1085 @@
+/*************************************************************************/
+/* shape_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "shape_2d_sw.h"
+#include "geometry.h"
+#include "sort.h"
+
+#define _SEGMENT_IS_VALID_SUPPORT_TRESHOLD 0.99998
+
+
+void Shape2DSW::configure(const Rect2& p_aabb) {
+ aabb=p_aabb;
+ configured=true;
+ for (Map<ShapeOwner2DSW*,int>::Element *E=owners.front();E;E=E->next()) {
+ ShapeOwner2DSW* co=(ShapeOwner2DSW*)E->key();
+ co->_shape_changed();
+ }
+}
+
+
+Vector2 Shape2DSW::get_support(const Vector2& p_normal) const {
+
+ Vector2 res[2];
+ int amnt;
+ get_supports(p_normal,res,amnt);
+ return res[0];
+}
+
+void Shape2DSW::add_owner(ShapeOwner2DSW *p_owner) {
+
+ Map<ShapeOwner2DSW*,int>::Element *E=owners.find(p_owner);
+ if (E) {
+ E->get()++;
+ } else {
+ owners[p_owner]=1;
+ }
+}
+
+void Shape2DSW::remove_owner(ShapeOwner2DSW *p_owner){
+
+ Map<ShapeOwner2DSW*,int>::Element *E=owners.find(p_owner);
+ ERR_FAIL_COND(!E);
+ E->get()--;
+ if (E->get()==0) {
+ owners.erase(E);
+ }
+
+}
+
+bool Shape2DSW::is_owner(ShapeOwner2DSW *p_owner) const{
+
+ return owners.has(p_owner);
+
+}
+
+const Map<ShapeOwner2DSW*,int>& Shape2DSW::get_owners() const{
+ return owners;
+}
+
+
+Shape2DSW::Shape2DSW() {
+
+ custom_bias=0;
+ configured=false;
+}
+
+
+Shape2DSW::~Shape2DSW() {
+
+ ERR_FAIL_COND(owners.size());
+}
+
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void LineShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ r_amount=0;
+}
+
+bool LineShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+ Vector2 segment= p_begin - p_end;
+ real_t den=normal.dot( segment );
+
+ //printf("den is %i\n",den);
+ if (Math::abs(den)<=CMP_EPSILON) {
+
+ return false;
+ }
+
+ real_t dist=(normal.dot( p_begin ) - d) / den;
+ //printf("dist is %i\n",dist);
+
+ if (dist<-CMP_EPSILON || dist > (1.0 +CMP_EPSILON)) {
+
+ return false;
+ }
+
+ r_point = p_begin + segment * -dist;
+ r_normal=normal;
+
+ return true;
+}
+
+real_t LineShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ return 0;
+}
+
+void LineShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(p_data.get_type()!=Variant::ARRAY);
+ Array arr = p_data;
+ ERR_FAIL_COND(arr.size()!=2);
+ normal=arr[0];
+ d=arr[1];
+ configure(Rect2(Vector2(-1e4,-1e4),Vector2(1e4*2,1e4*2)));
+
+}
+
+Variant LineShape2DSW::get_data() const {
+
+ Array arr;
+ arr.resize(2);
+ arr[0]=normal;
+ arr[1]=d;
+ return arr;
+}
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void RayShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+
+ r_amount=1;
+
+ if (p_normal.y>0)
+ *r_supports=Vector2(0,length);
+ else
+ *r_supports=Vector2();
+
+}
+
+bool RayShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+ return false; //rays can't be intersected
+
+}
+
+real_t RayShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ return 0; //rays are mass-less
+}
+
+void RayShape2DSW::set_data(const Variant& p_data) {
+
+ length=p_data;
+ configure(Rect2(0,0,0.001,length));
+}
+
+Variant RayShape2DSW::get_data() const {
+
+ return length;
+}
+
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void SegmentShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ if (Math::abs(p_normal.dot(n))>_SEGMENT_IS_VALID_SUPPORT_TRESHOLD) {
+ r_supports[0]=a;
+ r_supports[1]=b;
+ r_amount=2;
+ return;
+
+ }
+
+ float dp=p_normal.dot(b-a);
+ if (dp>0)
+ *r_supports=b;
+ else
+ *r_supports=a;
+ r_amount=1;
+
+}
+
+bool SegmentShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+ if (!Geometry::segment_intersects_segment_2d(p_begin,p_end,a,b,&r_point))
+ return false;
+
+ Vector2 d = p_end-p_begin;
+ if (n.dot(p_begin) > n.dot(a)) {
+ r_normal=n;
+ } else {
+ r_normal=-n;
+ }
+
+ return true;
+}
+
+real_t SegmentShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ real_t l = b.distance_to(a);
+ Vector2 ofs = (a+b)*0.5;
+
+ return p_mass*(l*l/12.0f + ofs.length_squared());
+}
+
+void SegmentShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(p_data.get_type()!=Variant::RECT2);
+
+ Rect2 r = p_data;
+ a=r.pos;
+ b=r.size;
+ n=(b-a).tangent();
+
+ Rect2 aabb;
+ aabb.pos=a;
+ aabb.expand_to(b);
+ if (aabb.size.x==0)
+ aabb.size.x=0.001;
+ if (aabb.size.y==0)
+ aabb.size.y=0.001;
+ configure(aabb);
+}
+
+Variant SegmentShape2DSW::get_data() const {
+
+ Rect2 r;
+ r.pos=a;
+ r.size=b;
+ return r;
+}
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void CircleShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ r_amount=1;
+ *r_supports=p_normal*radius;
+
+}
+
+bool CircleShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+
+ Vector2 line_vec = p_end - p_begin;
+
+ real_t a, b, c;
+
+ a = line_vec.dot(line_vec);
+ b = 2 * p_begin.dot(line_vec);
+ c = p_begin.dot(p_begin) - radius * radius;
+
+ real_t sqrtterm = b*b - 4*a*c;
+
+ if(sqrtterm < 0)
+ return false;
+ sqrtterm = Math::sqrt(sqrtterm);
+ real_t res = ( -b - sqrtterm ) / (2 * a);
+
+ if (res <0 || res >1+CMP_EPSILON) {
+ return false;
+ }
+
+ r_point=p_begin+line_vec*res;
+ r_normal=r_point.normalized();
+ return true;
+}
+
+real_t CircleShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ return radius*radius;
+}
+
+void CircleShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(!p_data.is_num());
+ radius=p_data;
+ configure(Rect2(-radius,-radius,radius*2,radius*2));
+}
+
+Variant CircleShape2DSW::get_data() const {
+
+ return radius;
+}
+
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void RectangleShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ for(int i=0;i<2;i++) {
+
+ Vector2 ag;
+ ag[i]=1.0;
+ float dp = ag.dot(p_normal);
+ if (Math::abs(dp)<_SEGMENT_IS_VALID_SUPPORT_TRESHOLD)
+ continue;
+
+ float sgn = dp>0 ? 1.0 : -1.0;
+
+ r_amount=2;
+
+ r_supports[0][i]=half_extents[i]*sgn;
+ r_supports[0][i^1]=half_extents[i^1];
+
+ r_supports[1][i]=half_extents[i]*sgn;
+ r_supports[1][i^1]=-half_extents[i^1];
+
+ return;
+
+
+ }
+
+ /* USE POINT */
+
+ r_amount=1;
+ r_supports[0]=Vector2(
+ (p_normal.x<0) ? -half_extents.x : half_extents.x,
+ (p_normal.y<0) ? -half_extents.y : half_extents.y
+ );
+
+}
+
+bool RectangleShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+
+ return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal);
+}
+
+real_t RectangleShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ Vector2 he2=half_extents*2;
+ return p_mass*he2.dot(he2)/12.0f;
+}
+
+void RectangleShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(p_data.get_type()!=Variant::VECTOR2);
+
+ half_extents=p_data;
+ configure(Rect2(-half_extents,half_extents*2.0));
+}
+
+Variant RectangleShape2DSW::get_data() const {
+
+ return half_extents;
+}
+
+
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void CapsuleShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ Vector2 n=p_normal;
+
+ float d = n.y;
+
+ if (Math::abs( d )<(1.0-_SEGMENT_IS_VALID_SUPPORT_TRESHOLD) ) {
+
+ // make it flat
+ n.y=0.0;
+ n.normalize();
+ n*=radius;
+
+ r_amount=2;
+ r_supports[0]=n;
+ r_supports[0].y+=height*0.5;
+ r_supports[1]=n;
+ r_supports[1].y-=height*0.5;
+
+ } else {
+
+ float h = (d > 0) ? height : -height;
+
+ n*=radius;
+ n.y += h*0.5;
+ r_amount=1;
+ *r_supports=n;
+
+ }
+}
+
+bool CapsuleShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+
+ float d = 1e10;
+ Vector2 n = (p_end-p_begin).normalized();
+ bool collided=false;
+
+ //try spheres
+ for(int i=0;i<2;i++) {
+
+ Vector2 begin = p_begin;
+ Vector2 end = p_end;
+ float ofs = (i==0)?-height*0.5:height*0.5;
+ begin.y+=ofs;
+ end.y+=ofs;
+
+ Vector2 line_vec = end - begin;
+
+ real_t a, b, c;
+
+ a = line_vec.dot(line_vec);
+ b = 2 * begin.dot(line_vec);
+ c = begin.dot(begin) - radius * radius;
+
+ real_t sqrtterm = b*b - 4*a*c;
+
+ if(sqrtterm < 0)
+ continue;
+
+ sqrtterm = Math::sqrt(sqrtterm);
+ real_t res = ( -b - sqrtterm ) / (2 * a);
+
+ if (res <0 || res >1+CMP_EPSILON) {
+ continue;
+ }
+
+ Vector2 point = begin+line_vec*res;
+ Vector2 pointf(point.x,point.y-ofs);
+ real_t pd = n.dot(pointf);
+ if (pd<d) {
+ r_point=pointf;
+ r_normal=point.normalized();
+ d=pd;
+ collided=true;
+ }
+ }
+
+
+ Vector2 rpos,rnorm;
+ if (Rect2( Point2(-radius,-height*0.5), Size2(radius*2.0,height) ).intersects_segment(p_begin,p_end,&rpos,&rnorm)) {
+
+ real_t pd = n.dot(rpos);
+ if (pd<d) {
+ r_point=rpos;
+ r_normal=rnorm;
+ d=pd;
+ collided=true;
+ }
+ }
+
+ //return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal);
+ return collided; //todo
+}
+
+real_t CapsuleShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ Vector2 he2(radius*2,height+radius*2);
+ return p_mass*he2.dot(he2)/12.0f;
+}
+
+void CapsuleShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(p_data.get_type()!=Variant::ARRAY && p_data.get_type()!=Variant::VECTOR2);
+
+ if (p_data.get_type()==Variant::ARRAY) {
+ Array arr=p_data;
+ ERR_FAIL_COND(arr.size()!=2);
+ height=arr[0];
+ radius=arr[1];
+ } else {
+
+ Point2 p = p_data;
+ radius=p.x;
+ height=p.y;
+ }
+
+ Point2 he(radius,height*0.5+radius);
+ configure(Rect2(-he,he*2));
+
+}
+
+Variant CapsuleShape2DSW::get_data() const {
+
+ return Point2(height,radius);
+}
+
+
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
+
+
+void ConvexPolygonShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ int support_idx=-1;
+ real_t d=-1e10;
+
+ for(int i=0;i<point_count;i++) {
+
+ //test point
+ real_t ld = p_normal.dot(points[i].pos);
+ if (ld>d) {
+ support_idx=i;
+ d=ld;
+ }
+
+ //test segment
+ if (points[i].normal.dot(p_normal)>_SEGMENT_IS_VALID_SUPPORT_TRESHOLD) {
+
+ r_amount=2;
+ r_supports[0]=points[i].pos;
+ r_supports[1]=points[(i+1)%point_count].pos;
+ return;
+ }
+ }
+
+ ERR_FAIL_COND(support_idx==-1);
+
+ r_amount=1;
+ r_supports[0]=points[support_idx].pos;
+
+}
+
+bool ConvexPolygonShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const {
+
+ Vector2 n = (p_end-p_begin).normalized();
+ real_t d=1e10;
+ bool inters=false;
+
+ for(int i=0;i<point_count;i++) {
+
+ //hmm crap.. no can do..
+ //if (d.dot(points[i].normal)>=0)
+ // continue;
+
+
+ Vector2 res;
+
+ if (!Geometry::segment_intersects_segment_2d(p_begin,p_end,points[i].pos,points[(i+1)%point_count].pos,&res))
+ continue;
+
+ float nd = n.dot(res);
+ if (nd<d) {
+
+ d=nd;
+ r_point=res;
+ r_normal=points[i].normal;
+ inters=true;
+ }
+
+ }
+
+
+ if (inters) {
+
+ if (n.dot(r_normal)>0)
+ r_normal=-r_normal;
+ }
+
+ //return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal);
+ return inters; //todo
+}
+
+real_t ConvexPolygonShape2DSW::get_moment_of_inertia(float p_mass) const {
+
+ Rect2 aabb;
+ aabb.pos=points[0].pos;
+ for(int i=0;i<point_count;i++) {
+
+ aabb.expand_to(points[i].pos);
+ }
+
+ return p_mass*aabb.size.dot(aabb.size)/12.0f;
+}
+
+void ConvexPolygonShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(p_data.get_type()!=Variant::VECTOR2_ARRAY && p_data.get_type()!=Variant::REAL_ARRAY);
+
+
+ if (points)
+ memdelete_arr(points);
+ points=NULL;
+ point_count=0;
+
+ if (p_data.get_type()==Variant::VECTOR2_ARRAY) {
+ DVector<Vector2> arr=p_data;
+ ERR_FAIL_COND(arr.size()==0);
+ point_count=arr.size();
+ points = memnew_arr(Point,point_count);
+ DVector<Vector2>::Read r = arr.read();
+
+ for(int i=0;i<point_count;i++) {
+ points[i].pos=r[i];
+ }
+
+ for(int i=0;i<point_count;i++) {
+
+ Vector2 p = points[i].pos;
+ Vector2 pn = points[(i+1)%point_count].pos;
+ points[i].normal=(pn-p).tangent().normalized();
+ }
+ } else {
+
+ DVector<real_t> dvr = p_data;
+ point_count=dvr.size()/4;
+ ERR_FAIL_COND(point_count==0);
+
+ points = memnew_arr(Point,point_count);
+ DVector<real_t>::Read r = dvr.read();
+
+ for(int i=0;i<point_count;i++) {
+
+ int idx=i<<2;
+ points[i].pos.x=r[idx+0];
+ points[i].pos.y=r[idx+1];
+ points[i].normal.x=r[idx+2];
+ points[i].normal.y=r[idx+3];
+
+ }
+ }
+
+
+ ERR_FAIL_COND(point_count==0);
+ Rect2 aabb;
+ aabb.pos=points[0].pos;
+ for(int i=1;i<point_count;i++)
+ aabb.expand_to(points[i].pos);
+
+ configure(aabb);
+}
+
+Variant ConvexPolygonShape2DSW::get_data() const {
+
+ DVector<Vector2> dvr;
+
+ dvr.resize(point_count);
+
+ for(int i=0;i<point_count;i++) {
+ dvr.set(i,points[i].pos);
+ }
+
+ return dvr;
+}
+
+
+ConvexPolygonShape2DSW::ConvexPolygonShape2DSW() {
+
+ points=NULL;
+ point_count=0;
+
+}
+
+ConvexPolygonShape2DSW::~ConvexPolygonShape2DSW(){
+
+ if (points)
+ memdelete_arr(points);
+
+}
+
+//////////////////////////////////////////////////
+
+
+void ConcavePolygonShape2DSW::get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const {
+
+ real_t d=-1e10;
+ int idx=-1;
+ for(int i=0;i<points.size();i++) {
+
+ real_t ld = p_normal.dot(points[i]);
+ if (ld>d) {
+ d=ld;
+ idx=i;
+ }
+ }
+
+
+ r_amount=1;
+ ERR_FAIL_COND(idx==-1);
+ *r_supports=points[idx];
+
+}
+
+bool ConcavePolygonShape2DSW::intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const{
+
+ uint32_t* stack = (uint32_t*)alloca(sizeof(int)*bvh_depth);
+
+ enum {
+ TEST_AABB_BIT=0,
+ VISIT_LEFT_BIT=1,
+ VISIT_RIGHT_BIT=2,
+ VISIT_DONE_BIT=3,
+ VISITED_BIT_SHIFT=29,
+ NODE_IDX_MASK=(1<<VISITED_BIT_SHIFT)-1,
+ VISITED_BIT_MASK=~NODE_IDX_MASK,
+
+
+ };
+
+ Vector2 n = (p_end-p_begin).normalized();
+ real_t d=1e10;
+ bool inters=false;
+
+ //for(int i=0;i<bvh_depth;i++)
+ // stack[i]=0;
+
+ int level=0;
+
+ const Segment *segmentptr=&segments[0];
+ const Vector2 *pointptr=&points[0];
+ const BVH *bvhptr = &bvh[0];
+ int pos=bvh.size()-1;
+
+
+ stack[0]=0;
+ while(true) {
+
+ uint32_t node = stack[level]&NODE_IDX_MASK;
+ const BVH &b = bvhptr[ node ];
+ bool done=false;
+
+ switch(stack[level]>>VISITED_BIT_SHIFT) {
+ case TEST_AABB_BIT: {
+
+
+ bool valid = b.aabb.intersects_segment(p_begin,p_end);
+ if (!valid) {
+
+ stack[level]=(VISIT_DONE_BIT<<VISITED_BIT_SHIFT)|node;
+
+ } else {
+
+ if (b.left<0) {
+
+ const Segment &s=segmentptr[ b.right ];
+ Vector2 a = pointptr[ s.points[0] ];
+ Vector2 b = pointptr[ s.points[1] ];
+
+
+ Vector2 res;
+
+ if (Geometry::segment_intersects_segment_2d(p_begin,p_end,a,b,&res)) {
+
+ float nd = n.dot(res);
+ if (nd<d) {
+
+ d=nd;
+ r_point=res;
+ r_normal=(b-a).tangent().normalized();
+ inters=true;
+ }
+
+ }
+
+ stack[level]=(VISIT_DONE_BIT<<VISITED_BIT_SHIFT)|node;
+
+ } else {
+
+ stack[level]=(VISIT_LEFT_BIT<<VISITED_BIT_SHIFT)|node;
+ }
+ }
+
+ } continue;
+ case VISIT_LEFT_BIT: {
+
+ stack[level]=(VISIT_RIGHT_BIT<<VISITED_BIT_SHIFT)|node;
+ stack[level+1]=b.left|TEST_AABB_BIT;
+ level++;
+
+ } continue;
+ case VISIT_RIGHT_BIT: {
+
+ stack[level]=(VISIT_DONE_BIT<<VISITED_BIT_SHIFT)|node;
+ stack[level+1]=b.right|TEST_AABB_BIT;
+ level++;
+ } continue;
+ case VISIT_DONE_BIT: {
+
+ if (level==0) {
+ done=true;
+ break;
+ } else
+ level--;
+
+ } continue;
+ }
+
+
+ if (done)
+ break;
+ }
+
+
+ if (inters) {
+
+ if (n.dot(r_normal)>0)
+ r_normal=-r_normal;
+ }
+
+ return inters;
+
+}
+
+
+
+int ConcavePolygonShape2DSW::_generate_bvh(BVH *p_bvh,int p_len,int p_depth) {
+
+ if (p_len==1) {
+
+ bvh_depth=MAX(p_depth,bvh_depth);
+ bvh.push_back(*p_bvh);
+ return bvh.size()-1;
+ }
+
+ //else sort best
+
+ Rect2 global_aabb=p_bvh[0].aabb;
+ for(int i=1;i<p_len;i++) {
+ global_aabb=global_aabb.merge(p_bvh[i].aabb);
+ }
+
+ if (global_aabb.size.x > global_aabb.size.y) {
+
+ SortArray<BVH,BVH_CompareX> sort;
+ sort.sort(p_bvh,p_len);
+
+ } else {
+
+ SortArray<BVH,BVH_CompareY> sort;
+ sort.sort(p_bvh,p_len);
+
+ }
+
+ int median = p_len/2;
+
+
+ BVH node;
+ node.aabb=global_aabb;
+ int node_idx = bvh.size();
+ bvh.push_back(node);
+
+ int l = _generate_bvh(p_bvh,median,p_depth+1);
+ int r = _generate_bvh(&p_bvh[median],p_len-median,p_depth+1);
+ bvh[node_idx].left=l;
+ bvh[node_idx].right=r;
+
+ return node_idx;
+
+}
+
+void ConcavePolygonShape2DSW::set_data(const Variant& p_data) {
+
+ ERR_FAIL_COND(p_data.get_type()!=Variant::VECTOR2_ARRAY && p_data.get_type()!=Variant::REAL_ARRAY);
+
+ segments.clear();;
+ points.clear();;
+ bvh.clear();;
+ bvh_depth=1;
+
+ Rect2 aabb;
+
+ if (p_data.get_type()==Variant::VECTOR2_ARRAY) {
+
+ DVector<Vector2> p2arr = p_data;
+ int len = p2arr.size();
+ DVector<Vector2>::Read arr = p2arr.read();
+
+
+ Map<Point2,int> pointmap;
+ for(int i=0;i<len;i+=2) {
+
+ Point2 p1 =arr[i];
+ Point2 p2 =arr[i+1];
+ int idx_p1,idx_p2;
+ if (p1==p2)
+ continue; //don't want it
+
+ if (pointmap.has(p1)) {
+ idx_p1=pointmap[p1];
+ } else {
+ idx_p1=pointmap.size();
+ pointmap[p1]=idx_p1;
+ }
+
+ if (pointmap.has(p2)) {
+ idx_p2=pointmap[p2];
+ } else {
+ idx_p2=pointmap.size();
+ pointmap[p2]=idx_p2;
+ }
+
+ Segment s;
+ s.points[0]=idx_p1;
+ s.points[1]=idx_p2;
+ segments.push_back(s);
+ }
+
+ points.resize(pointmap.size());
+ aabb.pos=pointmap.front()->key();
+ for(Map<Point2,int>::Element *E=pointmap.front();E;E=E->next()) {
+
+ aabb.expand_to(E->key());
+ points[E->get()]=E->key();
+ }
+
+ Vector<BVH> main_vbh;
+ main_vbh.resize(segments.size());
+ for(int i=0;i<main_vbh.size();i++) {
+
+
+ main_vbh[i].aabb.pos=points[segments[i].points[0]];
+ main_vbh[i].aabb.expand_to(points[segments[i].points[1]]);
+ main_vbh[i].left=-1;
+ main_vbh[i].right=i;
+ }
+
+ _generate_bvh(&main_vbh[0],main_vbh.size(),1);
+
+
+ } else {
+ //dictionary with arrays
+
+ }
+
+
+ configure(aabb);
+}
+Variant ConcavePolygonShape2DSW::get_data() const {
+
+
+ DVector<Vector2> rsegments;
+ int len = segments.size();
+ rsegments.resize(len*2);
+ DVector<Vector2>::Write w = rsegments.write();
+ for(int i=0;i<len;i++) {
+
+ w[(i<<1)+0]=points[segments[i].points[0]];
+ w[(i<<1)+1]=points[segments[i].points[1]];
+ }
+
+ w=DVector<Vector2>::Write();
+
+ return rsegments;
+}
+
+void ConcavePolygonShape2DSW::cull(const Rect2& p_local_aabb,Callback p_callback,void* p_userdata) const {
+
+ uint32_t* stack = (uint32_t*)alloca(sizeof(int)*bvh_depth);
+
+ enum {
+ TEST_AABB_BIT=0,
+ VISIT_LEFT_BIT=1,
+ VISIT_RIGHT_BIT=2,
+ VISIT_DONE_BIT=3,
+ VISITED_BIT_SHIFT=29,
+ NODE_IDX_MASK=(1<<VISITED_BIT_SHIFT)-1,
+ VISITED_BIT_MASK=~NODE_IDX_MASK,
+
+
+ };
+
+ //for(int i=0;i<bvh_depth;i++)
+ // stack[i]=0;
+
+
+ int level=0;
+
+ const Segment *segmentptr=&segments[0];
+ const Vector2 *pointptr=&points[0];
+ const BVH *bvhptr = &bvh[0];
+ int pos=bvh.size()-1;
+
+
+ stack[0]=0;
+ while(true) {
+
+ uint32_t node = stack[level]&NODE_IDX_MASK;
+ const BVH &b = bvhptr[ node ];
+
+ switch(stack[level]>>VISITED_BIT_SHIFT) {
+ case TEST_AABB_BIT: {
+
+
+ bool valid = p_local_aabb.intersects(b.aabb);
+ if (!valid) {
+
+ stack[level]=(VISIT_DONE_BIT<<VISITED_BIT_SHIFT)|node;
+
+ } else {
+
+ if (b.left<0) {
+
+ const Segment &s=segmentptr[ b.right ];
+ Vector2 a = pointptr[ s.points[0] ];
+ Vector2 b = pointptr[ s.points[1] ];
+
+ SegmentShape2DSW ss(a,b,(b-a).tangent().normalized());
+
+ p_callback(p_userdata,&ss);
+ stack[level]=(VISIT_DONE_BIT<<VISITED_BIT_SHIFT)|node;
+
+ } else {
+
+ stack[level]=(VISIT_LEFT_BIT<<VISITED_BIT_SHIFT)|node;
+ }
+ }
+
+ } continue;
+ case VISIT_LEFT_BIT: {
+
+ stack[level]=(VISIT_RIGHT_BIT<<VISITED_BIT_SHIFT)|node;
+ stack[level+1]=b.left|TEST_AABB_BIT;
+ level++;
+
+ } continue;
+ case VISIT_RIGHT_BIT: {
+
+ stack[level]=(VISIT_DONE_BIT<<VISITED_BIT_SHIFT)|node;
+ stack[level+1]=b.right|TEST_AABB_BIT;
+ level++;
+ } continue;
+ case VISIT_DONE_BIT: {
+
+ if (level==0)
+ return;
+ else
+ level--;
+
+ } continue;
+ }
+ }
+
+}
+
+
diff --git a/servers/physics_2d/shape_2d_sw.h b/servers/physics_2d/shape_2d_sw.h
new file mode 100644
index 0000000000..20e76fad5a
--- /dev/null
+++ b/servers/physics_2d/shape_2d_sw.h
@@ -0,0 +1,442 @@
+/*************************************************************************/
+/* shape_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SHAPE_2D_2DSW_H
+#define SHAPE_2D_2DSW_H
+
+#include "servers/physics_2d_server.h"
+/*
+
+SHAPE_LINE, ///< plane:"plane"
+SHAPE_SEGMENT, ///< float:"length"
+SHAPE_CIRCLE, ///< float:"radius"
+SHAPE_RECTANGLE, ///< vec3:"extents"
+SHAPE_CONVEX_POLYGON, ///< array of planes:"planes"
+SHAPE_CONCAVE_POLYGON, ///< Vector2 array:"triangles" , or Dictionary with "indices" (int array) and "triangles" (Vector2 array)
+SHAPE_CUSTOM, ///< Server-Implementation based custom shape, calling shape_create() with this value will result in an error
+
+*/
+
+class Shape2DSW;
+
+class ShapeOwner2DSW {
+public:
+
+ virtual void _shape_changed()=0;
+ virtual void remove_shape(Shape2DSW *p_shape)=0;
+
+ virtual ~ShapeOwner2DSW() {}
+};
+
+class Shape2DSW {
+
+ RID self;
+ Rect2 aabb;
+ bool configured;
+ real_t custom_bias;
+
+ Map<ShapeOwner2DSW*,int> owners;
+protected:
+
+ void configure(const Rect2& p_aabb);
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const {return self; }
+
+ virtual Physics2DServer::ShapeType get_type() const=0;
+
+ _FORCE_INLINE_ Rect2 get_aabb() const { return aabb; }
+ _FORCE_INLINE_ bool is_configured() const { return configured; }
+
+ virtual bool is_concave() const { return false; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const=0;
+ virtual Vector2 get_support(const Vector2& p_normal) const;
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const=0;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const=0;
+ virtual real_t get_moment_of_inertia(float p_mass) const=0;
+
+ virtual void set_data(const Variant& p_data)=0;
+ virtual Variant get_data() const=0;
+
+ _FORCE_INLINE_ void set_custom_bias(real_t p_bias) { custom_bias=p_bias; }
+ _FORCE_INLINE_ real_t get_custom_bias() const { return custom_bias; }
+
+ void add_owner(ShapeOwner2DSW *p_owner);
+ void remove_owner(ShapeOwner2DSW *p_owner);
+ bool is_owner(ShapeOwner2DSW *p_owner) const;
+ const Map<ShapeOwner2DSW*,int>& get_owners() const;
+
+ Shape2DSW();
+ virtual ~Shape2DSW();
+};
+
+
+class LineShape2DSW : public Shape2DSW {
+
+
+ Vector2 normal;
+ real_t d;
+
+public:
+
+ _FORCE_INLINE_ Vector2 get_normal() const { return normal; }
+ _FORCE_INLINE_ real_t get_d() const { return d; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_LINE; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ //real large
+ r_min=-1e10;
+ r_max=1e10;
+ }
+
+};
+
+
+class RayShape2DSW : public Shape2DSW {
+
+
+ real_t length;
+
+public:
+
+
+ _FORCE_INLINE_ real_t get_length() const { return length; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_RAY; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ //real large
+ r_max = p_normal.dot(p_transform.get_origin());
+ r_min = p_normal.dot(p_transform.xform(Vector2(0,length)));
+ if (r_max<r_min) {
+
+ SWAP(r_max,r_min);
+ }
+ }
+
+ _FORCE_INLINE_ RayShape2DSW() {}
+ _FORCE_INLINE_ RayShape2DSW(real_t p_length) { length=p_length; }
+};
+
+
+class SegmentShape2DSW : public Shape2DSW {
+
+
+ Vector2 a;
+ Vector2 b;
+ Vector2 n;
+
+public:
+
+
+ _FORCE_INLINE_ const Vector2& get_a() const { return a; }
+ _FORCE_INLINE_ const Vector2& get_b() const { return b; }
+ _FORCE_INLINE_ const Vector2& get_normal() const { return n; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_SEGMENT; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ //real large
+ r_max = p_normal.dot(p_transform.xform(a));
+ r_min = p_normal.dot(p_transform.xform(b));
+ if (r_max<r_min) {
+
+ SWAP(r_max,r_min);
+ }
+ }
+
+ _FORCE_INLINE_ SegmentShape2DSW() {}
+ _FORCE_INLINE_ SegmentShape2DSW(const Vector2& p_a,const Vector2& p_b,const Vector2& p_n) { a=p_a; b=p_b; n=p_n; }
+};
+
+
+class CircleShape2DSW : public Shape2DSW {
+
+
+ real_t radius;
+
+public:
+
+ _FORCE_INLINE_ const real_t& get_radius() const { return radius; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_CIRCLE; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ //real large
+ real_t d = p_normal.dot( p_transform.get_origin() );
+
+ // figure out scale at point
+ Vector2 local_normal = p_transform.basis_xform_inv(p_normal);
+ real_t scale = local_normal.length();
+
+ r_min = d - (radius) * scale;
+ r_max = d + (radius) * scale;
+ }
+
+};
+
+
+
+class RectangleShape2DSW : public Shape2DSW {
+
+
+ Vector2 half_extents;
+
+public:
+
+ _FORCE_INLINE_ const Vector2& get_half_extents() const { return half_extents; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_RECTANGLE; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ // no matter the angle, the box is mirrored anyway
+ Vector2 local_normal=p_transform.basis_xform_inv(p_normal);
+
+ float length = local_normal.abs().dot(half_extents);
+ float distance = p_normal.dot( p_transform.get_origin() );
+
+ r_min = distance - length;
+ r_max = distance + length;
+ }
+
+};
+
+class CapsuleShape2DSW : public Shape2DSW {
+
+
+ real_t radius;
+ real_t height;
+
+public:
+
+ _FORCE_INLINE_ const real_t& get_radius() const { return radius; }
+ _FORCE_INLINE_ const real_t& get_height() const { return height; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_CAPSULE; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ // no matter the angle, the box is mirrored anyway
+ Vector2 n=p_transform.basis_xform_inv(p_normal).normalized();
+ float h = (n.y > 0) ? height : -height;
+
+ n *= radius;
+ n.y += h * 0.5;
+
+ r_max = p_normal.dot(p_transform.xform(n));
+ r_min = p_normal.dot(p_transform.xform(-n));
+
+ if (r_max<r_min) {
+
+ SWAP(r_max,r_min);
+ }
+
+ //ERR_FAIL_COND( r_max < r_min );
+ }
+
+};
+
+
+class ConvexPolygonShape2DSW : public Shape2DSW {
+
+
+ struct Point {
+
+ Vector2 pos;
+ Vector2 normal; //normal to next segment
+ };
+
+ Point *points;
+ int point_count;
+
+public:
+
+ _FORCE_INLINE_ int get_point_count() const { return point_count; }
+ _FORCE_INLINE_ const Vector2& get_point(int p_idx) const { return points[p_idx].pos; }
+ _FORCE_INLINE_ const Vector2& get_segment_normal(int p_idx) const { return points[p_idx].normal; }
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_CONVEX_POLYGON; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal,p_transform,r_min,r_max); }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+ virtual real_t get_moment_of_inertia(float p_mass) const;
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ _FORCE_INLINE_ void project_range(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const {
+ // no matter the angle, the box is mirrored anyway
+
+ r_min = r_max = p_normal.dot(p_transform.xform(points[0].pos));
+ for(int i=1;i<point_count;i++) {
+
+ float d = p_normal.dot(p_transform.xform(points[i].pos));
+ if (d>r_max)
+ r_max=d;
+ if (d<r_min)
+ r_min=d;
+
+ }
+
+ }
+
+
+ ConvexPolygonShape2DSW();
+ ~ConvexPolygonShape2DSW();
+};
+
+
+class ConcaveShape2DSW : public Shape2DSW {
+
+public:
+
+ virtual bool is_concave() const { return true; }
+ typedef void (*Callback)(void* p_userdata,Shape2DSW *p_convex);
+
+ virtual void cull(const Rect2& p_local_aabb,Callback p_callback,void* p_userdata) const=0;
+
+};
+
+class ConcavePolygonShape2DSW : public ConcaveShape2DSW {
+
+ struct Segment {
+
+ int points[2];
+ };
+
+ Vector<Segment> segments;
+ Vector<Point2> points;
+
+ struct BVH {
+
+ Rect2 aabb;
+ int left,right;
+ };
+
+
+ Vector<BVH> bvh;
+ int bvh_depth;
+
+
+ struct BVH_CompareX {
+
+ _FORCE_INLINE_ bool operator ()(const BVH& a, const BVH& b) const {
+
+ return (a.aabb.pos.x+a.aabb.size.x*0.5) < (b.aabb.pos.x+b.aabb.size.x*0.5);
+ }
+ };
+
+ struct BVH_CompareY {
+
+ _FORCE_INLINE_ bool operator ()(const BVH& a, const BVH& b) const {
+
+ return (a.aabb.pos.y+a.aabb.size.y*0.5) < (b.aabb.pos.y+b.aabb.size.y*0.5);
+ }
+ };
+
+ int _generate_bvh(BVH *p_bvh,int p_len,int p_depth);
+
+public:
+
+ virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_CONCAVE_POLYGON; }
+
+ virtual void project_rangev(const Vector2& p_normal, const Matrix32& p_transform, real_t &r_min, real_t &r_max) const { /*project_range(p_normal,p_transform,r_min,r_max);*/ }
+ virtual void get_supports(const Vector2& p_normal,Vector2 *r_supports,int & r_amount) const;
+
+ virtual bool intersect_segment(const Vector2& p_begin,const Vector2& p_end,Vector2 &r_point, Vector2 &r_normal) const;
+
+ virtual real_t get_moment_of_inertia(float p_mass) const { return 0; }
+
+ virtual void set_data(const Variant& p_data);
+ virtual Variant get_data() const;
+
+ virtual void cull(const Rect2& p_local_aabb,Callback p_callback,void* p_userdata) const;
+
+};
+
+
+#endif // SHAPE_2D_2DSW_H
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
new file mode 100644
index 0000000000..4fe53aeb4d
--- /dev/null
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -0,0 +1,432 @@
+/*************************************************************************/
+/* space_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "space_2d_sw.h"
+#include "collision_solver_2d_sw.h"
+#include "physics_2d_server_sw.h"
+
+
+bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2& p_from, const Vector2& p_to,RayResult &r_result,const Set<RID>& p_exclude,uint32_t p_user_mask) {
+
+
+ ERR_FAIL_COND_V(space->locked,false);
+
+ Vector2 begin,end;
+ Vector2 normal;
+ begin=p_from;
+ end=p_to;
+ normal=(end-begin).normalized();
+
+
+ int amount = space->broadphase->cull_segment(begin,end,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
+
+ //todo, create another array tha references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision
+
+ bool collided=false;
+ Vector2 res_point,res_normal;
+ int res_shape;
+ const CollisionObject2DSW *res_obj;
+ real_t min_d=1e10;
+
+
+ for(int i=0;i<amount;i++) {
+
+ if (space->intersection_query_results[i]->get_type()==CollisionObject2DSW::TYPE_AREA)
+ continue; //ignore area
+
+ if (p_exclude.has( space->intersection_query_results[i]->get_self()))
+ continue;
+
+ const CollisionObject2DSW *col_obj=space->intersection_query_results[i];
+
+ int shape_idx=space->intersection_query_subindex_results[i];
+ Matrix32 inv_xform = col_obj->get_shape_inv_transform(shape_idx) * col_obj->get_inv_transform();
+
+ Vector2 local_from = inv_xform.xform(begin);
+ Vector2 local_to = inv_xform.xform(end);
+
+ /*local_from = col_obj->get_inv_transform().xform(begin);
+ local_from = col_obj->get_shape_inv_transform(shape_idx).xform(local_from);
+
+ local_to = col_obj->get_inv_transform().xform(end);
+ local_to = col_obj->get_shape_inv_transform(shape_idx).xform(local_to);*/
+
+ const Shape2DSW *shape = col_obj->get_shape(shape_idx);
+
+ Vector2 shape_point,shape_normal;
+
+
+ if (shape->intersect_segment(local_from,local_to,shape_point,shape_normal)) {
+
+
+ //print_line("inters sgment!");
+ Matrix32 xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
+ shape_point=xform.xform(shape_point);
+
+ real_t ld = normal.dot(shape_point);
+
+
+ if (ld<min_d) {
+
+ min_d=ld;
+ res_point=shape_point;
+ res_normal=inv_xform.basis_xform_inv(shape_normal).normalized();
+ res_shape=shape_idx;
+ res_obj=col_obj;
+ collided=true;
+ }
+ }
+
+ }
+
+ if (!collided)
+ return false;
+
+
+ r_result.collider_id=res_obj->get_instance_id();
+ if (r_result.collider_id!=0)
+ r_result.collider=ObjectDB::get_instance(r_result.collider_id);
+ r_result.normal=res_normal;
+ r_result.position=res_point;
+ r_result.rid=res_obj->get_self();
+ r_result.shape=res_shape;
+
+ return true;
+
+}
+
+
+int Physics2DDirectSpaceStateSW::intersect_shape(const RID& p_shape, const Matrix32& p_xform,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude,uint32_t p_user_mask) {
+
+ if (p_result_max<=0)
+ return 0;
+
+ Shape2DSW *shape = static_cast<Physics2DServerSW*>(Physics2DServer::get_singleton())->shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape,0);
+
+ Rect2 aabb = p_xform.xform(shape->get_aabb());
+
+ int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
+
+ bool collided=false;
+ int cc=0;
+
+ for(int i=0;i<amount;i++) {
+
+ if (cc>=p_result_max)
+ break;
+
+ if (space->intersection_query_results[i]->get_type()==CollisionObject2DSW::TYPE_AREA)
+ continue; //ignore area
+
+ if (p_exclude.has( space->intersection_query_results[i]->get_self()))
+ continue;
+
+
+ const CollisionObject2DSW *col_obj=space->intersection_query_results[i];
+ int shape_idx=space->intersection_query_subindex_results[i];
+
+ if (!CollisionSolver2DSW::solve_static(shape,p_xform,p_xform.affine_inverse(),col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), col_obj->get_inv_transform() * col_obj->get_shape_inv_transform(shape_idx),NULL,NULL,NULL))
+ continue;
+
+ r_results[cc].collider_id=col_obj->get_instance_id();
+ if (r_results[cc].collider_id!=0)
+ r_results[cc].collider=ObjectDB::get_instance(r_results[cc].collider_id);
+ r_results[cc].rid=col_obj->get_self();
+ r_results[cc].shape=shape_idx;
+
+ cc++;
+
+ }
+
+ return cc;
+
+}
+
+Physics2DDirectSpaceStateSW::Physics2DDirectSpaceStateSW() {
+
+
+ space=NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+
+
+
+void* Space2DSW::_broadphase_pair(CollisionObject2DSW *A,int p_subindex_A,CollisionObject2DSW *B,int p_subindex_B,void *p_self) {
+
+ CollisionObject2DSW::Type type_A=A->get_type();
+ CollisionObject2DSW::Type type_B=B->get_type();
+ if (type_A>type_B) {
+
+ SWAP(A,B);
+ SWAP(p_subindex_A,p_subindex_B);
+ SWAP(type_A,type_B);
+ }
+
+ Space2DSW *self = (Space2DSW*)p_self;
+
+ if (type_A==CollisionObject2DSW::TYPE_AREA) {
+
+
+ ERR_FAIL_COND_V(type_B!=CollisionObject2DSW::TYPE_BODY,NULL);
+ Area2DSW *area=static_cast<Area2DSW*>(A);
+ Body2DSW *body=static_cast<Body2DSW*>(B);
+
+ AreaPair2DSW *area_pair = memnew(AreaPair2DSW(body,p_subindex_B,area,p_subindex_A) );
+
+ return area_pair;
+ } else {
+
+
+ BodyPair2DSW *b = memnew( BodyPair2DSW((Body2DSW*)A,p_subindex_A,(Body2DSW*)B,p_subindex_B) );
+ return b;
+
+ }
+
+ return NULL;
+}
+
+void Space2DSW::_broadphase_unpair(CollisionObject2DSW *A,int p_subindex_A,CollisionObject2DSW *B,int p_subindex_B,void *p_data,void *p_self) {
+
+
+
+ Space2DSW *self = (Space2DSW*)p_self;
+ Constraint2DSW *c = (Constraint2DSW*)p_data;
+ memdelete(c);
+}
+
+
+const SelfList<Body2DSW>::List& Space2DSW::get_active_body_list() const {
+
+ return active_list;
+}
+void Space2DSW::body_add_to_active_list(SelfList<Body2DSW>* p_body) {
+
+ active_list.add(p_body);
+}
+void Space2DSW::body_remove_from_active_list(SelfList<Body2DSW>* p_body) {
+
+ active_list.remove(p_body);
+
+}
+
+void Space2DSW::body_add_to_inertia_update_list(SelfList<Body2DSW>* p_body) {
+
+
+ inertia_update_list.add(p_body);
+}
+
+void Space2DSW::body_remove_from_inertia_update_list(SelfList<Body2DSW>* p_body) {
+
+ inertia_update_list.remove(p_body);
+}
+
+BroadPhase2DSW *Space2DSW::get_broadphase() {
+
+ return broadphase;
+}
+
+void Space2DSW::add_object(CollisionObject2DSW *p_object) {
+
+ ERR_FAIL_COND( objects.has(p_object) );
+ objects.insert(p_object);
+}
+
+void Space2DSW::remove_object(CollisionObject2DSW *p_object) {
+
+ ERR_FAIL_COND( !objects.has(p_object) );
+ objects.erase(p_object);
+}
+
+const Set<CollisionObject2DSW*> &Space2DSW::get_objects() const {
+
+ return objects;
+}
+
+void Space2DSW::body_add_to_state_query_list(SelfList<Body2DSW>* p_body) {
+
+ state_query_list.add(p_body);
+}
+void Space2DSW::body_remove_from_state_query_list(SelfList<Body2DSW>* p_body) {
+
+ state_query_list.remove(p_body);
+}
+
+void Space2DSW::area_add_to_monitor_query_list(SelfList<Area2DSW>* p_area) {
+
+ monitor_query_list.add(p_area);
+}
+void Space2DSW::area_remove_from_monitor_query_list(SelfList<Area2DSW>* p_area) {
+
+ monitor_query_list.remove(p_area);
+}
+
+void Space2DSW::area_add_to_moved_list(SelfList<Area2DSW>* p_area) {
+
+ area_moved_list.add(p_area);
+}
+
+void Space2DSW::area_remove_from_moved_list(SelfList<Area2DSW>* p_area) {
+
+ area_moved_list.remove(p_area);
+}
+
+const SelfList<Area2DSW>::List& Space2DSW::get_moved_area_list() const {
+
+ return area_moved_list;
+}
+
+
+void Space2DSW::call_queries() {
+
+ while(state_query_list.first()) {
+
+ Body2DSW * b = state_query_list.first()->self();
+ b->call_queries();
+ state_query_list.remove(state_query_list.first());
+ }
+
+ while(monitor_query_list.first()) {
+
+ Area2DSW * a = monitor_query_list.first()->self();
+ a->call_queries();
+ monitor_query_list.remove(monitor_query_list.first());
+ }
+
+}
+
+void Space2DSW::setup() {
+
+
+ while(inertia_update_list.first()) {
+ inertia_update_list.first()->self()->update_inertias();
+ inertia_update_list.remove(inertia_update_list.first());
+ }
+
+
+}
+
+void Space2DSW::update() {
+
+ broadphase->update();
+
+}
+
+
+void Space2DSW::set_param(Physics2DServer::SpaceParameter p_param, real_t p_value) {
+
+ switch(p_param) {
+
+ case Physics2DServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: contact_recycle_radius=p_value; break;
+ case Physics2DServer::SPACE_PARAM_CONTACT_MAX_SEPARATION: contact_max_separation=p_value; break;
+ case Physics2DServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION: contact_max_allowed_penetration=p_value; break;
+ case Physics2DServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD: body_linear_velocity_sleep_treshold=p_value; break;
+ case Physics2DServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD: body_angular_velocity_sleep_treshold=p_value; break;
+ case Physics2DServer::SPACE_PARAM_BODY_TIME_TO_SLEEP: body_time_to_sleep=p_value; break;
+ case Physics2DServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO: body_angular_velocity_damp_ratio=p_value; break;
+ case Physics2DServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: constraint_bias=p_value; break;
+ }
+}
+
+real_t Space2DSW::get_param(Physics2DServer::SpaceParameter p_param) const {
+
+ switch(p_param) {
+
+ case Physics2DServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: return contact_recycle_radius;
+ case Physics2DServer::SPACE_PARAM_CONTACT_MAX_SEPARATION: return contact_max_separation;
+ case Physics2DServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION: return contact_max_allowed_penetration;
+ case Physics2DServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD: return body_linear_velocity_sleep_treshold;
+ case Physics2DServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD: return body_angular_velocity_sleep_treshold;
+ case Physics2DServer::SPACE_PARAM_BODY_TIME_TO_SLEEP: return body_time_to_sleep;
+ case Physics2DServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO: return body_angular_velocity_damp_ratio;
+ case Physics2DServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: return constraint_bias;
+ }
+ return 0;
+}
+
+void Space2DSW::lock() {
+
+ locked=true;
+}
+
+void Space2DSW::unlock() {
+
+ locked=false;
+}
+
+bool Space2DSW::is_locked() const {
+
+ return locked;
+}
+
+Physics2DDirectSpaceStateSW *Space2DSW::get_direct_state() {
+
+ return direct_access;
+}
+
+Space2DSW::Space2DSW() {
+
+
+ locked=false;
+ contact_recycle_radius=0.01;
+ contact_max_separation=0.05;
+ contact_max_allowed_penetration= 0.01;
+
+ constraint_bias = 0.01;
+ body_linear_velocity_sleep_treshold=0.01;
+ body_angular_velocity_sleep_treshold=(8.0 / 180.0 * Math_PI);
+ body_time_to_sleep=0.5;
+ body_angular_velocity_damp_ratio=15;
+
+
+ broadphase = BroadPhase2DSW::create_func();
+ broadphase->set_pair_callback(_broadphase_pair,this);
+ broadphase->set_unpair_callback(_broadphase_unpair,this);
+ area=NULL;
+
+ direct_access = memnew( Physics2DDirectSpaceStateSW );
+ direct_access->space=this;
+}
+
+Space2DSW::~Space2DSW() {
+
+ memdelete(broadphase);
+ memdelete( direct_access );
+}
+
+
+
diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h
new file mode 100644
index 0000000000..f65ec14427
--- /dev/null
+++ b/servers/physics_2d/space_2d_sw.h
@@ -0,0 +1,160 @@
+/*************************************************************************/
+/* space_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SPACE_2D_SW_H
+#define SPACE_2D_SW_H
+
+#include "typedefs.h"
+#include "hash_map.h"
+#include "body_2d_sw.h"
+#include "area_2d_sw.h"
+#include "body_pair_2d_sw.h"
+#include "area_pair_2d_sw.h"
+#include "broad_phase_2d_sw.h"
+#include "collision_object_2d_sw.h"
+
+
+class Physics2DDirectSpaceStateSW : public Physics2DDirectSpaceState {
+
+ OBJ_TYPE( Physics2DDirectSpaceStateSW, Physics2DDirectSpaceState );
+public:
+
+ Space2DSW *space;
+
+ bool intersect_ray(const Vector2& p_from, const Vector2& p_to,RayResult &r_result,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0);
+ int intersect_shape(const RID& p_shape, const Matrix32& p_xform,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0);
+
+ Physics2DDirectSpaceStateSW();
+};
+
+
+
+class Space2DSW {
+
+
+ Physics2DDirectSpaceStateSW *direct_access;
+ RID self;
+
+ BroadPhase2DSW *broadphase;
+ SelfList<Body2DSW>::List active_list;
+ SelfList<Body2DSW>::List inertia_update_list;
+ SelfList<Body2DSW>::List state_query_list;
+ SelfList<Area2DSW>::List monitor_query_list;
+ SelfList<Area2DSW>::List area_moved_list;
+
+ static void* _broadphase_pair(CollisionObject2DSW *A,int p_subindex_A,CollisionObject2DSW *B,int p_subindex_B,void *p_self);
+ static void _broadphase_unpair(CollisionObject2DSW *A,int p_subindex_A,CollisionObject2DSW *B,int p_subindex_B,void *p_data,void *p_self);
+
+ Set<CollisionObject2DSW*> objects;
+
+ Area2DSW *area;
+
+ real_t contact_recycle_radius;
+ real_t contact_max_separation;
+ real_t contact_max_allowed_penetration;
+ real_t constraint_bias;
+
+ enum {
+
+ INTERSECTION_QUERY_MAX=2048
+ };
+
+ CollisionObject2DSW *intersection_query_results[INTERSECTION_QUERY_MAX];
+ int intersection_query_subindex_results[INTERSECTION_QUERY_MAX];
+
+ float body_linear_velocity_sleep_treshold;
+ float body_angular_velocity_sleep_treshold;
+ float body_time_to_sleep;
+ float body_angular_velocity_damp_ratio;
+
+ bool locked;
+
+friend class Physics2DDirectSpaceStateSW;
+
+public:
+
+ _FORCE_INLINE_ void set_self(const RID& p_self) { self=p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ void set_default_area(Area2DSW *p_area) { area=p_area; }
+ Area2DSW *get_default_area() const { return area; }
+
+ const SelfList<Body2DSW>::List& get_active_body_list() const;
+ void body_add_to_active_list(SelfList<Body2DSW>* p_body);
+ void body_remove_from_active_list(SelfList<Body2DSW>* p_body);
+ void body_add_to_inertia_update_list(SelfList<Body2DSW>* p_body);
+ void body_remove_from_inertia_update_list(SelfList<Body2DSW>* p_body);
+ void area_add_to_moved_list(SelfList<Area2DSW>* p_area);
+ void area_remove_from_moved_list(SelfList<Area2DSW>* p_area);
+ const SelfList<Area2DSW>::List& get_moved_area_list() const;
+
+
+
+
+ void body_add_to_state_query_list(SelfList<Body2DSW>* p_body);
+ void body_remove_from_state_query_list(SelfList<Body2DSW>* p_body);
+
+ void area_add_to_monitor_query_list(SelfList<Area2DSW>* p_area);
+ void area_remove_from_monitor_query_list(SelfList<Area2DSW>* p_area);
+
+ BroadPhase2DSW *get_broadphase();
+
+ void add_object(CollisionObject2DSW *p_object);
+ void remove_object(CollisionObject2DSW *p_object);
+ const Set<CollisionObject2DSW*> &get_objects() const;
+
+ _FORCE_INLINE_ real_t get_contact_recycle_radius() const { return contact_recycle_radius; }
+ _FORCE_INLINE_ real_t get_contact_max_separation() const { return contact_max_separation; }
+ _FORCE_INLINE_ real_t get_contact_max_allowed_penetration() const { return contact_max_allowed_penetration; }
+ _FORCE_INLINE_ real_t get_constraint_bias() const { return constraint_bias; }
+ _FORCE_INLINE_ real_t get_body_linear_velocity_sleep_treshold() const { return body_linear_velocity_sleep_treshold; }
+ _FORCE_INLINE_ real_t get_body_angular_velocity_sleep_treshold() const { return body_angular_velocity_sleep_treshold; }
+ _FORCE_INLINE_ real_t get_body_time_to_sleep() const { return body_time_to_sleep; }
+ _FORCE_INLINE_ real_t get_body_angular_velocity_damp_ratio() const { return body_angular_velocity_damp_ratio; }
+
+
+ void update();
+ void setup();
+ void call_queries();
+
+
+ bool is_locked() const;
+ void lock();
+ void unlock();
+
+ void set_param(Physics2DServer::SpaceParameter p_param, real_t p_value);
+ real_t get_param(Physics2DServer::SpaceParameter p_param) const;
+
+ Physics2DDirectSpaceStateSW *get_direct_state();
+
+ Space2DSW();
+ ~Space2DSW();
+};
+
+
+#endif // SPACE_2D_SW_H
diff --git a/servers/physics_2d/step_2d_sw.cpp b/servers/physics_2d/step_2d_sw.cpp
new file mode 100644
index 0000000000..9f41fc94eb
--- /dev/null
+++ b/servers/physics_2d/step_2d_sw.cpp
@@ -0,0 +1,239 @@
+/*************************************************************************/
+/* step_2d_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "step_2d_sw.h"
+
+
+void Step2DSW::_populate_island(Body2DSW* p_body,Body2DSW** p_island,Constraint2DSW **p_constraint_island) {
+
+ p_body->set_island_step(_step);
+ p_body->set_island_next(*p_island);
+ *p_island=p_body;
+
+ for(Map<Constraint2DSW*,int>::Element *E=p_body->get_constraint_map().front();E;E=E->next()) {
+
+ Constraint2DSW *c=(Constraint2DSW*)E->key();
+ if (c->get_island_step()==_step)
+ continue; //already processed
+ c->set_island_step(_step);
+ c->set_island_next(*p_constraint_island);
+ *p_constraint_island=c;
+
+
+ for(int i=0;i<c->get_body_count();i++) {
+ if (i==E->get())
+ continue;
+ Body2DSW *b = c->get_body_ptr()[i];
+ if (b->get_island_step()==_step || b->get_mode()==Physics2DServer::BODY_MODE_STATIC)
+ continue; //no go
+ _populate_island(c->get_body_ptr()[i],p_island,p_constraint_island);
+ }
+ }
+}
+
+void Step2DSW::_setup_island(Constraint2DSW *p_island,float p_delta) {
+
+ Constraint2DSW *ci=p_island;
+ while(ci) {
+ bool process = ci->setup(p_delta);
+ //todo remove from island if process fails
+ ci=ci->get_island_next();
+ }
+}
+
+void Step2DSW::_solve_island(Constraint2DSW *p_island,int p_iterations,float p_delta){
+
+
+ for(int i=0;i<p_iterations;i++) {
+
+ Constraint2DSW *ci=p_island;
+ while(ci) {
+ ci->solve(p_delta);
+ ci=ci->get_island_next();
+ }
+ }
+}
+
+void Step2DSW::_check_suspend(Body2DSW *p_island,float p_delta) {
+
+
+ bool can_sleep=true;
+
+ Body2DSW *b = p_island;
+ while(b) {
+
+ if (b->get_mode()==Physics2DServer::BODY_MODE_STATIC)
+ continue; //ignore for static
+
+ if (!b->sleep_test(p_delta))
+ can_sleep=false;
+
+ b=b->get_island_next();
+ }
+
+ //put all to sleep or wake up everyoen
+
+ b = p_island;
+ while(b) {
+
+ if (b->get_mode()==Physics2DServer::BODY_MODE_STATIC)
+ continue; //ignore for static
+
+ bool active = b->is_active();
+
+ if (active==can_sleep)
+ b->set_active(!can_sleep);
+
+ b=b->get_island_next();
+ }
+}
+
+void Step2DSW::step(Space2DSW* p_space,float p_delta,int p_iterations) {
+
+
+ p_space->lock(); // can't access space during this
+
+ p_space->setup(); //update inertias, etc
+
+ const SelfList<Body2DSW>::List * body_list = &p_space->get_active_body_list();
+
+ /* INTEGRATE FORCES */
+ int active_count=0;
+
+ const SelfList<Body2DSW>*b = body_list->first();
+ while(b) {
+
+ b->self()->integrate_forces(p_delta);
+ b=b->next();
+ active_count++;
+ }
+
+ /* GENERATE CONSTRAINT ISLANDS */
+
+ Body2DSW *island_list=NULL;
+ Constraint2DSW *constraint_island_list=NULL;
+ b = body_list->first();
+
+ int island_count=0;
+
+ while(b) {
+ Body2DSW *body = b->self();
+
+
+ if (body->get_island_step()!=_step) {
+
+ Body2DSW *island=NULL;
+ Constraint2DSW *constraint_island=NULL;
+ _populate_island(body,&island,&constraint_island);
+
+ island->set_island_list_next(island_list);
+ island_list=island;
+
+ if (constraint_island) {
+ constraint_island->set_island_list_next(constraint_island_list);
+ constraint_island_list=constraint_island;
+ island_count++;
+ }
+
+ }
+ b=b->next();
+ }
+
+ const SelfList<Area2DSW>::List &aml = p_space->get_moved_area_list();
+
+
+
+ while(aml.first()) {
+ for(const Set<Constraint2DSW*>::Element *E=aml.first()->self()->get_constraints().front();E;E=E->next()) {
+
+ Constraint2DSW*c=E->get();
+ if (c->get_island_step()==_step)
+ continue;
+ c->set_island_step(_step);
+ c->set_island_next(NULL);
+ c->set_island_list_next(constraint_island_list);
+ constraint_island_list=c;
+ }
+ p_space->area_remove_from_moved_list((SelfList<Area2DSW>*)aml.first()); //faster to remove here
+ }
+
+// print_line("island count: "+itos(island_count)+" active count: "+itos(active_count));
+ /* SETUP CONSTRAINT ISLANDS */
+
+ {
+ Constraint2DSW *ci=constraint_island_list;
+ while(ci) {
+
+ _setup_island(ci,p_delta);
+ ci=ci->get_island_list_next();
+ }
+ }
+
+ /* SOLVE CONSTRAINT ISLANDS */
+
+ {
+ Constraint2DSW *ci=constraint_island_list;
+ while(ci) {
+ //iterating each island separatedly improves cache efficiency
+ _solve_island(ci,p_iterations,p_delta);
+ ci=ci->get_island_list_next();
+ }
+ }
+
+ /* INTEGRATE VELOCITIES */
+
+ b = body_list->first();
+ while(b) {
+
+ b->self()->integrate_velocities(p_delta);
+ b=b->next();
+ }
+
+ /* SLEEP / WAKE UP ISLANDS */
+
+ {
+ Body2DSW *bi=island_list;
+ while(bi) {
+
+ _check_suspend(bi,p_delta);
+ bi=bi->get_island_list_next();
+ }
+ }
+
+ p_space->update();
+ p_space->unlock();
+ _step++;
+
+
+
+}
+
+Step2DSW::Step2DSW() {
+
+ _step=1;
+}
diff --git a/servers/physics_2d/step_2d_sw.h b/servers/physics_2d/step_2d_sw.h
new file mode 100644
index 0000000000..91ac9d8584
--- /dev/null
+++ b/servers/physics_2d/step_2d_sw.h
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* step_2d_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 STEP_2D_SW_H
+#define STEP_2D_SW_H
+
+#include "space_2d_sw.h"
+
+class Step2DSW {
+
+ uint64_t _step;
+
+ void _populate_island(Body2DSW* p_body,Body2DSW** p_island,Constraint2DSW **p_constraint_island);
+ void _setup_island(Constraint2DSW *p_island,float p_delta);
+ void _solve_island(Constraint2DSW *p_island,int p_iterations,float p_delta);
+ void _check_suspend(Body2DSW *p_island,float p_delta);
+public:
+
+ void step(Space2DSW* p_space,float p_delta,int p_iterations);
+ Step2DSW();
+};
+
+#endif // STEP_2D_SW_H
diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp
new file mode 100644
index 0000000000..cae9565c46
--- /dev/null
+++ b/servers/physics_2d_server.cpp
@@ -0,0 +1,417 @@
+/*************************************************************************/
+/* physics_2d_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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_2d_server.h"
+#include "print_string.h"
+Physics2DServer * Physics2DServer::singleton=NULL;
+
+
+void Physics2DDirectBodyState::integrate_forces() {
+
+ real_t step = get_step();
+ Vector2 lv = get_linear_velocity();
+ lv+=get_total_gravity() * step;
+
+ real_t av = get_angular_velocity();
+
+ float damp = 1.0 - step * get_total_density();
+
+ if (damp<0) // reached zero in the given time
+ damp=0;
+
+ lv*=damp;
+ av*=damp;
+
+ set_linear_velocity(lv);
+ set_angular_velocity(av);
+
+
+
+
+}
+
+Object* Physics2DDirectBodyState::get_contact_collider_object(int p_contact_idx) const {
+
+ ObjectID objid = get_contact_collider_id(p_contact_idx);
+ Object *obj = ObjectDB::get_instance( objid );
+ return obj;
+}
+
+Physics2DServer * Physics2DServer::get_singleton() {
+
+ return singleton;
+}
+
+void Physics2DDirectBodyState::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("get_total_gravity"),&Physics2DDirectBodyState::get_total_gravity);
+ ObjectTypeDB::bind_method(_MD("get_total_density"),&Physics2DDirectBodyState::get_total_density);
+
+ ObjectTypeDB::bind_method(_MD("get_inverse_mass"),&Physics2DDirectBodyState::get_inverse_mass);
+ ObjectTypeDB::bind_method(_MD("get_inverse_inertia"),&Physics2DDirectBodyState::get_inverse_inertia);
+
+ ObjectTypeDB::bind_method(_MD("set_linear_velocity","velocity"),&Physics2DDirectBodyState::set_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&Physics2DDirectBodyState::get_linear_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_angular_velocity","velocity"),&Physics2DDirectBodyState::set_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("get_angular_velocity"),&Physics2DDirectBodyState::get_angular_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_transform","transform"),&Physics2DDirectBodyState::set_transform);
+ ObjectTypeDB::bind_method(_MD("get_transform"),&Physics2DDirectBodyState::get_transform);
+
+ ObjectTypeDB::bind_method(_MD("set_sleep_state","enabled"),&Physics2DDirectBodyState::set_sleep_state);
+ ObjectTypeDB::bind_method(_MD("is_sleeping"),&Physics2DDirectBodyState::is_sleeping);
+
+ ObjectTypeDB::bind_method(_MD("get_contact_count"),&Physics2DDirectBodyState::get_contact_count);
+
+ ObjectTypeDB::bind_method(_MD("get_contact_local_pos","contact_idx"),&Physics2DDirectBodyState::get_contact_local_pos);
+ ObjectTypeDB::bind_method(_MD("get_contact_local_normal","contact_idx"),&Physics2DDirectBodyState::get_contact_local_normal);
+ ObjectTypeDB::bind_method(_MD("get_contact_local_shape","contact_idx"),&Physics2DDirectBodyState::get_contact_local_shape);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider","contact_idx"),&Physics2DDirectBodyState::get_contact_collider);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_pos","contact_idx"),&Physics2DDirectBodyState::get_contact_collider_pos);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_id","contact_idx"),&Physics2DDirectBodyState::get_contact_collider_id);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_object","contact_idx"),&Physics2DDirectBodyState::get_contact_collider_object);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_shape","contact_idx"),&Physics2DDirectBodyState::get_contact_collider_shape);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_velocity_at_pos","contact_idx"),&Physics2DDirectBodyState::get_contact_collider_velocity_at_pos);
+ ObjectTypeDB::bind_method(_MD("get_step"),&Physics2DDirectBodyState::get_step);
+ ObjectTypeDB::bind_method(_MD("integrate_forces"),&Physics2DDirectBodyState::integrate_forces);
+ ObjectTypeDB::bind_method(_MD("get_space_state:Physics2DDirectSpaceState"),&Physics2DDirectBodyState::get_space_state);
+
+}
+
+Physics2DDirectBodyState::Physics2DDirectBodyState() {}
+
+///////////////////////////////////////////////////////
+
+
+
+Variant Physics2DDirectSpaceState::_intersect_ray(const Vector2& p_from, const Vector2& p_to,const Vector<RID>& p_exclude,uint32_t p_user_mask) {
+
+ RayResult inters;
+ Set<RID> exclude;
+ for(int i=0;i<p_exclude.size();i++)
+ exclude.insert(p_exclude[i]);
+
+ bool res = intersect_ray(p_from,p_to,inters,exclude,p_user_mask);
+
+ if (!res)
+ return Variant();
+
+ Dictionary d;
+ d["position"]=inters.position;
+ d["normal"]=inters.normal;
+ d["collider_id"]=inters.collider_id;
+ d["collider"]=inters.collider;
+ d["shape"]=inters.shape;
+ d["rid"]=inters.rid;
+
+ return d;
+}
+
+Variant Physics2DDirectSpaceState::_intersect_shape(const RID& p_shape, const Matrix32& p_xform,int p_result_max,const Vector<RID>& p_exclude,uint32_t p_user_mask) {
+
+ ERR_FAIL_INDEX_V(p_result_max,4096,Variant());
+ if (p_result_max<=0)
+ return Variant();
+
+ Set<RID> exclude;
+ for(int i=0;i<p_exclude.size();i++)
+ exclude.insert(p_exclude[i]);
+
+ ShapeResult *res=(ShapeResult*)alloca(p_result_max*sizeof(ShapeResult));
+
+ int rc = intersect_shape(p_shape,p_xform,res,p_result_max,exclude,p_user_mask);
+
+ if (rc==0)
+ return Variant();
+
+ Ref<Physics2DShapeQueryResult> result = memnew( Physics2DShapeQueryResult );
+ result->result.resize(rc);
+ for(int i=0;i<rc;i++)
+ result->result[i]=res[i];
+
+ return result;
+
+}
+
+
+
+
+
+Physics2DDirectSpaceState::Physics2DDirectSpaceState() {
+
+
+
+}
+
+
+void Physics2DDirectSpaceState::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("intersect_ray:Dictionary","from","to","exclude","umask"),&Physics2DDirectSpaceState::_intersect_ray,DEFVAL(Array()),DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("intersect_shape:Physics2DShapeQueryResult","shape","xform","result_max","exclude","umask"),&Physics2DDirectSpaceState::_intersect_shape,DEFVAL(Array()),DEFVAL(0));
+
+}
+
+
+int Physics2DShapeQueryResult::get_result_count() const {
+
+ return result.size();
+}
+RID Physics2DShapeQueryResult::get_result_rid(int p_idx) const {
+
+ return result[p_idx].rid;
+}
+ObjectID Physics2DShapeQueryResult::get_result_object_id(int p_idx) const {
+
+ return result[p_idx].collider_id;
+}
+Object* Physics2DShapeQueryResult::get_result_object(int p_idx) const {
+
+ return result[p_idx].collider;
+}
+int Physics2DShapeQueryResult::get_result_object_shape(int p_idx) const {
+
+ return result[p_idx].shape;
+}
+
+Physics2DShapeQueryResult::Physics2DShapeQueryResult() {
+
+
+}
+
+void Physics2DShapeQueryResult::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("get_result_count"),&Physics2DShapeQueryResult::get_result_count);
+ ObjectTypeDB::bind_method(_MD("get_result_rid","idx"),&Physics2DShapeQueryResult::get_result_rid);
+ ObjectTypeDB::bind_method(_MD("get_result_object_id","idx"),&Physics2DShapeQueryResult::get_result_object_id);
+ ObjectTypeDB::bind_method(_MD("get_result_object","idx"),&Physics2DShapeQueryResult::get_result_object);
+ ObjectTypeDB::bind_method(_MD("get_result_object_shape","idx"),&Physics2DShapeQueryResult::get_result_object_shape);
+
+
+}
+
+
+
+
+
+
+///////////////////////////////////////
+
+void Physics2DServer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("shape_create","type"),&Physics2DServer::shape_create);
+ ObjectTypeDB::bind_method(_MD("shape_set_data","shape","data"),&Physics2DServer::shape_set_data);
+
+ ObjectTypeDB::bind_method(_MD("shape_get_type","shape"),&Physics2DServer::shape_get_type);
+ ObjectTypeDB::bind_method(_MD("shape_get_data","shape"),&Physics2DServer::shape_get_data);
+
+
+ ObjectTypeDB::bind_method(_MD("space_create"),&Physics2DServer::space_create);
+ ObjectTypeDB::bind_method(_MD("space_set_active","space","active"),&Physics2DServer::space_set_active);
+ ObjectTypeDB::bind_method(_MD("space_is_active","space"),&Physics2DServer::space_is_active);
+ ObjectTypeDB::bind_method(_MD("space_set_param","space","param","value"),&Physics2DServer::space_set_param);
+ ObjectTypeDB::bind_method(_MD("space_get_param","space","param"),&Physics2DServer::space_get_param);
+ ObjectTypeDB::bind_method(_MD("space_get_direct_state:Physics2DDirectSpaceState","space"),&Physics2DServer::space_get_direct_state);
+
+ ObjectTypeDB::bind_method(_MD("area_create"),&Physics2DServer::area_create);
+ ObjectTypeDB::bind_method(_MD("area_set_space","area","space"),&Physics2DServer::area_set_space);
+ ObjectTypeDB::bind_method(_MD("area_get_space","area"),&Physics2DServer::area_get_space);
+
+ ObjectTypeDB::bind_method(_MD("area_set_space_override_mode","area","mode"),&Physics2DServer::area_set_space_override_mode);
+ ObjectTypeDB::bind_method(_MD("area_get_space_override_mode","area"),&Physics2DServer::area_get_space_override_mode);
+
+ ObjectTypeDB::bind_method(_MD("area_add_shape","area","shape","transform"),&Physics2DServer::area_set_shape,DEFVAL(Matrix32()));
+ ObjectTypeDB::bind_method(_MD("area_set_shape","area","shape_idx","shape"),&Physics2DServer::area_get_shape);
+ ObjectTypeDB::bind_method(_MD("area_set_shape_transform","area","shape_idx","transform"),&Physics2DServer::area_set_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_get_shape_count","area"),&Physics2DServer::area_get_shape_count);
+ ObjectTypeDB::bind_method(_MD("area_get_shape","area","shape_idx"),&Physics2DServer::area_get_shape);
+ ObjectTypeDB::bind_method(_MD("area_get_shape_transform","area","shape_idx"),&Physics2DServer::area_get_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_remove_shape","area","shape_idx"),&Physics2DServer::area_remove_shape);
+ ObjectTypeDB::bind_method(_MD("area_clear_shapes","area"),&Physics2DServer::area_clear_shapes);
+
+
+ ObjectTypeDB::bind_method(_MD("area_set_param","area","param","value"),&Physics2DServer::area_get_param);
+ ObjectTypeDB::bind_method(_MD("area_set_transform","area","transform"),&Physics2DServer::area_get_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_get_param","area","param"),&Physics2DServer::area_get_param);
+ ObjectTypeDB::bind_method(_MD("area_get_transform","area"),&Physics2DServer::area_get_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_attach_object_instance_ID","area","id"),&Physics2DServer::area_attach_object_instance_ID);
+ ObjectTypeDB::bind_method(_MD("area_get_object_instance_ID","area"),&Physics2DServer::area_get_object_instance_ID);
+
+ ObjectTypeDB::bind_method(_MD("area_set_monitor_callback","receiver","method"),&Physics2DServer::area_set_monitor_callback);
+
+ ObjectTypeDB::bind_method(_MD("body_create","mode","init_sleeping"),&Physics2DServer::body_create,DEFVAL(BODY_MODE_RIGID),DEFVAL(false));
+
+ ObjectTypeDB::bind_method(_MD("body_set_space","body","space"),&Physics2DServer::body_set_space);
+ ObjectTypeDB::bind_method(_MD("body_get_space","body"),&Physics2DServer::body_get_space);
+
+ ObjectTypeDB::bind_method(_MD("body_set_mode","body","mode"),&Physics2DServer::body_set_mode);
+ ObjectTypeDB::bind_method(_MD("body_get_mode","body"),&Physics2DServer::body_get_mode);
+
+ ObjectTypeDB::bind_method(_MD("body_add_shape","body","shape","transform"),&Physics2DServer::body_add_shape,DEFVAL(Matrix32()));
+ ObjectTypeDB::bind_method(_MD("body_set_shape","body","shape_idx","shape"),&Physics2DServer::body_set_shape);
+ ObjectTypeDB::bind_method(_MD("body_set_shape_transform","body","shape_idx","transform"),&Physics2DServer::body_set_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("body_get_shape_count","body"),&Physics2DServer::body_get_shape_count);
+ ObjectTypeDB::bind_method(_MD("body_get_shape","body","shape_idx"),&Physics2DServer::body_get_shape);
+ ObjectTypeDB::bind_method(_MD("body_get_shape_transform","body","shape_idx"),&Physics2DServer::body_get_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("body_remove_shape","body","shape_idx"),&Physics2DServer::body_remove_shape);
+ ObjectTypeDB::bind_method(_MD("body_clear_shapes","body"),&Physics2DServer::body_clear_shapes);
+
+
+ ObjectTypeDB::bind_method(_MD("body_set_shape_as_trigger","body","shape_idx","enable"),&Physics2DServer::body_set_shape_as_trigger);
+ ObjectTypeDB::bind_method(_MD("body_is_shape_set_as_trigger","body","shape_idx"),&Physics2DServer::body_is_shape_set_as_trigger);
+
+ ObjectTypeDB::bind_method(_MD("body_attach_object_instance_ID","body","id"),&Physics2DServer::body_attach_object_instance_ID);
+ ObjectTypeDB::bind_method(_MD("body_get_object_instance_ID","body"),&Physics2DServer::body_get_object_instance_ID);
+
+
+ ObjectTypeDB::bind_method(_MD("body_set_enable_continuous_collision_detection","body","enable"),&Physics2DServer::body_set_enable_continuous_collision_detection);
+ ObjectTypeDB::bind_method(_MD("body_is_continuous_collision_detection_enabled","body"),&Physics2DServer::body_is_continuous_collision_detection_enabled);
+
+
+ //ObjectTypeDB::bind_method(_MD("body_set_user_flags","flags""),&Physics2DServer::body_set_shape,DEFVAL(Matrix32));
+ //ObjectTypeDB::bind_method(_MD("body_get_user_flags","body","shape_idx","shape"),&Physics2DServer::body_get_shape);
+
+ ObjectTypeDB::bind_method(_MD("body_set_param","body","param","value"),&Physics2DServer::body_set_param);
+ ObjectTypeDB::bind_method(_MD("body_get_param","body","param"),&Physics2DServer::body_get_param);
+
+ ObjectTypeDB::bind_method(_MD("body_static_simulate_motion","body","new_xform"),&Physics2DServer::body_static_simulate_motion);
+
+ ObjectTypeDB::bind_method(_MD("body_set_state","body","state","value"),&Physics2DServer::body_set_state);
+ ObjectTypeDB::bind_method(_MD("body_get_state","body","state"),&Physics2DServer::body_get_state);
+
+ ObjectTypeDB::bind_method(_MD("body_apply_impulse","body","pos","impulse"),&Physics2DServer::body_apply_impulse);
+ ObjectTypeDB::bind_method(_MD("body_set_axis_velocity","body","axis_velocity"),&Physics2DServer::body_set_axis_velocity);
+
+ ObjectTypeDB::bind_method(_MD("body_add_collision_exception","body","excepted_body"),&Physics2DServer::body_add_collision_exception);
+ ObjectTypeDB::bind_method(_MD("body_remove_collision_exception","body","excepted_body"),&Physics2DServer::body_remove_collision_exception);
+// virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions)=0;
+
+ ObjectTypeDB::bind_method(_MD("body_set_max_contacts_reported","body","amount"),&Physics2DServer::body_set_max_contacts_reported);
+ ObjectTypeDB::bind_method(_MD("body_get_max_contacts_reported","body"),&Physics2DServer::body_get_max_contacts_reported);
+
+ ObjectTypeDB::bind_method(_MD("body_set_omit_force_integration","body","enable"),&Physics2DServer::body_set_omit_force_integration);
+ ObjectTypeDB::bind_method(_MD("body_is_omitting_force_integration","body"),&Physics2DServer::body_is_omitting_force_integration);
+
+ ObjectTypeDB::bind_method(_MD("body_set_force_integration_callback","body","receiver","method"),&Physics2DServer::body_set_force_integration_callback);
+
+ /* JOINT API */
+
+ ObjectTypeDB::bind_method(_MD("joint_set_param","joint","param","value"),&Physics2DServer::joint_set_param);
+ ObjectTypeDB::bind_method(_MD("joint_get_param","joint","param"),&Physics2DServer::joint_get_param);
+
+ ObjectTypeDB::bind_method(_MD("pin_joint_create","anchor","body_a","body_b"),&Physics2DServer::pin_joint_create,DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("groove_joint_create","groove1_a","groove2_a","anchor_b","body_a","body_b"),&Physics2DServer::groove_joint_create,DEFVAL(RID()),DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("damped_spring_joint_create","anchor_a","anchor_b","body_a","body_b"),&Physics2DServer::damped_spring_joint_create,DEFVAL(RID()));
+
+ ObjectTypeDB::bind_method(_MD("damped_string_joint_set_param","joint","param","value"),&Physics2DServer::damped_string_joint_set_param,DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("damped_string_joint_get_param","joint","param"),&Physics2DServer::damped_string_joint_get_param);
+
+ ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&Physics2DServer::joint_get_type);
+
+ ObjectTypeDB::bind_method(_MD("free","rid"),&Physics2DServer::free);
+
+ ObjectTypeDB::bind_method(_MD("set_active","active"),&Physics2DServer::set_active);
+
+// ObjectTypeDB::bind_method(_MD("init"),&Physics2DServer::init);
+// ObjectTypeDB::bind_method(_MD("step"),&Physics2DServer::step);
+// ObjectTypeDB::bind_method(_MD("sync"),&Physics2DServer::sync);
+ //ObjectTypeDB::bind_method(_MD("flush_queries"),&Physics2DServer::flush_queries);
+
+ BIND_CONSTANT( SHAPE_LINE );
+ BIND_CONSTANT( SHAPE_SEGMENT );
+ BIND_CONSTANT( SHAPE_CIRCLE );
+ BIND_CONSTANT( SHAPE_RECTANGLE );
+ BIND_CONSTANT( SHAPE_CAPSULE );
+ BIND_CONSTANT( SHAPE_CONVEX_POLYGON );
+ BIND_CONSTANT( SHAPE_CONCAVE_POLYGON );
+ BIND_CONSTANT( SHAPE_CUSTOM );
+
+ BIND_CONSTANT( AREA_PARAM_GRAVITY );
+ BIND_CONSTANT( AREA_PARAM_GRAVITY_VECTOR );
+ BIND_CONSTANT( AREA_PARAM_GRAVITY_IS_POINT );
+ BIND_CONSTANT( AREA_PARAM_GRAVITY_POINT_ATTENUATION );
+ BIND_CONSTANT( AREA_PARAM_DENSITY );
+ BIND_CONSTANT( AREA_PARAM_PRIORITY );
+
+ BIND_CONSTANT( AREA_SPACE_OVERRIDE_COMBINE );
+ BIND_CONSTANT( AREA_SPACE_OVERRIDE_DISABLED );
+ BIND_CONSTANT( AREA_SPACE_OVERRIDE_REPLACE );
+
+ BIND_CONSTANT( BODY_MODE_STATIC );
+ BIND_CONSTANT( BODY_MODE_STATIC_ACTIVE );
+ BIND_CONSTANT( BODY_MODE_RIGID );
+ BIND_CONSTANT( BODY_MODE_CHARACTER );
+
+ BIND_CONSTANT( BODY_PARAM_BOUNCE );
+ BIND_CONSTANT( BODY_PARAM_FRICTION );
+ BIND_CONSTANT( BODY_PARAM_MASS );
+ BIND_CONSTANT( BODY_PARAM_MAX );
+
+ BIND_CONSTANT( BODY_STATE_TRANSFORM );
+ BIND_CONSTANT( BODY_STATE_LINEAR_VELOCITY );
+ BIND_CONSTANT( BODY_STATE_ANGULAR_VELOCITY );
+ BIND_CONSTANT( BODY_STATE_SLEEPING );
+ BIND_CONSTANT( BODY_STATE_CAN_SLEEP );
+
+ BIND_CONSTANT( JOINT_PIN );
+ BIND_CONSTANT( JOINT_GROOVE );
+ BIND_CONSTANT( JOINT_DAMPED_SPRING );
+
+ BIND_CONSTANT( DAMPED_STRING_REST_LENGTH );
+ BIND_CONSTANT( DAMPED_STRING_STIFFNESS );
+ BIND_CONSTANT( DAMPED_STRING_DAMPING );
+
+// BIND_CONSTANT( TYPE_BODY );
+// BIND_CONSTANT( TYPE_AREA );
+
+ BIND_CONSTANT( AREA_BODY_ADDED );
+ BIND_CONSTANT( AREA_BODY_REMOVED );
+
+
+}
+
+
+Physics2DServer::Physics2DServer() {
+
+ ERR_FAIL_COND( singleton!=NULL );
+ singleton=this;
+}
+
+Physics2DServer::~Physics2DServer() {
+
+ singleton=NULL;
+}
+
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
new file mode 100644
index 0000000000..8ace6282fd
--- /dev/null
+++ b/servers/physics_2d_server.h
@@ -0,0 +1,426 @@
+/*************************************************************************/
+/* physics_2d_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef PHYSICS_2D_SERVER_H
+#define PHYSICS_2D_SERVER_H
+
+#include "object.h"
+#include "reference.h"
+
+class Physics2DDirectSpaceState;
+
+class Physics2DDirectBodyState : public Object {
+
+ OBJ_TYPE( Physics2DDirectBodyState, Object );
+protected:
+ static void _bind_methods();
+public:
+
+ virtual Vector2 get_total_gravity() const=0; // get gravity vector working on this body space/area
+ virtual float get_total_density() const=0; // get density of this body space/area
+
+ virtual float get_inverse_mass() const=0; // get the mass
+ virtual real_t get_inverse_inertia() const=0; // get density of this body space
+
+ virtual void set_linear_velocity(const Vector2& p_velocity)=0;
+ virtual Vector2 get_linear_velocity() const=0;
+
+ virtual void set_angular_velocity(real_t p_velocity)=0;
+ virtual real_t get_angular_velocity() const=0;
+
+ virtual void set_transform(const Matrix32& p_transform)=0;
+ virtual Matrix32 get_transform() const=0;
+
+ virtual void set_sleep_state(bool p_enable)=0;
+ virtual bool is_sleeping() const=0;
+
+ virtual int get_contact_count() const=0;
+
+ virtual Vector2 get_contact_local_pos(int p_contact_idx) const=0;
+ virtual Vector2 get_contact_local_normal(int p_contact_idx) const=0;
+ virtual int get_contact_local_shape(int p_contact_idx) const=0;
+
+ virtual RID get_contact_collider(int p_contact_idx) const=0;
+ virtual Vector2 get_contact_collider_pos(int p_contact_idx) const=0;
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const=0;
+ virtual Object* get_contact_collider_object(int p_contact_idx) const;
+ virtual int get_contact_collider_shape(int p_contact_idx) const=0;
+ virtual Vector2 get_contact_collider_velocity_at_pos(int p_contact_idx) const=0;
+
+ virtual real_t get_step() const=0;
+ virtual void integrate_forces();
+
+ virtual Physics2DDirectSpaceState* get_space_state()=0;
+
+ Physics2DDirectBodyState();
+};
+
+
+class Physics2DShapeQueryResult;
+
+
+class Physics2DDirectSpaceState : public Object {
+
+ OBJ_TYPE( Physics2DDirectSpaceState, Object );
+
+ Variant _intersect_ray(const Vector2& p_from, const Vector2& p_to,const Vector<RID>& p_exclude=Vector<RID>(),uint32_t p_user_mask=0);
+ Variant _intersect_shape(const RID& p_shape, const Matrix32& p_xform,int p_result_max=64,const Vector<RID>& p_exclude=Vector<RID>(),uint32_t p_user_mask=0);
+
+
+protected:
+ static void _bind_methods();
+
+public:
+
+ struct RayResult {
+
+ Vector2 position;
+ Vector2 normal;
+ RID rid;
+ ObjectID collider_id;
+ Object *collider;
+ int shape;
+ };
+
+ virtual bool intersect_ray(const Vector2& p_from, const Vector2& p_to,RayResult &r_result,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0)=0;
+
+ struct ShapeResult {
+
+ RID rid;
+ ObjectID collider_id;
+ Object *collider;
+ int shape;
+
+ };
+
+ virtual int intersect_shape(const RID& p_shape, const Matrix32& p_xform,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0)=0;
+
+ Physics2DDirectSpaceState();
+};
+
+
+class Physics2DShapeQueryResult : public Reference {
+
+ OBJ_TYPE( Physics2DShapeQueryResult, Reference );
+
+ Vector<Physics2DDirectSpaceState::ShapeResult> result;
+
+friend class Physics2DDirectSpaceState;
+
+protected:
+ static void _bind_methods();
+public:
+
+ int get_result_count() const;
+ RID get_result_rid(int p_idx) const;
+ ObjectID get_result_object_id(int p_idx) const;
+ Object* get_result_object(int p_idx) const;
+ int get_result_object_shape(int p_idx) const;
+
+ Physics2DShapeQueryResult();
+};
+
+
+class Physics2DServer : public Object {
+
+ OBJ_TYPE( Physics2DServer, Object );
+
+ static Physics2DServer * singleton;
+
+protected:
+ static void _bind_methods();
+
+public:
+
+ static Physics2DServer * get_singleton();
+
+ enum ShapeType {
+ SHAPE_LINE, ///< plane:"plane"
+ SHAPE_RAY, ///< float:"length"
+ SHAPE_SEGMENT, ///< float:"length"
+ SHAPE_CIRCLE, ///< float:"radius"
+ SHAPE_RECTANGLE, ///< vec3:"extents"
+ SHAPE_CAPSULE,
+ SHAPE_CONVEX_POLYGON, ///< array of planes:"planes"
+ SHAPE_CONCAVE_POLYGON, ///< Vector2 array:"triangles" , or Dictionary with "indices" (int array) and "triangles" (Vector2 array)
+ SHAPE_CUSTOM, ///< Server-Implementation based custom shape, calling shape_create() with this value will result in an error
+ };
+
+ virtual RID shape_create(ShapeType p_shape)=0;
+ virtual void shape_set_data(RID p_shape, const Variant& p_data)=0;
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias)=0;
+
+ virtual ShapeType shape_get_type(RID p_shape) const=0;
+ virtual Variant shape_get_data(RID p_shape) const=0;
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const=0;
+
+
+ /* SPACE API */
+
+ virtual RID space_create()=0;
+ virtual void space_set_active(RID p_space,bool p_active)=0;
+ virtual bool space_is_active(RID p_space) const=0;
+
+ enum SpaceParameter {
+
+ SPACE_PARAM_CONTACT_RECYCLE_RADIUS,
+ SPACE_PARAM_CONTACT_MAX_SEPARATION,
+ SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION,
+ SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD,
+ SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD,
+ SPACE_PARAM_BODY_TIME_TO_SLEEP,
+ SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO,
+ SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS,
+ };
+
+ virtual void space_set_param(RID p_space,SpaceParameter p_param, real_t p_value)=0;
+ virtual real_t space_get_param(RID p_space,SpaceParameter p_param) const=0;
+
+ // this function only works on fixed process, errors and returns null otherwise
+ virtual Physics2DDirectSpaceState* space_get_direct_state(RID p_space)=0;
+
+
+ //missing space parameters
+
+ /* AREA API */
+
+ //missing attenuation? missing better override?
+
+
+
+ enum AreaParameter {
+ AREA_PARAM_GRAVITY,
+ AREA_PARAM_GRAVITY_VECTOR,
+ AREA_PARAM_GRAVITY_IS_POINT,
+ AREA_PARAM_GRAVITY_POINT_ATTENUATION,
+ AREA_PARAM_DENSITY,
+ AREA_PARAM_PRIORITY
+ };
+
+ virtual RID area_create()=0;
+
+ virtual void area_set_space(RID p_area, RID p_space)=0;
+ virtual RID area_get_space(RID p_area) const=0;
+
+
+ enum AreaSpaceOverrideMode {
+ AREA_SPACE_OVERRIDE_DISABLED,
+ AREA_SPACE_OVERRIDE_COMBINE,
+ AREA_SPACE_OVERRIDE_REPLACE,
+ };
+
+ virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode)=0;
+ virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const=0;
+
+ virtual void area_add_shape(RID p_area, RID p_shape, const Matrix32& p_transform=Matrix32())=0;
+ virtual void area_set_shape(RID p_area, int p_shape_idx,RID p_shape)=0;
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Matrix32& p_transform)=0;
+
+ virtual int area_get_shape_count(RID p_area) const=0;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const=0;
+ virtual Matrix32 area_get_shape_transform(RID p_area, int p_shape_idx) const=0;
+
+ virtual void area_remove_shape(RID p_area, int p_shape_idx)=0;
+ virtual void area_clear_shapes(RID p_area)=0;
+
+ virtual void area_attach_object_instance_ID(RID p_area,ObjectID p_ID)=0;
+ virtual ObjectID area_get_object_instance_ID(RID p_area) const=0;
+
+ virtual void area_set_param(RID p_area,AreaParameter p_param,const Variant& p_value)=0;
+ virtual void area_set_transform(RID p_area, const Matrix32& p_transform)=0;
+
+ virtual Variant area_get_param(RID p_parea,AreaParameter p_param) const=0;
+ virtual Matrix32 area_get_transform(RID p_area) const=0;
+
+ virtual void area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method)=0;
+
+ /* BODY API */
+
+ //missing ccd?
+
+ enum BodyMode {
+ BODY_MODE_STATIC,
+ BODY_MODE_STATIC_ACTIVE,
+ BODY_MODE_RIGID,
+ //BODY_MODE_SOFT
+ BODY_MODE_CHARACTER
+ };
+
+ virtual RID body_create(BodyMode p_mode=BODY_MODE_RIGID,bool p_init_sleeping=false)=0;
+
+ virtual void body_set_space(RID p_body, RID p_space)=0;
+ virtual RID body_get_space(RID p_body) const=0;
+
+ virtual void body_set_mode(RID p_body, BodyMode p_mode)=0;
+ virtual BodyMode body_get_mode(RID p_body, BodyMode p_mode) const=0;
+
+ virtual void body_add_shape(RID p_body, RID p_shape, const Matrix32& p_transform=Matrix32())=0;
+ virtual void body_set_shape(RID p_body, int p_shape_idx,RID p_shape)=0;
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Matrix32& p_transform)=0;
+
+ virtual int body_get_shape_count(RID p_body) const=0;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const=0;
+ virtual Matrix32 body_get_shape_transform(RID p_body, int p_shape_idx) const=0;
+
+ virtual void body_set_shape_as_trigger(RID p_body, int p_shape_idx,bool p_enable)=0;
+ virtual bool body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const=0;
+
+ virtual void body_remove_shape(RID p_body, int p_shape_idx)=0;
+ virtual void body_clear_shapes(RID p_body)=0;
+
+ virtual void body_attach_object_instance_ID(RID p_body,uint32_t p_ID)=0;
+ virtual uint32_t body_get_object_instance_ID(RID p_body) const=0;
+
+ virtual void body_set_enable_continuous_collision_detection(RID p_body,bool p_enable)=0;
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const=0;
+
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags)=0;
+ virtual uint32_t body_get_user_flags(RID p_body, uint32_t p_flags) const=0;
+
+ // common body variables
+ enum BodyParameter {
+ BODY_PARAM_BOUNCE,
+ BODY_PARAM_FRICTION,
+ BODY_PARAM_MASS, ///< unused for static, always infinite
+ BODY_PARAM_MAX,
+ };
+
+ virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value)=0;
+ virtual float body_get_param(RID p_body, BodyParameter p_param) const=0;
+
+ //advanced simulation
+ virtual void body_static_simulate_motion(RID p_body,const Matrix32& p_new_transform)=0;
+
+ //state
+ enum BodyState {
+ BODY_STATE_TRANSFORM,
+ BODY_STATE_LINEAR_VELOCITY,
+ BODY_STATE_ANGULAR_VELOCITY,
+ BODY_STATE_SLEEPING,
+ BODY_STATE_CAN_SLEEP
+ };
+
+ virtual void body_set_state(RID p_body, BodyState p_state, const Variant& p_variant)=0;
+ virtual Variant body_get_state(RID p_body, BodyState p_state) const=0;
+
+ //do something about it
+ virtual void body_set_applied_force(RID p_body, const Vector2& p_force)=0;
+ virtual Vector2 body_get_applied_force(RID p_body) const=0;
+
+ virtual void body_set_applied_torque(RID p_body, float p_torque)=0;
+ virtual float body_get_applied_torque(RID p_body) const=0;
+
+ virtual void body_apply_impulse(RID p_body, const Vector2& p_pos, const Vector2& p_impulse)=0;
+ virtual void body_set_axis_velocity(RID p_body, const Vector2& p_axis_velocity)=0;
+
+ //fix
+ virtual void body_add_collision_exception(RID p_body, RID p_body_b)=0;
+ virtual void body_remove_collision_exception(RID p_body, RID p_body_b)=0;
+ virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions)=0;
+
+ virtual void body_set_max_contacts_reported(RID p_body, int p_contacts)=0;
+ virtual int body_get_max_contacts_reported(RID p_body) const=0;
+
+ //missing remove
+ virtual void body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold)=0;
+ virtual float body_get_contacts_reported_depth_treshold(RID p_body) const=0;
+
+ virtual void body_set_omit_force_integration(RID p_body,bool p_omit)=0;
+ virtual bool body_is_omitting_force_integration(RID p_body) const=0;
+
+ virtual void body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata=Variant())=0;
+
+ /* JOINT API */
+
+ enum JointType {
+
+ JOINT_PIN,
+ JOINT_GROOVE,
+ JOINT_DAMPED_SPRING
+ };
+
+ enum JointParam {
+ JOINT_PARAM_BIAS,
+ JOINT_PARAM_MAX_BIAS,
+ JOINT_PARAM_MAX_FORCE,
+ };
+
+ virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value)=0;
+ virtual real_t joint_get_param(RID p_joint,JointParam p_param) const=0;
+
+ virtual RID pin_joint_create(const Vector2& p_anchor,RID p_body_a,RID p_body_b=RID())=0;
+ virtual RID groove_joint_create(const Vector2& p_a_groove1,const Vector2& p_a_groove2, const Vector2& p_b_anchor, RID p_body_a,RID p_body_b)=0;
+ virtual RID damped_spring_joint_create(const Vector2& p_anchor_a,const Vector2& p_anchor_b,RID p_body_a,RID p_body_b=RID())=0;
+
+ enum DampedStringParam {
+ DAMPED_STRING_REST_LENGTH,
+ DAMPED_STRING_STIFFNESS,
+ DAMPED_STRING_DAMPING
+ };
+ virtual void damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value)=0;
+ virtual real_t damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const=0;
+
+ virtual JointType joint_get_type(RID p_joint) const=0;
+
+ /* QUERY API */
+
+ enum AreaBodyStatus {
+ AREA_BODY_ADDED,
+ AREA_BODY_REMOVED
+ };
+
+
+ /* MISC */
+
+ virtual void free(RID p_rid)=0;
+
+ virtual void set_active(bool p_active)=0;
+ virtual void init()=0;
+ virtual void step(float p_step)=0;
+ virtual void sync()=0;
+ virtual void flush_queries()=0;
+ virtual void finish()=0;
+
+ Physics2DServer();
+ ~Physics2DServer();
+};
+
+VARIANT_ENUM_CAST( Physics2DServer::ShapeType );
+VARIANT_ENUM_CAST( Physics2DServer::SpaceParameter );
+VARIANT_ENUM_CAST( Physics2DServer::AreaParameter );
+VARIANT_ENUM_CAST( Physics2DServer::AreaSpaceOverrideMode );
+VARIANT_ENUM_CAST( Physics2DServer::BodyMode );
+VARIANT_ENUM_CAST( Physics2DServer::BodyParameter );
+VARIANT_ENUM_CAST( Physics2DServer::BodyState );
+VARIANT_ENUM_CAST( Physics2DServer::JointParam );
+VARIANT_ENUM_CAST( Physics2DServer::JointType );
+VARIANT_ENUM_CAST( Physics2DServer::DampedStringParam );
+//VARIANT_ENUM_CAST( Physics2DServer::ObjectType );
+VARIANT_ENUM_CAST( Physics2DServer::AreaBodyStatus );
+
+#endif
diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp
new file mode 100644
index 0000000000..69a2adae77
--- /dev/null
+++ b/servers/physics_server.cpp
@@ -0,0 +1,420 @@
+/*************************************************************************/
+/* physics_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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.h"
+#include "print_string.h"
+PhysicsServer * PhysicsServer::singleton=NULL;
+
+
+void PhysicsDirectBodyState::integrate_forces() {
+
+ real_t step = get_step();
+ Vector3 lv = get_linear_velocity();
+ lv+=get_total_gravity() * step;
+
+ Vector3 av = get_angular_velocity();
+
+ float damp = 1.0 - step * get_total_density();
+
+ if (damp<0) // reached zero in the given time
+ damp=0;
+
+ lv*=damp;
+ av*=damp;
+
+ set_linear_velocity(lv);
+ set_angular_velocity(av);
+
+
+
+
+}
+
+Object* PhysicsDirectBodyState::get_contact_collider_object(int p_contact_idx) const {
+
+ ObjectID objid = get_contact_collider_id(p_contact_idx);
+ Object *obj = ObjectDB::get_instance( objid );
+ return obj;
+}
+
+PhysicsServer * PhysicsServer::get_singleton() {
+
+ return singleton;
+}
+
+void PhysicsDirectBodyState::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("get_total_gravity"),&PhysicsDirectBodyState::get_total_gravity);
+ ObjectTypeDB::bind_method(_MD("get_total_density"),&PhysicsDirectBodyState::get_total_density);
+
+ ObjectTypeDB::bind_method(_MD("get_inverse_mass"),&PhysicsDirectBodyState::get_inverse_mass);
+ ObjectTypeDB::bind_method(_MD("get_inverse_inertia"),&PhysicsDirectBodyState::get_inverse_inertia);
+
+ ObjectTypeDB::bind_method(_MD("set_linear_velocity","velocity"),&PhysicsDirectBodyState::set_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&PhysicsDirectBodyState::get_linear_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_angular_velocity","velocity"),&PhysicsDirectBodyState::set_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("get_angular_velocity"),&PhysicsDirectBodyState::get_angular_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_transform","transform"),&PhysicsDirectBodyState::set_transform);
+ ObjectTypeDB::bind_method(_MD("get_transform"),&PhysicsDirectBodyState::get_transform);
+
+ ObjectTypeDB::bind_method(_MD("add_force","force","pos"),&PhysicsDirectBodyState::add_force);
+
+ ObjectTypeDB::bind_method(_MD("set_sleep_state","enabled"),&PhysicsDirectBodyState::set_sleep_state);
+ ObjectTypeDB::bind_method(_MD("is_sleeping"),&PhysicsDirectBodyState::is_sleeping);
+
+ ObjectTypeDB::bind_method(_MD("get_contact_count"),&PhysicsDirectBodyState::get_contact_count);
+
+ ObjectTypeDB::bind_method(_MD("get_contact_local_pos","contact_idx"),&PhysicsDirectBodyState::get_contact_local_pos);
+ ObjectTypeDB::bind_method(_MD("get_contact_local_normal","contact_idx"),&PhysicsDirectBodyState::get_contact_local_normal);
+ ObjectTypeDB::bind_method(_MD("get_contact_local_shape","contact_idx"),&PhysicsDirectBodyState::get_contact_local_shape);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider","contact_idx"),&PhysicsDirectBodyState::get_contact_collider);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_pos","contact_idx"),&PhysicsDirectBodyState::get_contact_collider_pos);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_id","contact_idx"),&PhysicsDirectBodyState::get_contact_collider_id);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_object","contact_idx"),&PhysicsDirectBodyState::get_contact_collider_object);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_shape","contact_idx"),&PhysicsDirectBodyState::get_contact_collider_shape);
+ ObjectTypeDB::bind_method(_MD("get_contact_collider_velocity_at_pos","contact_idx"),&PhysicsDirectBodyState::get_contact_collider_velocity_at_pos);
+ ObjectTypeDB::bind_method(_MD("get_step"),&PhysicsDirectBodyState::get_step);
+ ObjectTypeDB::bind_method(_MD("integrate_forces"),&PhysicsDirectBodyState::integrate_forces);
+ ObjectTypeDB::bind_method(_MD("get_space_state:PhysicsDirectSpaceState"),&PhysicsDirectBodyState::get_space_state);
+
+}
+
+PhysicsDirectBodyState::PhysicsDirectBodyState() {}
+
+///////////////////////////////////////////////////////
+
+
+
+Variant PhysicsDirectSpaceState::_intersect_ray(const Vector3& p_from, const Vector3& p_to,const Vector<RID>& p_exclude,uint32_t p_user_mask) {
+
+ RayResult inters;
+ Set<RID> exclude;
+ for(int i=0;i<p_exclude.size();i++)
+ exclude.insert(p_exclude[i]);
+
+ bool res = intersect_ray(p_from,p_to,inters,exclude,p_user_mask);
+
+ if (!res)
+ return Variant();
+
+ Dictionary d;
+ d["position"]=inters.position;
+ d["normal"]=inters.normal;
+ d["collider_id"]=inters.collider_id;
+ d["collider"]=inters.collider;
+ d["shape"]=inters.shape;
+ d["rid"]=inters.rid;
+
+ return d;
+}
+
+Variant PhysicsDirectSpaceState::_intersect_shape(const RID& p_shape, const Transform& p_xform,int p_result_max,const Vector<RID>& p_exclude,uint32_t p_user_mask) {
+
+
+
+ ERR_FAIL_INDEX_V(p_result_max,4096,Variant());
+ if (p_result_max<=0)
+ return Variant();
+
+ Set<RID> exclude;
+ for(int i=0;i<p_exclude.size();i++)
+ exclude.insert(p_exclude[i]);
+
+ ShapeResult *res=(ShapeResult*)alloca(p_result_max*sizeof(ShapeResult));
+
+ int rc = intersect_shape(p_shape,p_xform,res,p_result_max,exclude,p_user_mask);
+
+ if (rc==0)
+ return Variant();
+
+ Ref<PhysicsShapeQueryResult> result = memnew( PhysicsShapeQueryResult );
+ result->result.resize(rc);
+ for(int i=0;i<rc;i++)
+ result->result[i]=res[i];
+
+ return result;
+
+}
+
+
+
+
+
+PhysicsDirectSpaceState::PhysicsDirectSpaceState() {
+
+
+
+}
+
+
+void PhysicsDirectSpaceState::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("intersect_ray","from","to","exclude","umask"),&PhysicsDirectSpaceState::_intersect_ray,DEFVAL(Array()),DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("intersect_shape:PhysicsShapeQueryResult","shape","xform","result_max","exclude","umask"),&PhysicsDirectSpaceState::_intersect_shape,DEFVAL(Array()),DEFVAL(0));
+
+}
+
+
+int PhysicsShapeQueryResult::get_result_count() const {
+
+ return result.size();
+}
+RID PhysicsShapeQueryResult::get_result_rid(int p_idx) const {
+
+ return result[p_idx].rid;
+}
+ObjectID PhysicsShapeQueryResult::get_result_object_id(int p_idx) const {
+
+ return result[p_idx].collider_id;
+}
+Object* PhysicsShapeQueryResult::get_result_object(int p_idx) const {
+
+ return result[p_idx].collider;
+}
+int PhysicsShapeQueryResult::get_result_object_shape(int p_idx) const {
+
+ return result[p_idx].shape;
+}
+
+PhysicsShapeQueryResult::PhysicsShapeQueryResult() {
+
+
+}
+
+void PhysicsShapeQueryResult::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("get_result_count"),&PhysicsShapeQueryResult::get_result_count);
+ ObjectTypeDB::bind_method(_MD("get_result_rid","idx"),&PhysicsShapeQueryResult::get_result_rid);
+ ObjectTypeDB::bind_method(_MD("get_result_object_id","idx"),&PhysicsShapeQueryResult::get_result_object_id);
+ ObjectTypeDB::bind_method(_MD("get_result_object","idx"),&PhysicsShapeQueryResult::get_result_object);
+ ObjectTypeDB::bind_method(_MD("get_result_object_shape","idx"),&PhysicsShapeQueryResult::get_result_object_shape);
+
+
+}
+
+
+
+
+
+
+///////////////////////////////////////
+
+void PhysicsServer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("shape_create","type"),&PhysicsServer::shape_create);
+ ObjectTypeDB::bind_method(_MD("shape_set_data","shape","data"),&PhysicsServer::shape_set_data);
+
+ ObjectTypeDB::bind_method(_MD("shape_get_type","shape"),&PhysicsServer::shape_get_type);
+ ObjectTypeDB::bind_method(_MD("shape_get_data","shape"),&PhysicsServer::shape_get_data);
+
+
+ ObjectTypeDB::bind_method(_MD("space_create"),&PhysicsServer::space_create);
+ ObjectTypeDB::bind_method(_MD("space_set_active","space","active"),&PhysicsServer::space_set_active);
+ ObjectTypeDB::bind_method(_MD("space_is_active","space"),&PhysicsServer::space_is_active);
+ ObjectTypeDB::bind_method(_MD("space_set_param","space","param","value"),&PhysicsServer::space_set_param);
+ ObjectTypeDB::bind_method(_MD("space_get_param","space","param"),&PhysicsServer::space_get_param);
+ ObjectTypeDB::bind_method(_MD("space_get_direct_state:PhysicsDirectSpaceState","space"),&PhysicsServer::space_get_direct_state);
+
+ ObjectTypeDB::bind_method(_MD("area_create"),&PhysicsServer::area_create);
+ ObjectTypeDB::bind_method(_MD("area_set_space","area","space"),&PhysicsServer::area_set_space);
+ ObjectTypeDB::bind_method(_MD("area_get_space","area"),&PhysicsServer::area_get_space);
+
+ ObjectTypeDB::bind_method(_MD("area_set_space_override_mode","area","mode"),&PhysicsServer::area_set_space_override_mode);
+ ObjectTypeDB::bind_method(_MD("area_get_space_override_mode","area"),&PhysicsServer::area_get_space_override_mode);
+
+ ObjectTypeDB::bind_method(_MD("area_add_shape","area","shape","transform"),&PhysicsServer::area_set_shape,DEFVAL(Transform()));
+ ObjectTypeDB::bind_method(_MD("area_set_shape","area","shape_idx","shape"),&PhysicsServer::area_get_shape);
+ ObjectTypeDB::bind_method(_MD("area_set_shape_transform","area","shape_idx","transform"),&PhysicsServer::area_set_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_get_shape_count","area"),&PhysicsServer::area_get_shape_count);
+ ObjectTypeDB::bind_method(_MD("area_get_shape","area","shape_idx"),&PhysicsServer::area_get_shape);
+ ObjectTypeDB::bind_method(_MD("area_get_shape_transform","area","shape_idx"),&PhysicsServer::area_get_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_remove_shape","area","shape_idx"),&PhysicsServer::area_remove_shape);
+ ObjectTypeDB::bind_method(_MD("area_clear_shapes","area"),&PhysicsServer::area_clear_shapes);
+
+
+ ObjectTypeDB::bind_method(_MD("area_set_param","area","param","value"),&PhysicsServer::area_get_param);
+ ObjectTypeDB::bind_method(_MD("area_set_transform","area","transform"),&PhysicsServer::area_get_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_get_param","area","param"),&PhysicsServer::area_get_param);
+ ObjectTypeDB::bind_method(_MD("area_get_transform","area"),&PhysicsServer::area_get_transform);
+
+ ObjectTypeDB::bind_method(_MD("area_attach_object_instance_ID","area","id"),&PhysicsServer::area_attach_object_instance_ID);
+ ObjectTypeDB::bind_method(_MD("area_get_object_instance_ID","area"),&PhysicsServer::area_get_object_instance_ID);
+
+ ObjectTypeDB::bind_method(_MD("area_set_monitor_callback","receiver","method"),&PhysicsServer::area_set_monitor_callback);
+
+ ObjectTypeDB::bind_method(_MD("body_create","mode","init_sleeping"),&PhysicsServer::body_create,DEFVAL(BODY_MODE_RIGID),DEFVAL(false));
+
+ ObjectTypeDB::bind_method(_MD("body_set_space","body","space"),&PhysicsServer::body_set_space);
+ ObjectTypeDB::bind_method(_MD("body_get_space","body"),&PhysicsServer::body_get_space);
+
+ ObjectTypeDB::bind_method(_MD("body_set_mode","body","mode"),&PhysicsServer::body_set_mode);
+ ObjectTypeDB::bind_method(_MD("body_get_mode","body"),&PhysicsServer::body_get_mode);
+
+ ObjectTypeDB::bind_method(_MD("body_add_shape","body","shape","transform"),&PhysicsServer::body_add_shape,DEFVAL(Transform()));
+ ObjectTypeDB::bind_method(_MD("body_set_shape","body","shape_idx","shape"),&PhysicsServer::body_set_shape);
+ ObjectTypeDB::bind_method(_MD("body_set_shape_transform","body","shape_idx","transform"),&PhysicsServer::body_set_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("body_get_shape_count","body"),&PhysicsServer::body_get_shape_count);
+ ObjectTypeDB::bind_method(_MD("body_get_shape","body","shape_idx"),&PhysicsServer::body_get_shape);
+ ObjectTypeDB::bind_method(_MD("body_get_shape_transform","body","shape_idx"),&PhysicsServer::body_get_shape_transform);
+
+ ObjectTypeDB::bind_method(_MD("body_remove_shape","body","shape_idx"),&PhysicsServer::body_remove_shape);
+ ObjectTypeDB::bind_method(_MD("body_clear_shapes","body"),&PhysicsServer::body_clear_shapes);
+
+ ObjectTypeDB::bind_method(_MD("body_attach_object_instance_ID","body","id"),&PhysicsServer::body_attach_object_instance_ID);
+ ObjectTypeDB::bind_method(_MD("body_get_object_instance_ID","body"),&PhysicsServer::body_get_object_instance_ID);
+
+
+ ObjectTypeDB::bind_method(_MD("body_set_enable_continuous_collision_detection","body","enable"),&PhysicsServer::body_set_enable_continuous_collision_detection);
+ ObjectTypeDB::bind_method(_MD("body_is_continuous_collision_detection_enabled","body"),&PhysicsServer::body_is_continuous_collision_detection_enabled);
+
+
+ //ObjectTypeDB::bind_method(_MD("body_set_user_flags","flags""),&PhysicsServer::body_set_shape,DEFVAL(Transform));
+ //ObjectTypeDB::bind_method(_MD("body_get_user_flags","body","shape_idx","shape"),&PhysicsServer::body_get_shape);
+
+ ObjectTypeDB::bind_method(_MD("body_set_param","body","param","value"),&PhysicsServer::body_set_param);
+ ObjectTypeDB::bind_method(_MD("body_get_param","body","param"),&PhysicsServer::body_get_param);
+
+ ObjectTypeDB::bind_method(_MD("body_static_simulate_motion","body","new_xform"),&PhysicsServer::body_static_simulate_motion);
+
+ ObjectTypeDB::bind_method(_MD("body_set_state","body","state","value"),&PhysicsServer::body_set_state);
+ ObjectTypeDB::bind_method(_MD("body_get_state","body","state"),&PhysicsServer::body_get_state);
+
+ ObjectTypeDB::bind_method(_MD("body_apply_impulse","body","pos","impulse"),&PhysicsServer::body_apply_impulse);
+ ObjectTypeDB::bind_method(_MD("body_set_axis_velocity","body","axis_velocity"),&PhysicsServer::body_set_axis_velocity);
+
+ ObjectTypeDB::bind_method(_MD("body_add_collision_exception","body","excepted_body"),&PhysicsServer::body_add_collision_exception);
+ ObjectTypeDB::bind_method(_MD("body_remove_collision_exception","body","excepted_body"),&PhysicsServer::body_remove_collision_exception);
+// virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions)=0;
+
+ ObjectTypeDB::bind_method(_MD("body_set_max_contacts_reported","body","amount"),&PhysicsServer::body_set_max_contacts_reported);
+ ObjectTypeDB::bind_method(_MD("body_get_max_contacts_reported","body"),&PhysicsServer::body_get_max_contacts_reported);
+
+ ObjectTypeDB::bind_method(_MD("body_set_omit_force_integration","body","enable"),&PhysicsServer::body_set_omit_force_integration);
+ ObjectTypeDB::bind_method(_MD("body_is_omitting_force_integration","body"),&PhysicsServer::body_is_omitting_force_integration);
+
+ ObjectTypeDB::bind_method(_MD("body_set_force_integration_callback","body","receiver","method","userdata"),&PhysicsServer::body_set_force_integration_callback,DEFVAL(Variant()));
+
+ /* JOINT API */
+/*
+ ObjectTypeDB::bind_method(_MD("joint_set_param","joint","param","value"),&PhysicsServer::joint_set_param);
+ ObjectTypeDB::bind_method(_MD("joint_get_param","joint","param"),&PhysicsServer::joint_get_param);
+
+ ObjectTypeDB::bind_method(_MD("pin_joint_create","anchor","body_a","body_b"),&PhysicsServer::pin_joint_create,DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("groove_joint_create","groove1_a","groove2_a","anchor_b","body_a","body_b"),&PhysicsServer::groove_joint_create,DEFVAL(RID()),DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("damped_spring_joint_create","anchor_a","anchor_b","body_a","body_b"),&PhysicsServer::damped_spring_joint_create,DEFVAL(RID()));
+
+ ObjectTypeDB::bind_method(_MD("damped_string_joint_set_param","joint","param","value"),&PhysicsServer::damped_string_joint_set_param,DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("damped_string_joint_get_param","joint","param"),&PhysicsServer::damped_string_joint_get_param);
+
+ ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&PhysicsServer::joint_get_type);
+*/
+ ObjectTypeDB::bind_method(_MD("free","rid"),&PhysicsServer::free);
+
+ ObjectTypeDB::bind_method(_MD("set_active","active"),&PhysicsServer::set_active);
+
+// ObjectTypeDB::bind_method(_MD("init"),&PhysicsServer::init);
+// ObjectTypeDB::bind_method(_MD("step"),&PhysicsServer::step);
+// ObjectTypeDB::bind_method(_MD("sync"),&PhysicsServer::sync);
+ //ObjectTypeDB::bind_method(_MD("flush_queries"),&PhysicsServer::flush_queries);
+
+
+ BIND_CONSTANT( SHAPE_PLANE );
+ BIND_CONSTANT( SHAPE_RAY );
+ BIND_CONSTANT( SHAPE_SPHERE );
+ BIND_CONSTANT( SHAPE_BOX );
+ BIND_CONSTANT( SHAPE_CAPSULE );
+ BIND_CONSTANT( SHAPE_CONVEX_POLYGON );
+ BIND_CONSTANT( SHAPE_CONCAVE_POLYGON );
+ BIND_CONSTANT( SHAPE_HEIGHTMAP );
+ BIND_CONSTANT( SHAPE_CUSTOM );
+
+
+ BIND_CONSTANT( AREA_PARAM_GRAVITY );
+ BIND_CONSTANT( AREA_PARAM_GRAVITY_VECTOR );
+ BIND_CONSTANT( AREA_PARAM_GRAVITY_IS_POINT );
+ BIND_CONSTANT( AREA_PARAM_GRAVITY_POINT_ATTENUATION );
+ BIND_CONSTANT( AREA_PARAM_DENSITY );
+ BIND_CONSTANT( AREA_PARAM_PRIORITY );
+
+ BIND_CONSTANT( AREA_SPACE_OVERRIDE_COMBINE );
+ BIND_CONSTANT( AREA_SPACE_OVERRIDE_DISABLED );
+ BIND_CONSTANT( AREA_SPACE_OVERRIDE_REPLACE );
+
+ BIND_CONSTANT( BODY_MODE_STATIC );
+ BIND_CONSTANT( BODY_MODE_STATIC_ACTIVE );
+ BIND_CONSTANT( BODY_MODE_RIGID );
+ BIND_CONSTANT( BODY_MODE_CHARACTER );
+
+ BIND_CONSTANT( BODY_PARAM_BOUNCE );
+ BIND_CONSTANT( BODY_PARAM_FRICTION );
+ BIND_CONSTANT( BODY_PARAM_MASS );
+ BIND_CONSTANT( BODY_PARAM_MAX );
+
+ BIND_CONSTANT( BODY_STATE_TRANSFORM );
+ BIND_CONSTANT( BODY_STATE_LINEAR_VELOCITY );
+ BIND_CONSTANT( BODY_STATE_ANGULAR_VELOCITY );
+ BIND_CONSTANT( BODY_STATE_SLEEPING );
+ BIND_CONSTANT( BODY_STATE_CAN_SLEEP );
+/*
+ BIND_CONSTANT( JOINT_PIN );
+ BIND_CONSTANT( JOINT_GROOVE );
+ BIND_CONSTANT( JOINT_DAMPED_SPRING );
+
+ BIND_CONSTANT( DAMPED_STRING_REST_LENGTH );
+ BIND_CONSTANT( DAMPED_STRING_STIFFNESS );
+ BIND_CONSTANT( DAMPED_STRING_DAMPING );
+*/
+// BIND_CONSTANT( TYPE_BODY );
+// BIND_CONSTANT( TYPE_AREA );
+
+ BIND_CONSTANT( AREA_BODY_ADDED );
+ BIND_CONSTANT( AREA_BODY_REMOVED );
+
+
+}
+
+
+PhysicsServer::PhysicsServer() {
+
+ ERR_FAIL_COND( singleton!=NULL );
+ singleton=this;
+}
+
+PhysicsServer::~PhysicsServer() {
+
+ singleton=NULL;
+}
+
diff --git a/servers/physics_server.h b/servers/physics_server.h
new file mode 100644
index 0000000000..1fe477adc3
--- /dev/null
+++ b/servers/physics_server.h
@@ -0,0 +1,429 @@
+/*************************************************************************/
+/* physics_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef PHYSICS_SERVER_H
+#define PHYSICS_SERVER_H
+
+#include "object.h"
+#include "reference.h"
+
+class PhysicsDirectSpaceState;
+
+class PhysicsDirectBodyState : public Object {
+
+ OBJ_TYPE( PhysicsDirectBodyState, Object );
+protected:
+ static void _bind_methods();
+public:
+
+ virtual Vector3 get_total_gravity() const=0; // get gravity vector working on this body space/area
+ virtual float get_total_density() const=0; // get density of this body space/area
+
+ virtual float get_inverse_mass() const=0; // get the mass
+ virtual Vector3 get_inverse_inertia() const=0; // get density of this body space
+ virtual Matrix3 get_inverse_inertia_tensor() const=0; // get density of this body space
+
+ virtual void set_linear_velocity(const Vector3& p_velocity)=0;
+ virtual Vector3 get_linear_velocity() const=0;
+
+ virtual void set_angular_velocity(const Vector3& p_velocity)=0;
+ virtual Vector3 get_angular_velocity() const=0;
+
+ virtual void set_transform(const Transform& p_transform)=0;
+ virtual Transform get_transform() const=0;
+
+ virtual void add_force(const Vector3& p_force, const Vector3& p_pos)=0;
+
+ virtual void set_sleep_state(bool p_enable)=0;
+ virtual bool is_sleeping() const=0;
+
+ virtual int get_contact_count() const=0;
+
+ virtual Vector3 get_contact_local_pos(int p_contact_idx) const=0;
+ virtual Vector3 get_contact_local_normal(int p_contact_idx) const=0;
+ virtual int get_contact_local_shape(int p_contact_idx) const=0;
+
+ virtual RID get_contact_collider(int p_contact_idx) const=0;
+ virtual Vector3 get_contact_collider_pos(int p_contact_idx) const=0;
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const=0;
+ virtual Object* get_contact_collider_object(int p_contact_idx) const;
+ virtual int get_contact_collider_shape(int p_contact_idx) const=0;
+ virtual Vector3 get_contact_collider_velocity_at_pos(int p_contact_idx) const=0;
+
+ virtual real_t get_step() const=0;
+ virtual void integrate_forces();
+
+ virtual PhysicsDirectSpaceState* get_space_state()=0;
+
+ PhysicsDirectBodyState();
+};
+
+
+class PhysicsShapeQueryResult;
+
+
+class PhysicsDirectSpaceState : public Object {
+
+ OBJ_TYPE( PhysicsDirectSpaceState, Object );
+
+ Variant _intersect_ray(const Vector3& p_from, const Vector3& p_to,const Vector<RID>& p_exclude=Vector<RID>(),uint32_t p_user_mask=0);
+ Variant _intersect_shape(const RID& p_shape, const Transform& p_xform,int p_result_max=64,const Vector<RID>& p_exclude=Vector<RID>(),uint32_t p_user_mask=0);
+
+
+protected:
+ static void _bind_methods();
+
+public:
+
+ struct RayResult {
+
+ Vector3 position;
+ Vector3 normal;
+ RID rid;
+ ObjectID collider_id;
+ Object *collider;
+ int shape;
+ };
+
+ virtual bool intersect_ray(const Vector3& p_from, const Vector3& p_to,RayResult &r_result,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0)=0;
+
+ struct ShapeResult {
+
+ RID rid;
+ ObjectID collider_id;
+ Object *collider;
+ int shape;
+
+ };
+
+ virtual int intersect_shape(const RID& p_shape, const Transform& p_xform,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude=Set<RID>(),uint32_t p_user_mask=0)=0;
+
+ PhysicsDirectSpaceState();
+};
+
+
+class PhysicsShapeQueryResult : public Reference {
+
+ OBJ_TYPE( PhysicsShapeQueryResult, Reference );
+
+ Vector<PhysicsDirectSpaceState::ShapeResult> result;
+
+friend class PhysicsDirectSpaceState;
+
+protected:
+ static void _bind_methods();
+public:
+
+ int get_result_count() const;
+ RID get_result_rid(int p_idx) const;
+ ObjectID get_result_object_id(int p_idx) const;
+ Object* get_result_object(int p_idx) const;
+ int get_result_object_shape(int p_idx) const;
+
+ PhysicsShapeQueryResult();
+};
+
+
+class PhysicsServer : public Object {
+
+ OBJ_TYPE( PhysicsServer, Object );
+
+ static PhysicsServer * singleton;
+
+protected:
+ static void _bind_methods();
+
+public:
+
+ static PhysicsServer * get_singleton();
+
+ enum ShapeType {
+ SHAPE_PLANE, ///< plane:"plane"
+ SHAPE_RAY, ///< float:"length"
+ SHAPE_SPHERE, ///< float:"radius"
+ SHAPE_BOX, ///< vec3:"extents"
+ SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule
+ SHAPE_CONVEX_POLYGON, ///< array of planes:"planes"
+ SHAPE_CONCAVE_POLYGON, ///< vector3 array:"triangles" , or Dictionary with "indices" (int array) and "triangles" (Vector3 array)
+ SHAPE_HEIGHTMAP, ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"
+ SHAPE_CUSTOM, ///< Server-Implementation based custom shape, calling shape_create() with this value will result in an error
+ };
+
+ virtual RID shape_create(ShapeType p_shape)=0;
+ virtual void shape_set_data(RID p_shape, const Variant& p_data)=0;
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias)=0;
+
+ virtual ShapeType shape_get_type(RID p_shape) const=0;
+ virtual Variant shape_get_data(RID p_shape) const=0;
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const=0;
+
+
+ /* SPACE API */
+
+ virtual RID space_create()=0;
+ virtual void space_set_active(RID p_space,bool p_active)=0;
+ virtual bool space_is_active(RID p_space) const=0;
+
+ enum SpaceParameter {
+
+ SPACE_PARAM_CONTACT_RECYCLE_RADIUS,
+ SPACE_PARAM_CONTACT_MAX_SEPARATION,
+ SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION,
+ SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD,
+ SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD,
+ SPACE_PARAM_BODY_TIME_TO_SLEEP,
+ SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO,
+ SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS,
+ };
+
+ virtual void space_set_param(RID p_space,SpaceParameter p_param, real_t p_value)=0;
+ virtual real_t space_get_param(RID p_space,SpaceParameter p_param) const=0;
+
+ // this function only works on fixed process, errors and returns null otherwise
+ virtual PhysicsDirectSpaceState* space_get_direct_state(RID p_space)=0;
+
+
+ //missing space parameters
+
+ /* AREA API */
+
+ //missing attenuation? missing better override?
+
+
+
+ enum AreaParameter {
+ AREA_PARAM_GRAVITY,
+ AREA_PARAM_GRAVITY_VECTOR,
+ AREA_PARAM_GRAVITY_IS_POINT,
+ AREA_PARAM_GRAVITY_POINT_ATTENUATION,
+ AREA_PARAM_DENSITY,
+ AREA_PARAM_PRIORITY
+ };
+
+ virtual RID area_create()=0;
+
+ virtual void area_set_space(RID p_area, RID p_space)=0;
+ virtual RID area_get_space(RID p_area) const=0;
+
+
+ enum AreaSpaceOverrideMode {
+ AREA_SPACE_OVERRIDE_DISABLED,
+ AREA_SPACE_OVERRIDE_COMBINE,
+ AREA_SPACE_OVERRIDE_REPLACE,
+ };
+
+ virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode)=0;
+ virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const=0;
+
+ virtual void area_add_shape(RID p_area, RID p_shape, const Transform& p_transform=Transform())=0;
+ virtual void area_set_shape(RID p_area, int p_shape_idx,RID p_shape)=0;
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform& p_transform)=0;
+
+ virtual int area_get_shape_count(RID p_area) const=0;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const=0;
+ virtual Transform area_get_shape_transform(RID p_area, int p_shape_idx) const=0;
+
+ virtual void area_remove_shape(RID p_area, int p_shape_idx)=0;
+ virtual void area_clear_shapes(RID p_area)=0;
+
+ virtual void area_attach_object_instance_ID(RID p_area,ObjectID p_ID)=0;
+ virtual ObjectID area_get_object_instance_ID(RID p_area) const=0;
+
+ virtual void area_set_param(RID p_area,AreaParameter p_param,const Variant& p_value)=0;
+ virtual void area_set_transform(RID p_area, const Transform& p_transform)=0;
+
+ virtual Variant area_get_param(RID p_parea,AreaParameter p_param) const=0;
+ virtual Transform area_get_transform(RID p_area) const=0;
+
+ virtual void area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method)=0;
+
+ /* BODY API */
+
+ //missing ccd?
+
+ enum BodyMode {
+ BODY_MODE_STATIC,
+ BODY_MODE_STATIC_ACTIVE,
+ BODY_MODE_RIGID,
+ //BODY_MODE_SOFT
+ BODY_MODE_CHARACTER
+ };
+
+ virtual RID body_create(BodyMode p_mode=BODY_MODE_RIGID,bool p_init_sleeping=false)=0;
+
+ virtual void body_set_space(RID p_body, RID p_space)=0;
+ virtual RID body_get_space(RID p_body) const=0;
+
+ virtual void body_set_mode(RID p_body, BodyMode p_mode)=0;
+ virtual BodyMode body_get_mode(RID p_body, BodyMode p_mode) const=0;
+
+ virtual void body_add_shape(RID p_body, RID p_shape, const Transform& p_transform=Transform())=0;
+ virtual void body_set_shape(RID p_body, int p_shape_idx,RID p_shape)=0;
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform& p_transform)=0;
+
+ virtual int body_get_shape_count(RID p_body) const=0;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const=0;
+ virtual Transform body_get_shape_transform(RID p_body, int p_shape_idx) const=0;
+
+ virtual void body_set_shape_as_trigger(RID p_body, int p_shape_idx,bool p_enable)=0;
+ virtual bool body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const=0;
+
+ virtual void body_remove_shape(RID p_body, int p_shape_idx)=0;
+ virtual void body_clear_shapes(RID p_body)=0;
+
+ virtual void body_attach_object_instance_ID(RID p_body,uint32_t p_ID)=0;
+ virtual uint32_t body_get_object_instance_ID(RID p_body) const=0;
+
+ virtual void body_set_enable_continuous_collision_detection(RID p_body,bool p_enable)=0;
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const=0;
+
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags)=0;
+ virtual uint32_t body_get_user_flags(RID p_body, uint32_t p_flags) const=0;
+
+ // common body variables
+ enum BodyParameter {
+ BODY_PARAM_BOUNCE,
+ BODY_PARAM_FRICTION,
+ BODY_PARAM_MASS, ///< unused for static, always infinite
+ BODY_PARAM_MAX,
+ };
+
+ virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value)=0;
+ virtual float body_get_param(RID p_body, BodyParameter p_param) const=0;
+
+ //advanced simulation
+ virtual void body_static_simulate_motion(RID p_body,const Transform& p_new_transform)=0;
+
+ //state
+ enum BodyState {
+ BODY_STATE_TRANSFORM,
+ BODY_STATE_LINEAR_VELOCITY,
+ BODY_STATE_ANGULAR_VELOCITY,
+ BODY_STATE_SLEEPING,
+ BODY_STATE_CAN_SLEEP
+ };
+
+ virtual void body_set_state(RID p_body, BodyState p_state, const Variant& p_variant)=0;
+ virtual Variant body_get_state(RID p_body, BodyState p_state) const=0;
+
+ //do something about it
+ virtual void body_set_applied_force(RID p_body, const Vector3& p_force)=0;
+ virtual Vector3 body_get_applied_force(RID p_body) const=0;
+
+ virtual void body_set_applied_torque(RID p_body, const Vector3& p_torque)=0;
+ virtual Vector3 body_get_applied_torque(RID p_body) const=0;
+
+ virtual void body_apply_impulse(RID p_body, const Vector3& p_pos, const Vector3& p_impulse)=0;
+ virtual void body_set_axis_velocity(RID p_body, const Vector3& p_axis_velocity)=0;
+
+ //fix
+ virtual void body_add_collision_exception(RID p_body, RID p_body_b)=0;
+ virtual void body_remove_collision_exception(RID p_body, RID p_body_b)=0;
+ virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions)=0;
+
+ virtual void body_set_max_contacts_reported(RID p_body, int p_contacts)=0;
+ virtual int body_get_max_contacts_reported(RID p_body) const=0;
+
+ //missing remove
+ virtual void body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold)=0;
+ virtual float body_get_contacts_reported_depth_treshold(RID p_body) const=0;
+
+ virtual void body_set_omit_force_integration(RID p_body,bool p_omit)=0;
+ virtual bool body_is_omitting_force_integration(RID p_body) const=0;
+
+ virtual void body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata=Variant())=0;
+
+ /* JOINT API */
+#if 0
+ enum JointType {
+
+ JOINT_PIN,
+ JOINT_GROOVE,
+ JOINT_DAMPED_SPRING
+ };
+
+ enum JointParam {
+ JOINT_PARAM_BIAS,
+ JOINT_PARAM_MAX_BIAS,
+ JOINT_PARAM_MAX_FORCE,
+ };
+
+ virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value)=0;
+ virtual real_t joint_get_param(RID p_joint,JointParam p_param) const=0;
+
+ virtual RID pin_joint_create(const Vector3& p_anchor,RID p_body_a,RID p_body_b=RID())=0;
+ virtual RID groove_joint_create(const Vector3& p_a_groove1,const Vector3& p_a_groove2, const Vector3& p_b_anchor, RID p_body_a,RID p_body_b)=0;
+ virtual RID damped_spring_joint_create(const Vector3& p_anchor_a,const Vector3& p_anchor_b,RID p_body_a,RID p_body_b=RID())=0;
+
+ enum DampedStringParam {
+ DAMPED_STRING_REST_LENGTH,
+ DAMPED_STRING_STIFFNESS,
+ DAMPED_STRING_DAMPING
+ };
+ virtual void damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value)=0;
+ virtual real_t damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const=0;
+
+ virtual JointType joint_get_type(RID p_joint) const=0;
+#endif
+ /* QUERY API */
+
+ enum AreaBodyStatus {
+ AREA_BODY_ADDED,
+ AREA_BODY_REMOVED
+ };
+
+
+ /* MISC */
+
+ virtual void free(RID p_rid)=0;
+
+ virtual void set_active(bool p_active)=0;
+ virtual void init()=0;
+ virtual void step(float p_step)=0;
+ virtual void sync()=0;
+ virtual void flush_queries()=0;
+ virtual void finish()=0;
+
+ PhysicsServer();
+ ~PhysicsServer();
+};
+
+VARIANT_ENUM_CAST( PhysicsServer::ShapeType );
+VARIANT_ENUM_CAST( PhysicsServer::SpaceParameter );
+VARIANT_ENUM_CAST( PhysicsServer::AreaParameter );
+VARIANT_ENUM_CAST( PhysicsServer::AreaSpaceOverrideMode );
+VARIANT_ENUM_CAST( PhysicsServer::BodyMode );
+VARIANT_ENUM_CAST( PhysicsServer::BodyParameter );
+VARIANT_ENUM_CAST( PhysicsServer::BodyState );
+//VARIANT_ENUM_CAST( PhysicsServer::JointParam );
+//VARIANT_ENUM_CAST( PhysicsServer::JointType );
+//VARIANT_ENUM_CAST( PhysicsServer::DampedStringParam );
+//VARIANT_ENUM_CAST( PhysicsServer::ObjectType );
+VARIANT_ENUM_CAST( PhysicsServer::AreaBodyStatus );
+
+#endif
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
new file mode 100644
index 0000000000..638156d813
--- /dev/null
+++ b/servers/register_server_types.cpp
@@ -0,0 +1,68 @@
+/*************************************************************************/
+/* register_server_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "register_server_types.h"
+#include "globals.h"
+
+#include "visual_server.h"
+#include "audio_server.h"
+#include "physics_server.h"
+#include "physics_2d_server.h"
+#include "spatial_sound_server.h"
+#include "spatial_sound_2d_server.h"
+
+void register_server_types() {
+
+ Globals::get_singleton()->add_singleton( Globals::Singleton("VisualServer",VisualServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("VS",VisualServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("AudioServer",AudioServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("AS",AudioServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("PhysicsServer",PhysicsServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("PS",PhysicsServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("Physics2DServer",Physics2DServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("PS2D",Physics2DServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("SpatialSoundServer",SpatialSound2DServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("SS",SpatialSound2DServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("SpatialSound2DServer",SpatialSound2DServer::get_singleton()) );
+ Globals::get_singleton()->add_singleton( Globals::Singleton("SS2D",SpatialSound2DServer::get_singleton()) );
+
+
+ ObjectTypeDB::register_virtual_type<Physics2DDirectBodyState>();
+ ObjectTypeDB::register_virtual_type<Physics2DDirectSpaceState>();
+ ObjectTypeDB::register_virtual_type<Physics2DShapeQueryResult>();
+
+ ObjectTypeDB::register_virtual_type<PhysicsDirectBodyState>();
+ ObjectTypeDB::register_virtual_type<PhysicsDirectSpaceState>();
+ ObjectTypeDB::register_virtual_type<PhysicsShapeQueryResult>();
+
+}
+
+void unregister_server_types(){
+
+
+}
diff --git a/servers/register_server_types.h b/servers/register_server_types.h
new file mode 100644
index 0000000000..b0a01fc0a5
--- /dev/null
+++ b/servers/register_server_types.h
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* register_server_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 REGISTER_SERVER_TYPES_H
+#define REGISTER_SERVER_TYPES_H
+
+void register_server_types();
+void unregister_server_types();
+
+#endif // REGISTER_SERVER_TYPES_H
diff --git a/servers/spatial_sound/SCsub b/servers/spatial_sound/SCsub
new file mode 100644
index 0000000000..16fe3a59ac
--- /dev/null
+++ b/servers/spatial_sound/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/servers/spatial_sound/spatial_sound_server_sw.cpp b/servers/spatial_sound/spatial_sound_server_sw.cpp
new file mode 100644
index 0000000000..7ec29d32cb
--- /dev/null
+++ b/servers/spatial_sound/spatial_sound_server_sw.cpp
@@ -0,0 +1,1049 @@
+/*************************************************/
+/* spatial_sound_server_sw.cpp */
+/*************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/*************************************************/
+/* Source code within this file is: */
+/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
+/* All Rights Reserved. */
+/*************************************************/
+
+#include "spatial_sound_server_sw.h"
+#include "os/os.h"
+#include "servers/audio/audio_filter_sw.h"
+
+
+
+int SpatialSoundServerSW::InternalAudioStream::get_channel_count() const {
+
+ return AudioServer::get_singleton()->get_default_channel_count();
+}
+
+void SpatialSoundServerSW::InternalAudioStream::set_mix_rate(int p_rate) {
+
+
+}
+
+void SpatialSoundServerSW::InternalAudioStream::update() {
+
+ owner->_update_sources();
+}
+
+bool SpatialSoundServerSW::InternalAudioStream::mix(int32_t *p_buffer,int p_frames) {
+
+ return owner->internal_buffer_mix(p_buffer,p_frames);
+}
+
+void SpatialSoundServerSW::_update_sources() {
+
+ _THREAD_SAFE_METHOD_
+ for (Set<Source*>::Element *E=streaming_sources.front();E;E=E->next()) {
+
+ Source *s=E->get();
+ ERR_CONTINUE(!s->stream);
+ s->stream->update();
+ }
+}
+
+
+SpatialSoundServerSW::Room::Room() {
+
+// params[ROOM_PARAM_SPEED_OF_SOUND]=343.0;
+ params[ROOM_PARAM_SPEED_OF_SOUND_SCALE]=1;
+ params[ROOM_PARAM_DOPPLER_FACTOR]=1.0;
+ params[ROOM_PARAM_PITCH_SCALE]=1.0;
+ params[ROOM_PARAM_VOLUME_SCALE_DB]=0;
+ params[ROOM_PARAM_REVERB_SEND]=0;
+ params[ROOM_PARAM_CHORUS_SEND]=0;
+ params[ROOM_PARAM_ATTENUATION_SCALE]=1.0;
+ params[ROOM_PARAM_ATTENUATION_HF_CUTOFF]=5000;
+ params[ROOM_PARAM_ATTENUATION_HF_FLOOR_DB]=-24.0;
+ params[ROOM_PARAM_ATTENUATION_HF_RATIO_EXP]=1.0;
+ params[ROOM_PARAM_ATTENUATION_REVERB_SCALE]=0.0;
+ override_other_sources=false;
+ reverb=ROOM_REVERB_HALL;
+ octree_id=0;
+ level=-1;
+
+
+}
+
+
+SpatialSoundServerSW::Source::Source() {
+
+ params[SOURCE_PARAM_VOLUME_DB]=0.0;
+ params[SOURCE_PARAM_PITCH_SCALE]=1.0;
+ params[SOURCE_PARAM_ATTENUATION_MIN_DISTANCE]=1;
+ params[SOURCE_PARAM_ATTENUATION_MAX_DISTANCE]=100;
+ params[SOURCE_PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good)
+ params[SOURCE_PARAM_EMISSION_CONE_DEGREES]=180.0; //cone disabled
+ params[SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB]=-6.0; //minus 6 db attenuation
+ stream=NULL;
+ voices.resize(1);
+ last_voice=0;
+}
+
+SpatialSoundServerSW::Source::Voice::Voice() {
+
+ active=false;
+ restart=false;
+ pitch_scale=1.0;
+ volume_scale=0.0;
+ voice_rid=AudioServer::get_singleton()->voice_create();
+
+}
+SpatialSoundServerSW::Source::Voice::~Voice() {
+
+ AudioServer::get_singleton()->free(voice_rid);
+}
+
+
+SpatialSoundServerSW::Listener::Listener() {
+
+ params[LISTENER_PARAM_VOLUME_SCALE_DB]=0.0;
+ params[LISTENER_PARAM_PITCH_SCALE]=1.0;
+ params[LISTENER_PARAM_ATTENUATION_SCALE]=1.0;
+ params[LISTENER_PARAM_RECEPTION_CONE_DEGREES]=60.0;
+ params[LISTENER_PARAM_RECEPTION_CONE_ATTENUATION_DB]=-6; // minus six decibels
+
+}
+
+/* SPACE */
+RID SpatialSoundServerSW::space_create() {
+
+ Space* space = memnew( Space );
+ RID space_rid = space_owner.make_rid(space);
+ space->default_room=room_create();
+ room_set_space(space->default_room,space_rid);
+ return space_rid;
+}
+
+/* ROOM */
+
+RID SpatialSoundServerSW::room_create() {
+
+ Room *room = memnew( Room );
+ return room_owner.make_rid(room);
+}
+
+void SpatialSoundServerSW::room_set_space(RID p_room,RID p_space) {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+
+ if (room->space.is_valid()) {
+
+ Space *space = space_owner.get(room->space);
+ space->rooms.erase(p_room);
+ space->octree.erase(room->octree_id);
+ //room->octree_id=0;
+ }
+
+ room->space=RID();
+
+ if (p_space.is_valid()) {
+
+ Space *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ space->rooms.insert(p_room);
+ room->octree_id=space->octree.create(room,AABB());
+ //set bounds
+ AABB aabb = room->bounds.is_empty()?AABB():room->bounds.get_aabb();
+ space->octree.move(room->octree_id,room->transform.xform(aabb));
+ room->space=p_space;
+ }
+
+
+}
+
+RID SpatialSoundServerSW::room_get_space(RID p_room) const {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,RID());
+
+
+ return room->space;
+}
+
+
+
+void SpatialSoundServerSW::room_set_bounds(RID p_room, const BSP_Tree& p_bounds) {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+
+ room->bounds=p_bounds;
+
+ if (!room->space.is_valid())
+ return;
+
+ AABB aabb = room->bounds.is_empty()?AABB():room->bounds.get_aabb();
+ Space* space = space_owner.get(room->space);
+ ERR_FAIL_COND(!space);
+
+ space->octree.move(room->octree_id,room->transform.xform(aabb));
+
+}
+BSP_Tree SpatialSoundServerSW::room_get_bounds(RID p_room) const {
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,BSP_Tree());
+
+ return room->bounds;
+}
+
+void SpatialSoundServerSW::room_set_transform(RID p_room, const Transform& p_transform) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->transform=p_transform;
+ room->inverse_transform=p_transform.affine_inverse(); // needs to be done to unscale BSP properly
+
+ if (!room->space.is_valid())
+ return;
+
+ if (!room->bounds.is_empty()) {
+
+ Space* space = space_owner.get(room->space);
+ ERR_FAIL_COND(!space);
+
+ space->octree.move(room->octree_id,room->transform.xform(room->bounds.get_aabb()));
+ }
+}
+
+Transform SpatialSoundServerSW::room_get_transform(RID p_room) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,Transform());
+ return room->transform;
+}
+
+
+void SpatialSoundServerSW::room_set_param(RID p_room, RoomParam p_param, float p_value) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ ERR_FAIL_INDEX(p_param,ROOM_PARAM_MAX);
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->params[p_param]=p_value;
+
+}
+float SpatialSoundServerSW::room_get_param(RID p_room, RoomParam p_param) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ ERR_FAIL_INDEX_V(p_param,ROOM_PARAM_MAX,0);
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,0);
+ return room->params[p_param];
+}
+
+void SpatialSoundServerSW::room_set_level(RID p_room, int p_level) {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->level =p_level;
+
+}
+
+int SpatialSoundServerSW::room_get_level(RID p_room) const {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,0);
+ return room->level;
+
+}
+
+
+void SpatialSoundServerSW::room_set_reverb(RID p_room, RoomReverb p_reverb) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->reverb=p_reverb;
+
+}
+SpatialSoundServerSW::RoomReverb SpatialSoundServerSW::room_get_reverb(RID p_room) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,ROOM_REVERB_SMALL);
+ return room->reverb;
+}
+
+//useful for underwater or rooms with very strange conditions
+void SpatialSoundServerSW::room_set_force_params_to_all_sources(RID p_room, bool p_force) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->override_other_sources=p_force;
+
+}
+bool SpatialSoundServerSW::room_is_forcing_params_to_all_sources(RID p_room) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,false);
+ return room->override_other_sources;
+}
+
+/* SOURCE */
+
+RID SpatialSoundServerSW::source_create(RID p_space) {
+
+ Space *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,RID());
+
+ Source *source = memnew( Source );
+ source->space=p_space;
+ RID source_rid = source_owner.make_rid(source);
+ space->sources.insert(source_rid);
+
+ return source_rid;
+}
+
+
+void SpatialSoundServerSW::source_set_polyphony(RID p_source,int p_voice_count) {
+
+
+ ERR_FAIL_COND(p_voice_count<=0); // more than 32 is too much, change this if you really need more
+ if (p_voice_count>32) {
+
+ ERR_PRINT("Voices will be clipped to 32");
+ p_voice_count=32;
+ }
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+
+ if (p_voice_count<source->voices.size()) {
+
+ for(int i=p_voice_count;i<source->voices.size();i++) {
+ active_voices.erase(ActiveVoice(source,i)); //erase from active voices
+ }
+ }
+ source->voices.resize(p_voice_count);
+
+}
+
+int SpatialSoundServerSW::source_get_polyphony(RID p_source) const {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,-1);
+ return source->voices.size();
+
+}
+
+void SpatialSoundServerSW::source_set_transform(RID p_source, const Transform& p_transform) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ source->transform=p_transform;
+ source->transform.orthonormalize();
+}
+Transform SpatialSoundServerSW::source_get_transform(RID p_source) const {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,Transform());
+ return source->transform;
+}
+
+void SpatialSoundServerSW::source_set_param(RID p_source, SourceParam p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,SOURCE_PARAM_MAX);
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ source->params[p_param]=p_value;
+
+}
+float SpatialSoundServerSW::source_get_param(RID p_source, SourceParam p_param) const {
+ ERR_FAIL_INDEX_V(p_param,SOURCE_PARAM_MAX,0);
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,0);
+ return source->params[p_param];
+
+
+}
+
+void SpatialSoundServerSW::source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ AudioServer::get_singleton()->lock();
+ source->stream=p_stream;
+ _THREAD_SAFE_METHOD_
+
+ if (!p_stream) {
+ streaming_sources.erase(source);
+ active_voices.erase(ActiveVoice(source,VOICE_IS_STREAM));
+ } else {
+ streaming_sources.insert(source);
+ active_voices.insert(ActiveVoice(source,VOICE_IS_STREAM));
+ zeromem(source->stream_data.filter_state,sizeof(Source::StreamData::FilterState)*4); //reset filter for safetyness
+ p_stream->set_mix_rate(AudioServer::get_singleton()->get_default_mix_rate());
+ }
+
+ AudioServer::get_singleton()->unlock();
+
+} //null to unset
+
+SpatialSoundServer::SourceVoiceID SpatialSoundServerSW::source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,SOURCE_INVALID_VOICE);
+
+ int to_play=0;
+
+ if (p_voice==SOURCE_NEXT_VOICE) {
+ to_play=source->last_voice+1;
+ if (to_play>=source->voices.size())
+ to_play=0;
+
+ } else
+ to_play=p_voice;
+
+ ERR_FAIL_INDEX_V(to_play,source->voices.size(),SOURCE_INVALID_VOICE);
+
+ source->voices[to_play].restart=true;
+ source->voices[to_play].sample_rid=p_sample;
+ source->voices[to_play].sample_mix_rate=p_mix_rate;
+ source->voices[to_play].pitch_scale=1;
+ source->voices[to_play].volume_scale=0;
+ source->last_voice=to_play;
+ active_voices.insert(ActiveVoice(source,to_play));
+ return to_play;
+}
+
+/* VOICES */
+void SpatialSoundServerSW::source_voice_set_pitch_scale(RID p_source, SourceVoiceID p_voice, float p_pitch_scale) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ ERR_FAIL_INDEX(p_voice,source->voices.size());
+ source->voices[p_voice].pitch_scale=p_pitch_scale;
+
+}
+void SpatialSoundServerSW::source_voice_set_volume_scale_db(RID p_source, SourceVoiceID p_voice, float p_db) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ ERR_FAIL_INDEX(p_voice,source->voices.size());
+ source->voices[p_voice].volume_scale=p_db;
+
+}
+
+bool SpatialSoundServerSW::source_is_voice_active(RID p_source, SourceVoiceID p_voice) const {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,false);
+ ERR_FAIL_INDEX_V(p_voice,source->voices.size(),false);
+ return source->voices[p_voice].active || source->voices[p_voice].restart;
+
+}
+void SpatialSoundServerSW::source_stop_voice(RID p_source, SourceVoiceID p_voice) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ ERR_FAIL_INDEX(p_voice,source->voices.size());
+ if (source->voices[p_voice].active) {
+ AudioServer::get_singleton()->voice_stop(source->voices[p_voice].voice_rid);
+ }
+ source->voices[p_voice].active=false;
+ source->voices[p_voice].restart=false;
+ active_voices.erase(ActiveVoice(source,p_voice));
+}
+
+/* LISTENER */
+
+RID SpatialSoundServerSW::listener_create() {
+
+ Listener *listener = memnew( Listener );
+ RID listener_rid = listener_owner.make_rid(listener);
+ return listener_rid;
+
+}
+
+void SpatialSoundServerSW::listener_set_space(RID p_listener,RID p_space) {
+
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND(!listener);
+
+ if (listener->space.is_valid()) {
+
+ Space *lspace = space_owner.get(listener->space);
+ ERR_FAIL_COND(!lspace);
+ lspace->listeners.erase(p_listener);
+ }
+
+ listener->space=RID();
+
+ if (p_space.is_valid()) {
+ Space *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+
+ listener->space=p_space;
+ space->listeners.insert(p_listener);
+ }
+
+}
+
+void SpatialSoundServerSW::listener_set_transform(RID p_listener, const Transform& p_transform) {
+
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND(!listener);
+ listener->transform=p_transform;
+ listener->transform.orthonormalize(); //must be done..
+}
+Transform SpatialSoundServerSW::listener_get_transform(RID p_listener) const {
+
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND_V(!listener,Transform());
+ return listener->transform;
+}
+
+void SpatialSoundServerSW::listener_set_param(RID p_listener, ListenerParam p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,LISTENER_PARAM_MAX);
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND(!listener);
+ listener->params[p_param]=p_value;
+}
+
+float SpatialSoundServerSW::listener_get_param(RID p_listener, ListenerParam p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param,LISTENER_PARAM_MAX,0);
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND_V(!listener,0);
+ return listener->params[p_param];
+}
+
+
+/* MISC */
+
+void SpatialSoundServerSW::free(RID p_id) {
+
+
+ if (space_owner.owns(p_id)) {
+
+ Space *space = space_owner.get(p_id);
+ free(space->default_room);
+
+ while(space->listeners.size()) {
+ listener_set_space(space->listeners.front()->get(),RID());
+ }
+ while(space->sources.size()) {
+ free(space->sources.front()->get());
+ }
+ while(space->rooms.size()) {
+ room_set_space(space->rooms.front()->get(),RID());
+ }
+ space_owner.free(p_id);
+ memdelete(space);
+
+ } else if (source_owner.owns(p_id)) {
+
+ Source *source = source_owner.get(p_id);
+ if (source->stream)
+ source_set_audio_stream(p_id,NULL);
+
+ Space *space = space_owner.get(source->space);
+ ERR_FAIL_COND(!space);
+ space->sources.erase(p_id);
+ for(int i=0;i<source->voices.size();i++) {
+ active_voices.erase(ActiveVoice(source,i));
+ }
+ source_owner.free(p_id);
+ memdelete(source);
+ } else if (listener_owner.owns(p_id)) {
+
+ Listener *listener = listener_owner.get(p_id);
+ if (listener->space.is_valid()) {
+ Space *space = space_owner.get(listener->space);
+ ERR_FAIL_COND(!space);
+ space->listeners.erase(p_id);
+ }
+ listener_owner.free(p_id);
+ memdelete(listener);
+
+ } else if (room_owner.owns(p_id)) {
+
+ Room *room = room_owner.get(p_id);
+
+ if (room->space.is_valid()) {
+ Space *space = space_owner.get(room->space);
+ ERR_FAIL_COND(!space);
+ space->octree.erase(room->octree_id);
+ space->rooms.erase(p_id);
+ }
+ room_owner.free(p_id);
+ memdelete(room);
+ } else {
+ ERR_PRINT("Attempt to free invalid ID") ;
+ }
+
+}
+
+void SpatialSoundServerSW::_clean_up_owner(RID_OwnerBase *p_owner, const char *p_area) {
+
+ List<RID> rids;
+ p_owner->get_owned_list(&rids);
+
+ for(List<RID>::Element *I=rids.front();I;I=I->next()) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+
+ print_line("Leaked RID ("+itos(I->get().get_id())+") of type "+String(p_area));
+ }
+ free(I->get());
+ }
+}
+
+void SpatialSoundServerSW::init() {
+
+ internal_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE*INTERNAL_BUFFER_MAX_CHANNELS);
+ internal_buffer_channels=AudioServer::get_singleton()->get_default_channel_count();
+
+ internal_audio_stream = memnew( InternalAudioStream );
+ internal_audio_stream->owner=this;
+ internal_audio_stream_rid = AudioServer::get_singleton()->audio_stream_create(internal_audio_stream);
+
+ AudioServer::get_singleton()->stream_set_active(internal_audio_stream_rid,true);
+
+}
+
+
+
+static float _get_attenuation(float cosine, float angle, float attenuation) {
+
+
+ float listener_ang = Math::rad2deg(Math::acos(cosine))-angle;
+
+ if (listener_ang>0 && angle<180.0) {
+ listener_ang/=(180.0-angle);
+ return Math::db2linear(Math::sin(listener_ang*(Math_PI/2.0))*attenuation);
+ }
+ return 1.0;
+}
+
+
+bool SpatialSoundServerSW::internal_buffer_mix(int32_t *p_buffer,int p_frames) {
+
+ if (streaming_sources.size()==0)
+ return false; //nothing to mix
+
+
+ for (Set<Source*>::Element *E=streaming_sources.front();E;E=E->next()) {
+
+ Source *s=E->get();
+ ERR_CONTINUE(!s->stream);
+
+ int channels = s->stream->get_channel_count();
+ Source::StreamData &sd=s->stream_data;
+
+ int todo=p_frames;
+
+ AudioFilterSW filter;
+ filter.set_sampling_rate(AudioServer::get_singleton()->get_default_mix_rate());
+ filter.set_cutoff(sd.filter_cutoff);
+ filter.set_gain(sd.filter_gain);
+ filter.set_resonance(1);
+ filter.set_mode(AudioFilterSW::HIGHSHELF);
+ filter.set_stages(1);
+ AudioFilterSW::Coeffs coefs;
+ filter.prepare_coefficients(&coefs);
+
+ int32_t in[4];
+#ifndef SPATIAL_SOUND_SERVER_NO_FILTER
+#define DO_FILTER(m_c)\
+ {\
+ float val = in[m_c];\
+ float pre=val;\
+ val = val*coefs.b0 + sd.filter_state[m_c].hb[0]*coefs.b1 + sd.filter_state[m_c].hb[1]*coefs.b2 + sd.filter_state[m_c].ha[0]*coefs.a1 + sd.filter_state[m_c].ha[1]*coefs.a2;\
+ sd.filter_state[m_c].ha[1]=sd.filter_state[m_c].ha[0];\
+ sd.filter_state[m_c].hb[1]=sd.filter_state[m_c].hb[0]; \
+ sd.filter_state[m_c].hb[0]=pre;\
+ sd.filter_state[m_c].ha[0]=val;\
+ in[m_c]=Math::fast_ftoi(val);\
+ }
+#else
+#define DO_FILTER(m_c)
+#endif
+
+ while(todo) {
+
+ int to_mix=MIN(todo,INTERNAL_BUFFER_SIZE);
+
+ s->stream->mix(internal_buffer,to_mix);
+
+ switch(internal_buffer_channels) {
+
+ case 2: {
+
+ float p = sd.panning.x*0.5+0.5;
+ float panf[2]={ (1.0-p),p };
+ panf[0]*=sd.volume;
+ panf[1]*=sd.volume;
+
+ int32_t pan[2]={Math::fast_ftoi(panf[0]*(1<<16)),Math::fast_ftoi(panf[1]*(1<<16))};
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[i];
+ in[1]=internal_buffer[i];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[(i<<1)+0];
+ in[1]=internal_buffer[(i<<1)+1];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=(internal_buffer[(i<<2)+0]+internal_buffer[(i<<2)+2])>>1;
+ in[1]=(internal_buffer[(i<<2)+1]+internal_buffer[(i<<2)+3])>>1;
+ DO_FILTER(0);
+ DO_FILTER(1);
+ p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 4: {
+
+ float xp = sd.panning.x*0.5+0.5;
+ float yp = sd.panning.y*0.5+0.5;
+ float panf[4]={ (1.0-xp)*(1.0-yp),(xp)*(1.0-yp),(1.0-xp)*(yp),(xp)*(yp) };
+ panf[0]*=sd.volume;
+ panf[1]*=sd.volume;
+ panf[2]*=sd.volume;
+ panf[3]*=sd.volume;
+
+ int32_t pan[4]={
+ Math::fast_ftoi(panf[0]*(1<<16)),
+ Math::fast_ftoi(panf[1]*(1<<16)),
+ Math::fast_ftoi(panf[2]*(1<<16)),
+ Math::fast_ftoi(panf[3]*(1<<16))};
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[i];
+ in[1]=internal_buffer[i];
+ in[2]=internal_buffer[i];
+ in[3]=internal_buffer[i];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ DO_FILTER(2);
+ DO_FILTER(3);
+ p_buffer[(i<<2)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]);
+ p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]);
+ p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[(i<<1)+0];
+ in[1]=internal_buffer[(i<<1)+1];
+ in[2]=internal_buffer[(i<<1)+0];
+ in[3]=internal_buffer[(i<<1)+1];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ DO_FILTER(2);
+ DO_FILTER(3);
+ p_buffer[(i<<2)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]);
+ p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]);
+ p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[(i<<2)+0];
+ in[1]=internal_buffer[(i<<2)+1];
+ in[2]=internal_buffer[(i<<2)+2];
+ in[3]=internal_buffer[(i<<2)+3];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ DO_FILTER(2);
+ DO_FILTER(3);
+ p_buffer[(i<<2)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]);
+ p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]);
+ p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 6: {
+
+
+ } break;
+ }
+ p_buffer+=to_mix*internal_buffer_channels;
+ todo-=to_mix;
+
+ }
+
+ }
+
+ return true;
+}
+
+void SpatialSoundServerSW::update(float p_delta) {
+
+ List<ActiveVoice> to_disable;
+
+
+ for(Set<ActiveVoice>::Element *E=active_voices.front();E;E=E->next()) {
+
+ Source *source = E->get().source;
+ int voice = E->get().voice;
+
+ if (voice!=VOICE_IS_STREAM) {
+ Source::Voice &v=source->voices[voice];
+ ERR_CONTINUE(!v.active && !v.restart); // likely a bug...
+ }
+
+ //this could be optimized at some point... am not sure
+ Space *space=space_owner.get(source->space);
+ Room *room=room_owner.get(space->default_room);
+ int max_level=-0x80000000;
+ int rooms_culled = space->octree.cull_point(source->transform.origin,cull_rooms,MAX_CULL_ROOMS);
+ for(int i=0;i<rooms_culled;i++) {
+
+ Room *r=cull_rooms[i];
+ ERR_CONTINUE( r->bounds.is_empty() ); // how did this happen??
+ if (r->level<=max_level) //ignore optimization (level too low)
+ continue;
+ Vector3 local_point = r->inverse_transform.xform(source->transform.origin);
+ if (!r->bounds.point_is_inside(local_point))
+ continue;
+ room=r;
+ max_level=r->level;
+
+ }
+
+
+ //compute mixing weights (support for multiple listeners in the same output)
+ float total_distance=0;
+ for(Set<RID>::Element *L=space->listeners.front();L;L=L->next()) {
+ Listener *listener=listener_owner.get(L->get());
+ total_distance+=listener->transform.origin.distance_to(source->transform.origin);
+ }
+
+ //compute spatialization variables, weighted according to distance
+ float volume_attenuation = 0.0;
+ float air_absorption_hf_cutoff = 0.0;
+ float air_absorption = 0.0;
+ float pitch_scale=0.0;
+ Vector3 panning;
+
+
+ for(Set<RID>::Element *L=space->listeners.front();L;L=L->next()) {
+
+ Listener *listener=listener_owner.get(L->get());
+
+ Vector3 rel_vector = listener->transform.xform_inv(source->transform.origin);
+ Vector3 source_rel_vector = source->transform.xform_inv(listener->transform.origin).normalized();
+ float distance=rel_vector.length();
+ float weight = distance/total_distance;
+ float pscale=1.0;
+
+ float distance_scale=listener->params[LISTENER_PARAM_ATTENUATION_SCALE]*room->params[ROOM_PARAM_ATTENUATION_SCALE];
+ float distance_min=source->params[SOURCE_PARAM_ATTENUATION_MIN_DISTANCE]*distance_scale;
+ float distance_max=source->params[SOURCE_PARAM_ATTENUATION_MAX_DISTANCE]*distance_scale;
+ float attenuation_exp=source->params[SOURCE_PARAM_ATTENUATION_DISTANCE_EXP];
+ float attenuation=1;
+
+ if (distance_max>0) {
+ distance = CLAMP(distance,distance_min,distance_max);
+ attenuation = Math::pow(1.0 - ((distance - distance_min)/distance_max),CLAMP(attenuation_exp,0.001,16));
+ }
+
+ float hf_attenuation_cutoff = room->params[ROOM_PARAM_ATTENUATION_HF_CUTOFF];
+ float hf_attenuation_exp = room->params[ROOM_PARAM_ATTENUATION_HF_RATIO_EXP];
+ float hf_attenuation_floor = room->params[ROOM_PARAM_ATTENUATION_HF_FLOOR_DB];
+ float absorption=Math::db2linear(Math::lerp(hf_attenuation_floor,0,Math::pow(attenuation,hf_attenuation_exp)));
+
+ // source emission cone
+
+ float emission_deg=source->params[SOURCE_PARAM_EMISSION_CONE_DEGREES];
+ float emission_attdb=source->params[SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB];
+ absorption*=_get_attenuation(source_rel_vector.dot(Vector3(0,0,-1)),emission_deg,emission_attdb);
+
+ Vector3 vpanning=rel_vector.normalized();
+
+ //listener stuff
+
+ {
+
+ // head cone
+
+ float reception_deg=listener->params[LISTENER_PARAM_RECEPTION_CONE_DEGREES];
+ float reception_attdb=listener->params[LISTENER_PARAM_RECEPTION_CONE_ATTENUATION_DB];
+
+ absorption*=_get_attenuation(vpanning.dot(Vector3(0,0,-1)),reception_deg,reception_attdb);
+
+ // scale
+
+ attenuation*=Math::db2linear(listener->params[LISTENER_PARAM_VOLUME_SCALE_DB]);
+ pscale*=Math::db2linear(listener->params[LISTENER_PARAM_PITCH_SCALE]);
+
+
+ }
+
+
+
+
+ //add values
+
+ volume_attenuation+=weight*attenuation; // plus other stuff i guess
+ air_absorption+=weight*absorption;
+ air_absorption_hf_cutoff+=weight*hf_attenuation_cutoff;
+ panning+=vpanning*weight;
+ pitch_scale+=pscale*weight;
+
+ }
+
+ RoomReverb reverb_room;
+ float reverb_send;
+
+ /* APPLY ROOM SETTINGS */
+
+ {
+ pitch_scale*=room->params[ROOM_PARAM_PITCH_SCALE];
+ volume_attenuation*=Math::db2linear(room->params[ROOM_PARAM_VOLUME_SCALE_DB]);
+ reverb_room=room->reverb;
+ reverb_send=Math::lerp(1.0,volume_attenuation,room->params[ROOM_PARAM_ATTENUATION_REVERB_SCALE])*room->params[ROOM_PARAM_REVERB_SEND];
+
+ }
+
+ /* UPDATE VOICE & STREAM */
+
+
+
+ if (voice==VOICE_IS_STREAM) {
+
+ //update voice!!
+ source->stream_data.panning=panning;
+ source->stream_data.volume=volume_attenuation*Math::db2linear(source->params[SOURCE_PARAM_VOLUME_DB]);
+ source->stream_data.reverb=reverb_room;
+ source->stream_data.reverb_send=reverb_send;
+ source->stream_data.filter_gain=air_absorption;
+ source->stream_data.filter_cutoff=air_absorption_hf_cutoff;
+
+ if (!source->stream) //stream is gone bye bye
+ to_disable.push_back(ActiveVoice(source,voice)); // oh well..
+
+ } else if (voice>=0) {
+ //update stream!!
+ Source::Voice &v=source->voices[voice];
+
+ if (v.restart)
+ AudioServer::get_singleton()->voice_play(v.voice_rid,v.sample_rid);
+
+ float volume_scale = Math::db2linear(v.volume_scale)*Math::db2linear(source->params[SOURCE_PARAM_VOLUME_DB]);
+ float volume = volume_attenuation*volume_scale;
+ reverb_send*=volume_scale;
+ int mix_rate = v.sample_mix_rate*v.pitch_scale*pitch_scale*source->params[SOURCE_PARAM_PITCH_SCALE];
+
+ if (mix_rate<=0) {
+
+ ERR_PRINT("Invalid mix rate for voice (0) check for invalid pitch_scale param.");
+ to_disable.push_back(ActiveVoice(source,voice)); // oh well..
+ continue; //invalid mix rate, disabling
+ }
+ if (v.restart || v.last_volume!=volume)
+ AudioServer::get_singleton()->voice_set_volume(v.voice_rid,volume);
+ if (v.restart || v.last_mix_rate!=mix_rate)
+ AudioServer::get_singleton()->voice_set_mix_rate(v.voice_rid,mix_rate);
+ if (v.restart || v.last_filter_gain!=air_absorption || v.last_filter_cutoff!=air_absorption_hf_cutoff)
+ AudioServer::get_singleton()->voice_set_filter(v.voice_rid,AudioServer::FILTER_HIGH_SHELF,air_absorption_hf_cutoff,1.0,air_absorption);
+ if (v.restart || v.last_panning!=panning)
+ AudioServer::get_singleton()->voice_set_pan(v.voice_rid,panning.x,panning.y,panning.z);
+ if (v.restart || v.last_reverb_room!=reverb_room || v.last_reverb_send!=reverb_send)
+ AudioServer::get_singleton()->voice_set_reverb(v.voice_rid,AudioServer::ReverbRoomType(reverb_room),reverb_send);
+
+ v.last_volume=volume;
+ v.last_mix_rate=mix_rate;
+ v.last_filter_gain=air_absorption;
+ v.last_filter_cutoff=air_absorption_hf_cutoff;
+ v.last_panning=panning;
+ v.restart=false;
+ v.active=true;
+
+ if (!AudioServer::get_singleton()->voice_is_active(v.voice_rid))
+ to_disable.push_back(ActiveVoice(source,voice)); // oh well..
+ }
+ }
+
+ while(to_disable.size()) {
+
+ ActiveVoice av = to_disable.front()->get();
+ av.source->voices[av.voice].active=false;
+ av.source->voices[av.voice].restart=false;
+ active_voices.erase(av);
+ to_disable.pop_front();
+ }
+
+}
+void SpatialSoundServerSW::finish() {
+
+ AudioServer::get_singleton()->free(internal_audio_stream_rid);
+ memdelete(internal_audio_stream);
+
+ _clean_up_owner(&source_owner,"Source");
+ _clean_up_owner(&listener_owner,"Listener");
+ _clean_up_owner(&room_owner,"Room");
+ _clean_up_owner(&space_owner,"Space");
+
+ memdelete_arr(internal_buffer);
+}
+
+SpatialSoundServerSW::SpatialSoundServerSW() {
+
+}
diff --git a/servers/spatial_sound/spatial_sound_server_sw.h b/servers/spatial_sound/spatial_sound_server_sw.h
new file mode 100644
index 0000000000..82b1b5fa8e
--- /dev/null
+++ b/servers/spatial_sound/spatial_sound_server_sw.h
@@ -0,0 +1,249 @@
+/*************************************************/
+/* spatial_sound_server_sw.h */
+/*************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/*************************************************/
+/* Source code within this file is: */
+/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
+/* All Rights Reserved. */
+/*************************************************/
+
+#ifndef SPATIAL_SOUND_SERVER_SW_H
+#define SPATIAL_SOUND_SERVER_SW_H
+
+#include "servers/spatial_sound_server.h"
+#include "octree.h"
+#include "os/thread_safe.h"
+
+
+class SpatialSoundServerSW : public SpatialSoundServer {
+
+ OBJ_TYPE(SpatialSoundServerSW,SpatialSoundServer);
+
+ _THREAD_SAFE_CLASS_
+
+ enum {
+ MAX_CULL_ROOMS=128,
+ INTERNAL_BUFFER_SIZE=4096,
+ INTERNAL_BUFFER_MAX_CHANNELS=4,
+ VOICE_IS_STREAM=-1
+
+ };
+
+
+ struct InternalAudioStream : public AudioServer::AudioStream {
+
+ ::SpatialSoundServerSW *owner;
+ virtual int get_channel_count() const;
+ virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate
+ virtual bool mix(int32_t *p_buffer,int p_frames);
+ virtual void update();
+ };
+
+ InternalAudioStream *internal_audio_stream;
+ RID internal_audio_stream_rid;
+ int32_t *internal_buffer;
+ int internal_buffer_channels;
+
+ bool internal_buffer_mix(int32_t *p_buffer,int p_frames);
+
+ struct Room;
+
+ struct Space {
+
+ RID default_room;
+ Set<RID> rooms;
+ Set<RID> sources;
+ Set<RID> listeners;
+
+ Octree<Room> octree;
+ };
+
+ mutable RID_Owner<Space> space_owner;
+
+ struct Room {
+ RID space;
+ Transform transform;
+ Transform inverse_transform;
+ BSP_Tree bounds;
+ RoomReverb reverb;
+ float params[ROOM_PARAM_MAX];
+ bool override_other_sources;
+ OctreeElementID octree_id;
+ int level;
+
+ Room();
+ };
+
+ mutable RID_Owner<Room> room_owner;
+
+
+
+ struct Source {
+
+ struct Voice {
+
+ RID voice_rid;
+ RID sample_rid;
+ bool active;
+ bool restart;
+ float pitch_scale;
+ float volume_scale;
+ int sample_mix_rate;
+
+
+ float last_volume;
+ float last_filter_gain;
+ float last_filter_cutoff;
+ Vector3 last_panning;
+ int last_mix_rate;
+ RoomReverb last_reverb_room;
+ float last_reverb_send;
+
+ Voice();
+ ~Voice();
+ };
+
+ struct StreamData {
+
+
+ Vector3 panning;
+ RoomReverb reverb;
+ float reverb_send;
+ float volume;
+ float filter_gain;
+ float filter_cutoff;
+
+ struct FilterState {
+
+ float ha[2];
+ float hb[2];
+ } filter_state[4];
+
+ StreamData() {
+
+ reverb_send=0;
+ reverb=ROOM_REVERB_HALL;
+ volume=1.0;
+ filter_gain=1;
+ filter_cutoff=5000;
+
+ }
+ } stream_data;
+
+ RID space;
+ Transform transform;
+ float params[SOURCE_PARAM_MAX];
+ AudioServer::AudioStream *stream;
+ Vector<Voice> voices;
+ int last_voice;
+
+ Source();
+ };
+
+ mutable RID_Owner<Source> source_owner;
+
+ struct Listener {
+
+ RID space;
+ Transform transform;
+ float params[LISTENER_PARAM_MAX];
+
+ Listener();
+ };
+
+ mutable RID_Owner<Listener> listener_owner;
+
+ struct ActiveVoice {
+
+ Source *source;
+ int voice;
+ bool operator<(const ActiveVoice& p_voice) const { return (voice==p_voice.voice)?(source<p_voice.source):(voice<p_voice.voice); }
+ ActiveVoice(Source *p_source=NULL,int p_voice=0) { source=p_source; voice=p_voice; }
+ };
+
+ Room *cull_rooms[MAX_CULL_ROOMS];
+
+ Set<Source*> streaming_sources;
+ Set<ActiveVoice> active_voices;
+
+ void _clean_up_owner(RID_OwnerBase *p_owner, const char *p_area);
+ void _update_sources();
+
+public:
+
+ /* SPACE */
+ virtual RID space_create();
+
+ /* ROOM */
+
+ virtual RID room_create();
+ virtual void room_set_space(RID p_room,RID p_space);
+ virtual RID room_get_space(RID p_room) const;
+
+ virtual void room_set_bounds(RID p_room, const BSP_Tree& p_bounds);
+ virtual BSP_Tree room_get_bounds(RID p_room) const;
+ virtual void room_set_transform(RID p_room, const Transform& p_transform);
+ virtual Transform room_get_transform(RID p_room) const;
+
+
+ virtual void room_set_param(RID p_room, RoomParam p_param, float p_value);
+ virtual float room_get_param(RID p_room, RoomParam p_param) const;
+
+ virtual void room_set_level(RID p_room, int p_level);
+ virtual int room_get_level(RID p_room) const;
+
+ virtual void room_set_reverb(RID p_room, RoomReverb p_reverb);
+ virtual RoomReverb room_get_reverb(RID p_room) const;
+
+ //useful for underwater or rooms with very strange conditions
+ virtual void room_set_force_params_to_all_sources(RID p_room, bool p_force);
+ virtual bool room_is_forcing_params_to_all_sources(RID p_room) const;
+
+ /* SOURCE */
+
+ virtual RID source_create(RID p_space);
+
+ virtual void source_set_polyphony(RID p_source,int p_voice_count);
+ virtual int source_get_polyphony(RID p_source) const;
+
+ virtual void source_set_transform(RID p_source, const Transform& p_transform);
+ virtual Transform source_get_transform(RID p_source) const;
+
+ virtual void source_set_param(RID p_source, SourceParam p_param, float p_value);
+ virtual float source_get_param(RID p_source, SourceParam p_param) const;
+
+ virtual void source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream); //null to unset
+ virtual SourceVoiceID source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice=SOURCE_NEXT_VOICE);
+ /* VOICES */
+ virtual void source_voice_set_pitch_scale(RID p_source, SourceVoiceID p_voice, float p_pitch_scale);
+ virtual void source_voice_set_volume_scale_db(RID p_source, SourceVoiceID p_voice, float p_volume);
+
+ virtual bool source_is_voice_active(RID p_source, SourceVoiceID p_voice) const;
+ virtual void source_stop_voice(RID p_source, SourceVoiceID p_voice);
+
+ /* LISTENER */
+
+ virtual RID listener_create();
+ virtual void listener_set_space(RID p_listener, RID p_space);
+
+ virtual void listener_set_transform(RID p_listener, const Transform& p_transform);
+ virtual Transform listener_get_transform(RID p_listener) const;
+
+ virtual void listener_set_param(RID p_listener, ListenerParam p_param, float p_value);
+ virtual float listener_get_param(RID p_listener, ListenerParam p_param) const;
+
+
+ /* MISC */
+
+ virtual void free(RID p_id);
+
+ virtual void init();
+ virtual void update(float p_delta);
+ virtual void finish();
+
+ SpatialSoundServerSW();
+};
+
+#endif // SPATIAL_SOUND_SERVER_SW_H
diff --git a/servers/spatial_sound_2d/SCsub b/servers/spatial_sound_2d/SCsub
new file mode 100644
index 0000000000..16fe3a59ac
--- /dev/null
+++ b/servers/spatial_sound_2d/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/servers/spatial_sound_2d/spatial_sound_2d_server_sw.cpp b/servers/spatial_sound_2d/spatial_sound_2d_server_sw.cpp
new file mode 100644
index 0000000000..e3c8aba758
--- /dev/null
+++ b/servers/spatial_sound_2d/spatial_sound_2d_server_sw.cpp
@@ -0,0 +1,1059 @@
+/*************************************************************************/
+/* spatial_sound_2d_server_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "spatial_sound_2d_server_sw.h"
+
+#include "os/os.h"
+#include "servers/audio/audio_filter_sw.h"
+
+
+
+int SpatialSound2DServerSW::InternalAudioStream::get_channel_count() const {
+
+ return AudioServer::get_singleton()->get_default_channel_count();
+}
+
+void SpatialSound2DServerSW::InternalAudioStream::set_mix_rate(int p_rate) {
+
+
+}
+
+void SpatialSound2DServerSW::InternalAudioStream::update() {
+
+ owner->_update_sources();
+}
+
+bool SpatialSound2DServerSW::InternalAudioStream::mix(int32_t *p_buffer,int p_frames) {
+
+ return owner->internal_buffer_mix(p_buffer,p_frames);
+}
+
+void SpatialSound2DServerSW::_update_sources() {
+
+ _THREAD_SAFE_METHOD_
+ for (Set<Source*>::Element *E=streaming_sources.front();E;E=E->next()) {
+
+ Source *s=E->get();
+ ERR_CONTINUE(!s->stream);
+ s->stream->update();
+ }
+}
+
+
+SpatialSound2DServerSW::Room::Room() {
+
+// params[ROOM_PARAM_SPEED_OF_SOUND]=343.0;
+ params[ROOM_PARAM_PITCH_SCALE]=1.0;
+ params[ROOM_PARAM_VOLUME_SCALE_DB]=0;
+ params[ROOM_PARAM_REVERB_SEND]=0;
+ params[ROOM_PARAM_CHORUS_SEND]=0;
+ params[ROOM_PARAM_ATTENUATION_SCALE]=1.0;
+ params[ROOM_PARAM_ATTENUATION_HF_CUTOFF]=5000;
+ params[ROOM_PARAM_ATTENUATION_HF_FLOOR_DB]=-24.0;
+ params[ROOM_PARAM_ATTENUATION_HF_RATIO_EXP]=1.0;
+ params[ROOM_PARAM_ATTENUATION_REVERB_SCALE]=0.0;
+ override_other_sources=false;
+ reverb=ROOM_REVERB_HALL;
+// octree_id=0;
+ level=-1;
+
+
+}
+
+
+SpatialSound2DServerSW::Source::Source() {
+
+ params[SOURCE_PARAM_VOLUME_DB]=0.0;
+ params[SOURCE_PARAM_PITCH_SCALE]=1.0;
+ params[SOURCE_PARAM_ATTENUATION_MIN_DISTANCE]=1;
+ params[SOURCE_PARAM_ATTENUATION_MAX_DISTANCE]=100;
+ params[SOURCE_PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good)
+ stream=NULL;
+ voices.resize(1);
+ last_voice=0;
+}
+
+SpatialSound2DServerSW::Source::Voice::Voice() {
+
+ active=false;
+ restart=false;
+ pitch_scale=1.0;
+ volume_scale=0.0;
+ voice_rid=AudioServer::get_singleton()->voice_create();
+
+}
+SpatialSound2DServerSW::Source::Voice::~Voice() {
+
+ AudioServer::get_singleton()->free(voice_rid);
+}
+
+
+SpatialSound2DServerSW::Listener::Listener() {
+
+ params[LISTENER_PARAM_VOLUME_SCALE_DB]=0.0;
+ params[LISTENER_PARAM_PITCH_SCALE]=1.0;
+ params[LISTENER_PARAM_ATTENUATION_SCALE]=1.0;
+ params[LISTENER_PARAM_PAN_RANGE]=128;
+
+}
+
+/* SPACE */
+RID SpatialSound2DServerSW::space_create() {
+
+ Space* space = memnew( Space );
+ RID space_rid = space_owner.make_rid(space);
+ space->default_room=room_create();
+ room_set_space(space->default_room,space_rid);
+ return space_rid;
+}
+
+/* ROOM */
+
+RID SpatialSound2DServerSW::room_create() {
+
+ Room *room = memnew( Room );
+ return room_owner.make_rid(room);
+}
+
+void SpatialSound2DServerSW::room_set_space(RID p_room,RID p_space) {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+
+ if (room->space.is_valid()) {
+
+ Space *space = space_owner.get(room->space);
+ space->rooms.erase(p_room);
+// space->octree.erase(room->octree_id);
+ //room->octree_id=0;
+ }
+
+ room->space=RID();
+
+ if (p_space.is_valid()) {
+
+ Space *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ space->rooms.insert(p_room);
+// room->octree_id=space->octree.create(room,AABB());
+ //set bounds
+// AABB aabb = room->bounds.is_empty()?AABB():room->bounds.get_aabb();
+ // space->octree.move(room->octree_id,room->transform.xform(aabb));
+ room->space=p_space;
+ }
+
+
+}
+
+RID SpatialSound2DServerSW::room_get_space(RID p_room) const {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,RID());
+
+
+ return room->space;
+}
+
+
+
+void SpatialSound2DServerSW::room_set_bounds(RID p_room, const DVector<Point2>& p_bounds) {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+
+ room->bounds=p_bounds;
+
+ if (!room->space.is_valid())
+ return;
+
+// AABB aabb = room->bounds.is_empty()?AABB():room->bounds.get_aabb();
+// Space* space = space_owner.get(room->space);
+// ERR_FAIL_COND(!space);
+
+// space->octree.move(room->octree_id,room->transform.xform(aabb));
+
+}
+DVector<Point2> SpatialSound2DServerSW::room_get_bounds(RID p_room) const {
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,DVector<Point2>());
+
+ return room->bounds;
+}
+
+void SpatialSound2DServerSW::room_set_transform(RID p_room, const Matrix32& p_transform) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->transform=p_transform;
+ room->inverse_transform=p_transform.affine_inverse(); // needs to be done to unscale BSP properly
+
+ if (!room->space.is_valid())
+ return;
+
+ /*
+ if (!room->bounds.is_empty()) {
+
+ Space* space = space_owner.get(room->space);
+ ERR_FAIL_COND(!space);
+
+ //space->octree.move(room->octree_id,room->transform.xform(room->bounds.get_aabb()));
+ }*/
+}
+
+Matrix32 SpatialSound2DServerSW::room_get_transform(RID p_room) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,Matrix32());
+ return room->transform;
+}
+
+
+void SpatialSound2DServerSW::room_set_param(RID p_room, RoomParam p_param, float p_value) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ ERR_FAIL_INDEX(p_param,ROOM_PARAM_MAX);
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->params[p_param]=p_value;
+
+}
+float SpatialSound2DServerSW::room_get_param(RID p_room, RoomParam p_param) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ ERR_FAIL_INDEX_V(p_param,ROOM_PARAM_MAX,0);
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,0);
+ return room->params[p_param];
+}
+
+void SpatialSound2DServerSW::room_set_level(RID p_room, int p_level) {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->level =p_level;
+
+}
+
+int SpatialSound2DServerSW::room_get_level(RID p_room) const {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,0);
+ return room->level;
+
+}
+
+
+void SpatialSound2DServerSW::room_set_reverb(RID p_room, RoomReverb p_reverb) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->reverb=p_reverb;
+
+}
+SpatialSound2DServerSW::RoomReverb SpatialSound2DServerSW::room_get_reverb(RID p_room) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,ROOM_REVERB_SMALL);
+ return room->reverb;
+}
+
+//useful for underwater or rooms with very strange conditions
+void SpatialSound2DServerSW::room_set_force_params_to_all_sources(RID p_room, bool p_force) {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->override_other_sources=p_force;
+
+}
+bool SpatialSound2DServerSW::room_is_forcing_params_to_all_sources(RID p_room) const {
+
+ if (space_owner.owns(p_room))
+ p_room=space_owner.get(p_room)->default_room;
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room,false);
+ return room->override_other_sources;
+}
+
+/* SOURCE */
+
+RID SpatialSound2DServerSW::source_create(RID p_space) {
+
+ Space *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space,RID());
+
+ Source *source = memnew( Source );
+ source->space=p_space;
+ RID source_rid = source_owner.make_rid(source);
+ space->sources.insert(source_rid);
+
+ return source_rid;
+}
+
+
+void SpatialSound2DServerSW::source_set_polyphony(RID p_source,int p_voice_count) {
+
+
+ ERR_FAIL_COND(p_voice_count<=0); // more than 32 is too much, change this if you really need more
+ if (p_voice_count>32) {
+
+ ERR_PRINT("Voices will be clipped to 32");
+ p_voice_count=32;
+ }
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+
+ if (p_voice_count<source->voices.size()) {
+
+ for(int i=p_voice_count;i<source->voices.size();i++) {
+ active_voices.erase(ActiveVoice(source,i)); //erase from active voices
+ }
+ }
+ source->voices.resize(p_voice_count);
+
+}
+
+int SpatialSound2DServerSW::source_get_polyphony(RID p_source) const {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,-1);
+ return source->voices.size();
+
+}
+
+void SpatialSound2DServerSW::source_set_transform(RID p_source, const Matrix32& p_transform) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ source->transform=p_transform;
+ source->transform.orthonormalize();
+}
+Matrix32 SpatialSound2DServerSW::source_get_transform(RID p_source) const {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,Matrix32());
+ return source->transform;
+}
+
+void SpatialSound2DServerSW::source_set_param(RID p_source, SourceParam p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,SOURCE_PARAM_MAX);
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ source->params[p_param]=p_value;
+
+}
+float SpatialSound2DServerSW::source_get_param(RID p_source, SourceParam p_param) const {
+ ERR_FAIL_INDEX_V(p_param,SOURCE_PARAM_MAX,0);
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,0);
+ return source->params[p_param];
+
+
+}
+
+void SpatialSound2DServerSW::source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ AudioServer::get_singleton()->lock();
+ source->stream=p_stream;
+ _THREAD_SAFE_METHOD_
+
+ if (!p_stream) {
+ streaming_sources.erase(source);
+ active_voices.erase(ActiveVoice(source,VOICE_IS_STREAM));
+ } else {
+ streaming_sources.insert(source);
+ active_voices.insert(ActiveVoice(source,VOICE_IS_STREAM));
+ zeromem(source->stream_data.filter_state,sizeof(Source::StreamData::FilterState)*4); //reset filter for safetyness
+ p_stream->set_mix_rate(AudioServer::get_singleton()->get_default_mix_rate());
+ }
+
+ AudioServer::get_singleton()->unlock();
+
+} //null to unset
+
+SpatialSound2DServer::SourceVoiceID SpatialSound2DServerSW::source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,SOURCE_INVALID_VOICE);
+
+ int to_play=0;
+
+ if (p_voice==SOURCE_NEXT_VOICE) {
+ to_play=source->last_voice+1;
+ if (to_play>=source->voices.size())
+ to_play=0;
+
+ } else
+ to_play=p_voice;
+
+ ERR_FAIL_INDEX_V(to_play,source->voices.size(),SOURCE_INVALID_VOICE);
+
+ source->voices[to_play].restart=true;
+ source->voices[to_play].sample_rid=p_sample;
+ source->voices[to_play].sample_mix_rate=p_mix_rate;
+ source->voices[to_play].pitch_scale=1;
+ source->voices[to_play].volume_scale=0;
+ source->last_voice=to_play;
+ active_voices.insert(ActiveVoice(source,to_play));
+ return to_play;
+}
+
+/* VOICES */
+void SpatialSound2DServerSW::source_voice_set_pitch_scale(RID p_source, SourceVoiceID p_voice, float p_pitch_scale) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ ERR_FAIL_INDEX(p_voice,source->voices.size());
+ source->voices[p_voice].pitch_scale=p_pitch_scale;
+
+}
+void SpatialSound2DServerSW::source_voice_set_volume_scale_db(RID p_source, SourceVoiceID p_voice, float p_db) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ ERR_FAIL_INDEX(p_voice,source->voices.size());
+ source->voices[p_voice].volume_scale=p_db;
+
+}
+
+bool SpatialSound2DServerSW::source_is_voice_active(RID p_source, SourceVoiceID p_voice) const {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND_V(!source,false);
+ ERR_FAIL_INDEX_V(p_voice,source->voices.size(),false);
+ return source->voices[p_voice].active || source->voices[p_voice].restart;
+
+}
+void SpatialSound2DServerSW::source_stop_voice(RID p_source, SourceVoiceID p_voice) {
+
+ Source *source = source_owner.get(p_source);
+ ERR_FAIL_COND(!source);
+ ERR_FAIL_INDEX(p_voice,source->voices.size());
+ if (source->voices[p_voice].active) {
+ AudioServer::get_singleton()->voice_stop(source->voices[p_voice].voice_rid);
+ }
+ source->voices[p_voice].active=false;
+ source->voices[p_voice].restart=false;
+ active_voices.erase(ActiveVoice(source,p_voice));
+}
+
+/* LISTENER */
+
+RID SpatialSound2DServerSW::listener_create() {
+
+ Listener *listener = memnew( Listener );
+ RID listener_rid = listener_owner.make_rid(listener);
+ return listener_rid;
+
+}
+
+void SpatialSound2DServerSW::listener_set_space(RID p_listener,RID p_space) {
+
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND(!listener);
+
+ if (listener->space.is_valid()) {
+
+ Space *lspace = space_owner.get(listener->space);
+ ERR_FAIL_COND(!lspace);
+ lspace->listeners.erase(p_listener);
+ }
+
+ listener->space=RID();
+
+ if (p_space.is_valid()) {
+ Space *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+
+ listener->space=p_space;
+ space->listeners.insert(p_listener);
+ }
+
+}
+
+void SpatialSound2DServerSW::listener_set_transform(RID p_listener, const Matrix32& p_transform) {
+
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND(!listener);
+ listener->transform=p_transform;
+ listener->transform.orthonormalize(); //must be done..
+}
+Matrix32 SpatialSound2DServerSW::listener_get_transform(RID p_listener) const {
+
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND_V(!listener,Matrix32());
+ return listener->transform;
+}
+
+void SpatialSound2DServerSW::listener_set_param(RID p_listener, ListenerParam p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,LISTENER_PARAM_MAX);
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND(!listener);
+ listener->params[p_param]=p_value;
+}
+
+float SpatialSound2DServerSW::listener_get_param(RID p_listener, ListenerParam p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param,LISTENER_PARAM_MAX,0);
+ Listener *listener = listener_owner.get(p_listener);
+ ERR_FAIL_COND_V(!listener,0);
+ return listener->params[p_param];
+}
+
+
+/* MISC */
+
+void SpatialSound2DServerSW::free(RID p_id) {
+
+
+ if (space_owner.owns(p_id)) {
+
+ Space *space = space_owner.get(p_id);
+ free(space->default_room);
+
+ while(space->listeners.size()) {
+ listener_set_space(space->listeners.front()->get(),RID());
+ }
+ while(space->sources.size()) {
+ free(space->sources.front()->get());
+ }
+ while(space->rooms.size()) {
+ room_set_space(space->rooms.front()->get(),RID());
+ }
+ space_owner.free(p_id);
+ memdelete(space);
+
+ } else if (source_owner.owns(p_id)) {
+
+ Source *source = source_owner.get(p_id);
+ if (source->stream)
+ source_set_audio_stream(p_id,NULL);
+
+ Space *space = space_owner.get(source->space);
+ ERR_FAIL_COND(!space);
+ space->sources.erase(p_id);
+ for(int i=0;i<source->voices.size();i++) {
+ active_voices.erase(ActiveVoice(source,i));
+ }
+ source_owner.free(p_id);
+ memdelete(source);
+ } else if (listener_owner.owns(p_id)) {
+
+ Listener *listener = listener_owner.get(p_id);
+ if (listener->space.is_valid()) {
+ Space *space = space_owner.get(listener->space);
+ ERR_FAIL_COND(!space);
+ space->listeners.erase(p_id);
+ }
+ listener_owner.free(p_id);
+ memdelete(listener);
+
+ } else if (room_owner.owns(p_id)) {
+
+ Room *room = room_owner.get(p_id);
+
+ if (room->space.is_valid()) {
+ Space *space = space_owner.get(room->space);
+ ERR_FAIL_COND(!space);
+// space->octree.erase(room->octree_id);
+ space->rooms.erase(p_id);
+ }
+ room_owner.free(p_id);
+ memdelete(room);
+ } else {
+ ERR_PRINT("Attempt to free invalid ID") ;
+ }
+
+}
+
+void SpatialSound2DServerSW::_clean_up_owner(RID_OwnerBase *p_owner, const char *p_area) {
+
+ List<RID> rids;
+ p_owner->get_owned_list(&rids);
+
+ for(List<RID>::Element *I=rids.front();I;I=I->next()) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+
+ print_line("Leaked RID ("+itos(I->get().get_id())+") of type "+String(p_area));
+ }
+ free(I->get());
+ }
+}
+
+void SpatialSound2DServerSW::init() {
+
+ internal_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE*INTERNAL_BUFFER_MAX_CHANNELS);
+ internal_buffer_channels=AudioServer::get_singleton()->get_default_channel_count();
+
+ internal_audio_stream = memnew( InternalAudioStream );
+ internal_audio_stream->owner=this;
+ internal_audio_stream_rid = AudioServer::get_singleton()->audio_stream_create(internal_audio_stream);
+
+ AudioServer::get_singleton()->stream_set_active(internal_audio_stream_rid,true);
+
+}
+
+
+
+bool SpatialSound2DServerSW::internal_buffer_mix(int32_t *p_buffer,int p_frames) {
+
+ if (streaming_sources.size()==0)
+ return false; //nothing to mix
+
+
+ for (Set<Source*>::Element *E=streaming_sources.front();E;E=E->next()) {
+
+ Source *s=E->get();
+ ERR_CONTINUE(!s->stream);
+
+ int channels = s->stream->get_channel_count();
+ Source::StreamData &sd=s->stream_data;
+
+ int todo=p_frames;
+
+ AudioFilterSW filter;
+ filter.set_sampling_rate(AudioServer::get_singleton()->get_default_mix_rate());
+ filter.set_cutoff(sd.filter_cutoff);
+ filter.set_gain(sd.filter_gain);
+ filter.set_resonance(1);
+ filter.set_mode(AudioFilterSW::HIGHSHELF);
+ filter.set_stages(1);
+ AudioFilterSW::Coeffs coefs;
+ filter.prepare_coefficients(&coefs);
+
+ int32_t in[4];
+#ifndef SPATIAL_SOUND_SERVER_NO_FILTER
+#define DO_FILTER(m_c)\
+ {\
+ float val = in[m_c];\
+ float pre=val;\
+ val = val*coefs.b0 + sd.filter_state[m_c].hb[0]*coefs.b1 + sd.filter_state[m_c].hb[1]*coefs.b2 + sd.filter_state[m_c].ha[0]*coefs.a1 + sd.filter_state[m_c].ha[1]*coefs.a2;\
+ sd.filter_state[m_c].ha[1]=sd.filter_state[m_c].ha[0];\
+ sd.filter_state[m_c].hb[1]=sd.filter_state[m_c].hb[0]; \
+ sd.filter_state[m_c].hb[0]=pre;\
+ sd.filter_state[m_c].ha[0]=val;\
+ in[m_c]=Math::fast_ftoi(val);\
+ }
+#else
+#define DO_FILTER(m_c)
+#endif
+
+ while(todo) {
+
+ int to_mix=MIN(todo,INTERNAL_BUFFER_SIZE);
+
+ s->stream->mix(internal_buffer,to_mix);
+
+ switch(internal_buffer_channels) {
+
+ case 2: {
+
+ float p = sd.panning.x*0.5+0.5;
+ float panf[2]={ (1.0-p),p };
+ panf[0]*=sd.volume;
+ panf[1]*=sd.volume;
+
+ int32_t pan[2]={Math::fast_ftoi(panf[0]*(1<<16)),Math::fast_ftoi(panf[1]*(1<<16))};
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[i];
+ in[1]=internal_buffer[i];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[(i<<1)+0];
+ in[1]=internal_buffer[(i<<1)+1];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=(internal_buffer[(i<<2)+0]+internal_buffer[(i<<2)+2])>>1;
+ in[1]=(internal_buffer[(i<<2)+1]+internal_buffer[(i<<2)+3])>>1;
+ DO_FILTER(0);
+ DO_FILTER(1);
+ p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 4: {
+
+ float xp = sd.panning.x*0.5+0.5;
+ float yp = sd.panning.y*0.5+0.5;
+ float panf[4]={ (1.0-xp)*(1.0-yp),(xp)*(1.0-yp),(1.0-xp)*(yp),(xp)*(yp) };
+ panf[0]*=sd.volume;
+ panf[1]*=sd.volume;
+ panf[2]*=sd.volume;
+ panf[3]*=sd.volume;
+
+ int32_t pan[4]={
+ Math::fast_ftoi(panf[0]*(1<<16)),
+ Math::fast_ftoi(panf[1]*(1<<16)),
+ Math::fast_ftoi(panf[2]*(1<<16)),
+ Math::fast_ftoi(panf[3]*(1<<16))};
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[i];
+ in[1]=internal_buffer[i];
+ in[2]=internal_buffer[i];
+ in[3]=internal_buffer[i];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ DO_FILTER(2);
+ DO_FILTER(3);
+ p_buffer[(i<<2)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]);
+ p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]);
+ p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[(i<<1)+0];
+ in[1]=internal_buffer[(i<<1)+1];
+ in[2]=internal_buffer[(i<<1)+0];
+ in[3]=internal_buffer[(i<<1)+1];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ DO_FILTER(2);
+ DO_FILTER(3);
+ p_buffer[(i<<2)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]);
+ p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]);
+ p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<to_mix;i++) {
+
+ in[0]=internal_buffer[(i<<2)+0];
+ in[1]=internal_buffer[(i<<2)+1];
+ in[2]=internal_buffer[(i<<2)+2];
+ in[3]=internal_buffer[(i<<2)+3];
+ DO_FILTER(0);
+ DO_FILTER(1);
+ DO_FILTER(2);
+ DO_FILTER(3);
+ p_buffer[(i<<2)+0]=((in[0]>>16)*pan[0]);
+ p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]);
+ p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]);
+ p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 6: {
+
+
+ } break;
+ }
+ p_buffer+=to_mix*internal_buffer_channels;
+ todo-=to_mix;
+
+ }
+
+ }
+
+ return true;
+}
+
+void SpatialSound2DServerSW::update(float p_delta) {
+
+ List<ActiveVoice> to_disable;
+
+ for(Set<ActiveVoice>::Element *E=active_voices.front();E;E=E->next()) {
+
+ Source *source = E->get().source;
+ int voice = E->get().voice;
+
+ if (voice!=VOICE_IS_STREAM) {
+ Source::Voice &v=source->voices[voice];
+ ERR_CONTINUE(!v.active && !v.restart); // likely a bug...
+ }
+
+ //this could be optimized at some point... am not sure
+ Space *space=space_owner.get(source->space);
+ Room *room=room_owner.get(space->default_room);
+ int max_level=-0x80000000;
+ /*
+ int rooms_culled = space->octree.cull_point(source->transform.origin,cull_rooms,MAX_CULL_ROOMS);
+ for(int i=0;i<rooms_culled;i++) {
+
+ Room *r=cull_rooms[i];
+ ERR_CONTINUE( r->bounds.is_empty() ); // how did this happen??
+ if (r->level<=max_level) //ignore optimization (level too low)
+ continue;
+ Vector2 local_point = r->inverse_transform.xform(source->transform.origin);
+ if (!r->bounds.point_is_inside(local_point))
+ continue;
+ room=r;
+ max_level=r->level;
+
+ }
+ */
+
+ //compute mixing weights (support for multiple listeners in the same output)
+ float total_distance=0;
+ for(Set<RID>::Element *L=space->listeners.front();L;L=L->next()) {
+ Listener *listener=listener_owner.get(L->get());
+ float d = listener->transform.get_origin().distance_to(source->transform.get_origin());
+ if (d==0)
+ d=0.1;
+ total_distance+=d;
+ }
+
+ //compute spatialization variables, weighted according to distance
+ float volume_attenuation = 0.0;
+ float air_absorption_hf_cutoff = 0.0;
+ float air_absorption = 0.0;
+ float pitch_scale=0.0;
+ Vector2 panning;
+
+ for(Set<RID>::Element *L=space->listeners.front();L;L=L->next()) {
+
+ Listener *listener=listener_owner.get(L->get());
+
+ Vector2 rel_vector = -listener->transform.xform_inv(source->transform.get_origin());
+ //Vector2 source_rel_vector = source->transform.xform_inv(listener->transform.get_origin()).normalized();
+ float distance=rel_vector.length();
+ float weight = distance/total_distance;
+ float pscale=1.0;
+
+ float distance_scale=listener->params[LISTENER_PARAM_ATTENUATION_SCALE]*room->params[ROOM_PARAM_ATTENUATION_SCALE];
+ float distance_min=source->params[SOURCE_PARAM_ATTENUATION_MIN_DISTANCE]*distance_scale;
+ float distance_max=source->params[SOURCE_PARAM_ATTENUATION_MAX_DISTANCE]*distance_scale;
+ float attenuation_exp=source->params[SOURCE_PARAM_ATTENUATION_DISTANCE_EXP];
+ float attenuation=1;
+
+ if (distance_max>0) {
+ distance = CLAMP(distance,distance_min,distance_max);
+ attenuation = Math::pow(1.0 - ((distance - distance_min)/distance_max),CLAMP(attenuation_exp,0.001,16));
+ }
+
+ float hf_attenuation_cutoff = room->params[ROOM_PARAM_ATTENUATION_HF_CUTOFF];
+ float hf_attenuation_exp = room->params[ROOM_PARAM_ATTENUATION_HF_RATIO_EXP];
+ float hf_attenuation_floor = room->params[ROOM_PARAM_ATTENUATION_HF_FLOOR_DB];
+ float absorption=Math::db2linear(Math::lerp(hf_attenuation_floor,0,Math::pow(attenuation,hf_attenuation_exp)));
+
+ // source emission cone
+/* only for 3D
+ float emission_deg=source->params[SOURCE_PARAM_EMISSION_CONE_DEGREES];
+ float emission_attdb=source->params[SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB];
+ absorption*=_get_attenuation(source_rel_vector.dot(Vector2(0,0,-1)),emission_deg,emission_attdb);
+*/
+ Vector2 vpanning=rel_vector.normalized();
+ if (distance < listener->params[LISTENER_PARAM_PAN_RANGE])
+ vpanning*=distance/listener->params[LISTENER_PARAM_PAN_RANGE];
+
+ //listener stuff
+
+ {
+
+ // head cone
+/* only for 3D
+ float reception_deg=listener->params[LISTENER_PARAM_RECEPTION_CONE_DEGREES];
+ float reception_attdb=listener->params[LISTENER_PARAM_RECEPTION_CONE_ATTENUATION_DB];
+
+ absorption*=_get_attenuation(vpanning.dot(Vector2(0,0,-1)),reception_deg,reception_attdb);
+*/
+
+ // scale
+
+ attenuation*=Math::db2linear(listener->params[LISTENER_PARAM_VOLUME_SCALE_DB]);
+ pscale*=Math::db2linear(listener->params[LISTENER_PARAM_PITCH_SCALE]);
+
+
+ }
+
+
+
+
+ //add values
+
+ volume_attenuation+=weight*attenuation; // plus other stuff i guess
+ air_absorption+=weight*absorption;
+ air_absorption_hf_cutoff+=weight*hf_attenuation_cutoff;
+ panning+=vpanning*weight;
+ pitch_scale+=pscale*weight;
+
+ }
+
+ RoomReverb reverb_room=ROOM_REVERB_HALL;
+ float reverb_send=0;
+
+ /* APPLY ROOM SETTINGS */
+
+ {
+ pitch_scale*=room->params[ROOM_PARAM_PITCH_SCALE];
+ volume_attenuation*=Math::db2linear(room->params[ROOM_PARAM_VOLUME_SCALE_DB]);
+ reverb_room=room->reverb;
+ reverb_send=Math::lerp(1.0,volume_attenuation,room->params[ROOM_PARAM_ATTENUATION_REVERB_SCALE])*room->params[ROOM_PARAM_REVERB_SEND];
+
+ }
+
+ /* UPDATE VOICE & STREAM */
+
+
+
+ if (voice==VOICE_IS_STREAM) {
+
+ //update voice!!
+ source->stream_data.panning=panning;
+ source->stream_data.volume=volume_attenuation*Math::db2linear(source->params[SOURCE_PARAM_VOLUME_DB]);
+ source->stream_data.reverb=reverb_room;
+ source->stream_data.reverb_send=reverb_send;
+ source->stream_data.filter_gain=air_absorption;
+ source->stream_data.filter_cutoff=air_absorption_hf_cutoff;
+
+ if (!source->stream) //stream is gone bye bye
+ to_disable.push_back(ActiveVoice(source,voice)); // oh well..
+
+ } else if (voice>=0) {
+ //update stream!!
+ Source::Voice &v=source->voices[voice];
+
+ if (v.restart)
+ AudioServer::get_singleton()->voice_play(v.voice_rid,v.sample_rid);
+
+ float volume_scale = Math::db2linear(v.volume_scale)*Math::db2linear(source->params[SOURCE_PARAM_VOLUME_DB]);
+ float volume = volume_attenuation*volume_scale;
+ reverb_send*=volume_scale;
+ int mix_rate = v.sample_mix_rate*v.pitch_scale*pitch_scale*source->params[SOURCE_PARAM_PITCH_SCALE];
+
+
+ if (mix_rate<=0) {
+
+ ERR_PRINT("Invalid mix rate for voice (0) check for invalid pitch_scale param.");
+ to_disable.push_back(ActiveVoice(source,voice)); // oh well..
+ continue; //invalid mix rate, disabling
+ }
+ if (v.restart || v.last_volume!=volume)
+ AudioServer::get_singleton()->voice_set_volume(v.voice_rid,volume);
+ if (v.restart || v.last_mix_rate!=mix_rate)
+ AudioServer::get_singleton()->voice_set_mix_rate(v.voice_rid,mix_rate);
+ if (v.restart || v.last_filter_gain!=air_absorption || v.last_filter_cutoff!=air_absorption_hf_cutoff)
+ AudioServer::get_singleton()->voice_set_filter(v.voice_rid,AudioServer::FILTER_HIGH_SHELF,air_absorption_hf_cutoff,1.0,air_absorption);
+ if (v.restart || v.last_panning!=panning) {
+ AudioServer::get_singleton()->voice_set_pan(v.voice_rid,-panning.x,panning.y,0);
+ }
+ if (v.restart || v.last_reverb_room!=reverb_room || v.last_reverb_send!=reverb_send)
+ AudioServer::get_singleton()->voice_set_reverb(v.voice_rid,AudioServer::ReverbRoomType(reverb_room),reverb_send);
+
+ v.last_volume=volume;
+ v.last_mix_rate=mix_rate;
+ v.last_filter_gain=air_absorption;
+ v.last_filter_cutoff=air_absorption_hf_cutoff;
+ v.last_panning=panning;
+ v.last_reverb_room=reverb_room;
+ v.last_reverb_send=reverb_send;
+ v.restart=false;
+ v.active=true;
+
+ if (!AudioServer::get_singleton()->voice_is_active(v.voice_rid))
+ to_disable.push_back(ActiveVoice(source,voice)); // oh well..
+ }
+ }
+
+ while(to_disable.size()) {
+
+ ActiveVoice av = to_disable.front()->get();
+ av.source->voices[av.voice].active=false;
+ av.source->voices[av.voice].restart=false;
+ active_voices.erase(av);
+ to_disable.pop_front();
+ }
+
+}
+void SpatialSound2DServerSW::finish() {
+
+ AudioServer::get_singleton()->free(internal_audio_stream_rid);
+ memdelete(internal_audio_stream);
+
+ _clean_up_owner(&source_owner,"Source");
+ _clean_up_owner(&listener_owner,"Listener");
+ _clean_up_owner(&room_owner,"Room");
+ _clean_up_owner(&space_owner,"Space");
+
+ memdelete_arr(internal_buffer);
+}
+
+SpatialSound2DServerSW::SpatialSound2DServerSW() {
+
+}
diff --git a/servers/spatial_sound_2d/spatial_sound_2d_server_sw.h b/servers/spatial_sound_2d/spatial_sound_2d_server_sw.h
new file mode 100644
index 0000000000..6e7ac2157d
--- /dev/null
+++ b/servers/spatial_sound_2d/spatial_sound_2d_server_sw.h
@@ -0,0 +1,265 @@
+/*************************************************************************/
+/* spatial_sound_2d_server_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SPATIAL_SOUND_2D_SERVER_SW_H
+#define SPATIAL_SOUND_2D_SERVER_SW_H
+
+#include "servers/spatial_sound_2d_server.h"
+
+#include "os/thread_safe.h"
+
+
+class SpatialSound2DServerSW : public SpatialSound2DServer {
+
+ OBJ_TYPE(SpatialSound2DServerSW,SpatialSound2DServer);
+
+ _THREAD_SAFE_CLASS_
+
+ enum {
+ INTERNAL_BUFFER_SIZE=4096,
+ INTERNAL_BUFFER_MAX_CHANNELS=4,
+ VOICE_IS_STREAM=-1
+
+ };
+
+
+ struct InternalAudioStream : public AudioServer::AudioStream {
+
+ ::SpatialSound2DServerSW *owner;
+ virtual int get_channel_count() const;
+ virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate
+ virtual bool mix(int32_t *p_buffer,int p_frames);
+ virtual void update();
+ };
+
+ InternalAudioStream *internal_audio_stream;
+ RID internal_audio_stream_rid;
+ int32_t *internal_buffer;
+ int internal_buffer_channels;
+
+ bool internal_buffer_mix(int32_t *p_buffer,int p_frames);
+
+ struct Room;
+
+ struct Space {
+
+ RID default_room;
+ Set<RID> rooms;
+ Set<RID> sources;
+ Set<RID> listeners;
+
+ //Octree<Room> octree;
+ };
+
+ mutable RID_Owner<Space> space_owner;
+
+ struct Room {
+ RID space;
+ Matrix32 transform;
+ Matrix32 inverse_transform;
+ DVector<Point2> bounds;
+ RoomReverb reverb;
+ float params[ROOM_PARAM_MAX];
+ bool override_other_sources;
+ //OctreeElementID octree_id;
+ int level;
+
+ Room();
+ };
+
+ mutable RID_Owner<Room> room_owner;
+
+
+
+ struct Source {
+
+ struct Voice {
+
+ RID voice_rid;
+ RID sample_rid;
+ bool active;
+ bool restart;
+ float pitch_scale;
+ float volume_scale;
+ int sample_mix_rate;
+
+
+ float last_volume;
+ float last_filter_gain;
+ float last_filter_cutoff;
+ Vector2 last_panning;
+ int last_mix_rate;
+ RoomReverb last_reverb_room;
+ float last_reverb_send;
+
+ Voice();
+ ~Voice();
+ };
+
+ struct StreamData {
+
+
+ Vector2 panning;
+ RoomReverb reverb;
+ float reverb_send;
+ float volume;
+ float filter_gain;
+ float filter_cutoff;
+
+ struct FilterState {
+
+ float ha[2];
+ float hb[2];
+ } filter_state[4];
+
+ StreamData() {
+
+ reverb_send=0;
+ reverb=ROOM_REVERB_HALL;
+ volume=1.0;
+ filter_gain=1;
+ filter_cutoff=5000;
+
+ }
+ } stream_data;
+
+ RID space;
+ Matrix32 transform;
+ float params[SOURCE_PARAM_MAX];
+ AudioServer::AudioStream *stream;
+ Vector<Voice> voices;
+ int last_voice;
+
+ Source();
+ };
+
+ mutable RID_Owner<Source> source_owner;
+
+ struct Listener {
+
+ RID space;
+ Matrix32 transform;
+ float params[LISTENER_PARAM_MAX];
+
+ Listener();
+ };
+
+ mutable RID_Owner<Listener> listener_owner;
+
+ struct ActiveVoice {
+
+ Source *source;
+ int voice;
+ bool operator<(const ActiveVoice& p_voice) const { return (voice==p_voice.voice)?(source<p_voice.source):(voice<p_voice.voice); }
+ ActiveVoice(Source *p_source=NULL,int p_voice=0) { source=p_source; voice=p_voice; }
+ };
+
+// Room *cull_rooms[MAX_CULL_ROOMS];
+
+ Set<Source*> streaming_sources;
+ Set<ActiveVoice> active_voices;
+
+ void _clean_up_owner(RID_OwnerBase *p_owner, const char *p_area);
+ void _update_sources();
+
+public:
+
+ /* SPACE */
+ virtual RID space_create();
+
+ /* ROOM */
+
+ virtual RID room_create();
+ virtual void room_set_space(RID p_room,RID p_space);
+ virtual RID room_get_space(RID p_room) const;
+
+ virtual void room_set_bounds(RID p_room, const DVector<Point2>& p_bounds);
+ virtual DVector<Point2> room_get_bounds(RID p_room) const;
+ virtual void room_set_transform(RID p_room, const Matrix32& p_transform);
+ virtual Matrix32 room_get_transform(RID p_room) const;
+
+
+ virtual void room_set_param(RID p_room, RoomParam p_param, float p_value);
+ virtual float room_get_param(RID p_room, RoomParam p_param) const;
+
+ virtual void room_set_level(RID p_room, int p_level);
+ virtual int room_get_level(RID p_room) const;
+
+ virtual void room_set_reverb(RID p_room, RoomReverb p_reverb);
+ virtual RoomReverb room_get_reverb(RID p_room) const;
+
+ //useful for underwater or rooms with very strange conditions
+ virtual void room_set_force_params_to_all_sources(RID p_room, bool p_force);
+ virtual bool room_is_forcing_params_to_all_sources(RID p_room) const;
+
+ /* SOURCE */
+
+ virtual RID source_create(RID p_space);
+
+ virtual void source_set_polyphony(RID p_source,int p_voice_count);
+ virtual int source_get_polyphony(RID p_source) const;
+
+ virtual void source_set_transform(RID p_source, const Matrix32& p_transform);
+ virtual Matrix32 source_get_transform(RID p_source) const;
+
+ virtual void source_set_param(RID p_source, SourceParam p_param, float p_value);
+ virtual float source_get_param(RID p_source, SourceParam p_param) const;
+
+ virtual void source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream); //null to unset
+ virtual SourceVoiceID source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice=SOURCE_NEXT_VOICE);
+ /* VOICES */
+ virtual void source_voice_set_pitch_scale(RID p_source, SourceVoiceID p_voice, float p_pitch_scale);
+ virtual void source_voice_set_volume_scale_db(RID p_source, SourceVoiceID p_voice, float p_volume);
+
+ virtual bool source_is_voice_active(RID p_source, SourceVoiceID p_voice) const;
+ virtual void source_stop_voice(RID p_source, SourceVoiceID p_voice);
+
+ /* LISTENER */
+
+ virtual RID listener_create();
+ virtual void listener_set_space(RID p_listener, RID p_space);
+
+ virtual void listener_set_transform(RID p_listener, const Matrix32& p_transform);
+ virtual Matrix32 listener_get_transform(RID p_listener) const;
+
+ virtual void listener_set_param(RID p_listener, ListenerParam p_param, float p_value);
+ virtual float listener_get_param(RID p_listener, ListenerParam p_param) const;
+
+
+ /* MISC */
+
+ virtual void free(RID p_id);
+
+ virtual void init();
+ virtual void update(float p_delta);
+ virtual void finish();
+
+ SpatialSound2DServerSW();
+};
+
+#endif // SPATIAL_SOUND_2D_SERVER_SW_H
diff --git a/servers/spatial_sound_2d_server.cpp b/servers/spatial_sound_2d_server.cpp
new file mode 100644
index 0000000000..d27803559e
--- /dev/null
+++ b/servers/spatial_sound_2d_server.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* spatial_sound_2d_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "spatial_sound_2d_server.h"
+
+SpatialSound2DServer *SpatialSound2DServer::singleton=NULL;
+
+
+SpatialSound2DServer *SpatialSound2DServer::get_singleton() {
+
+ return singleton;
+}
+
+
+SpatialSound2DServer::SpatialSound2DServer() {
+
+ ERR_FAIL_COND(singleton!=NULL);
+ singleton=this;
+}
diff --git a/servers/spatial_sound_2d_server.h b/servers/spatial_sound_2d_server.h
new file mode 100644
index 0000000000..e3ed4ce00c
--- /dev/null
+++ b/servers/spatial_sound_2d_server.h
@@ -0,0 +1,164 @@
+/*************************************************************************/
+/* spatial_sound_2d_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SPATIAL_SOUND_2D_SERVER_H
+#define SPATIAL_SOUND_2D_SERVER_H
+
+#include "object.h"
+#include "bsp_tree.h"
+#include "servers/audio_server.h"
+
+class SpatialSound2DServer : public Object {
+
+ OBJ_TYPE(SpatialSound2DServer,Object);
+ static SpatialSound2DServer *singleton;
+public:
+
+
+ enum {
+ SOURCE_INVALID_VOICE=-1,
+ SOURCE_NEXT_VOICE=-2,
+ };
+
+ typedef int SourceVoiceID;
+
+ /* SPACE */
+ virtual RID space_create()=0;
+
+ /* ROOM */
+
+ virtual RID room_create()=0;
+ virtual void room_set_space(RID p_room,RID p_space)=0;
+ virtual RID room_get_space(RID p_room) const=0;
+
+
+ virtual void room_set_bounds(RID p_room, const DVector<Point2>& p_bounds)=0;
+ virtual DVector<Point2> room_get_bounds(RID p_room) const=0;
+ virtual void room_set_transform(RID p_room, const Matrix32& p_transform)=0;
+ virtual Matrix32 room_get_transform(RID p_room) const=0;
+
+ enum RoomParam {
+ ROOM_PARAM_PITCH_SCALE,
+ ROOM_PARAM_VOLUME_SCALE_DB,
+ ROOM_PARAM_REVERB_SEND,
+ ROOM_PARAM_CHORUS_SEND,
+ ROOM_PARAM_ATTENUATION_SCALE,
+ ROOM_PARAM_ATTENUATION_HF_CUTOFF,
+ ROOM_PARAM_ATTENUATION_HF_FLOOR_DB,
+ ROOM_PARAM_ATTENUATION_HF_RATIO_EXP,
+ ROOM_PARAM_ATTENUATION_REVERB_SCALE,
+ ROOM_PARAM_MAX
+ };
+
+ virtual void room_set_param(RID p_room, RoomParam p_param, float p_value)=0;
+ virtual float room_get_param(RID p_room, RoomParam p_param) const=0;
+
+ enum RoomReverb {
+ ROOM_REVERB_SMALL,
+ ROOM_REVERB_MEDIUM,
+ ROOM_REVERB_LARGE,
+ ROOM_REVERB_HALL
+ };
+
+ virtual void room_set_reverb(RID p_room, RoomReverb p_reverb)=0;
+ virtual RoomReverb room_get_reverb(RID p_room) const=0;
+
+ virtual void room_set_level(RID p_room, int p_level)=0;
+ virtual int room_get_level(RID p_room) const=0;
+
+ //useful for underwater or rooms with very strange conditions
+ virtual void room_set_force_params_to_all_sources(RID p_room, bool p_force)=0;
+ virtual bool room_is_forcing_params_to_all_sources(RID p_room) const=0;
+
+ /* SOURCE */
+
+ virtual RID source_create(RID p_space)=0;
+
+ virtual void source_set_transform(RID p_source, const Matrix32& p_transform)=0;
+ virtual Matrix32 source_get_transform(RID p_source) const=0;
+
+ virtual void source_set_polyphony(RID p_source,int p_voice_count)=0;
+ virtual int source_get_polyphony(RID p_source) const=0;
+
+ enum SourceParam {
+
+ SOURCE_PARAM_VOLUME_DB,
+ SOURCE_PARAM_PITCH_SCALE,
+ SOURCE_PARAM_ATTENUATION_MIN_DISTANCE,
+ SOURCE_PARAM_ATTENUATION_MAX_DISTANCE,
+ SOURCE_PARAM_ATTENUATION_DISTANCE_EXP,
+ SOURCE_PARAM_MAX
+ };
+
+ virtual void source_set_param(RID p_source, SourceParam p_param, float p_value)=0;
+ virtual float source_get_param(RID p_source, SourceParam p_param) const=0;
+
+ virtual void source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream)=0; //null to unset
+ virtual SourceVoiceID source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice=SOURCE_NEXT_VOICE)=0;
+ //voices
+ virtual void source_voice_set_pitch_scale(RID p_source,SourceVoiceID p_voice, float p_pitch_scale)=0;
+ virtual void source_voice_set_volume_scale_db(RID p_source,SourceVoiceID p_voice, float p_volume_db)=0;
+
+ virtual bool source_is_voice_active(RID p_source,SourceVoiceID p_voice) const=0;
+ virtual void source_stop_voice(RID p_source,SourceVoiceID p_voice)=0;
+
+ /* LISTENER */
+
+ enum ListenerParam {
+
+ LISTENER_PARAM_VOLUME_SCALE_DB,
+ LISTENER_PARAM_PITCH_SCALE,
+ LISTENER_PARAM_ATTENUATION_SCALE,
+ LISTENER_PARAM_PAN_RANGE,
+ LISTENER_PARAM_MAX
+ };
+
+ virtual RID listener_create()=0;
+ virtual void listener_set_space(RID p_listener, RID p_space)=0;
+
+ virtual void listener_set_transform(RID p_listener, const Matrix32& p_transform)=0;
+ virtual Matrix32 listener_get_transform(RID p_listener) const=0;
+
+ virtual void listener_set_param(RID p_listener, ListenerParam p_param, float p_value)=0;
+ virtual float listener_get_param(RID p_listener, ListenerParam p_param) const=0;
+
+ /* MISC */
+
+ virtual void free(RID p_id)=0;
+
+ virtual void init()=0;
+ virtual void update(float p_delta)=0;
+ virtual void finish()=0;
+
+ static SpatialSound2DServer *get_singleton();
+
+ SpatialSound2DServer();
+
+};
+
+#endif // SPATIAL_SOUND_2D_SERVER_H
diff --git a/servers/spatial_sound_server.cpp b/servers/spatial_sound_server.cpp
new file mode 100644
index 0000000000..1fcfb0008b
--- /dev/null
+++ b/servers/spatial_sound_server.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* spatial_sound_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "spatial_sound_server.h"
+
+SpatialSoundServer *SpatialSoundServer::singleton=NULL;
+
+
+SpatialSoundServer *SpatialSoundServer::get_singleton() {
+
+ return singleton;
+}
+
+
+SpatialSoundServer::SpatialSoundServer() {
+
+ ERR_FAIL_COND(singleton!=NULL);
+ singleton=this;
+}
diff --git a/servers/spatial_sound_server.h b/servers/spatial_sound_server.h
new file mode 100644
index 0000000000..ff4342a42b
--- /dev/null
+++ b/servers/spatial_sound_server.h
@@ -0,0 +1,168 @@
+/*************************************************************************/
+/* spatial_sound_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SPATIAL_SOUND_SERVER_H
+#define SPATIAL_SOUND_SERVER_H
+
+#include "object.h"
+#include "bsp_tree.h"
+#include "servers/audio_server.h"
+
+class SpatialSoundServer : public Object {
+ OBJ_TYPE(SpatialSoundServer,Object);
+
+ static SpatialSoundServer *singleton;
+public:
+
+
+ enum {
+ SOURCE_INVALID_VOICE=-1,
+ SOURCE_NEXT_VOICE=-2,
+ };
+
+ typedef int SourceVoiceID;
+
+ /* SPACE */
+ virtual RID space_create()=0;
+
+ /* ROOM */
+
+ virtual RID room_create()=0;
+ virtual void room_set_space(RID p_room,RID p_space)=0;
+ virtual RID room_get_space(RID p_room) const=0;
+
+
+ virtual void room_set_bounds(RID p_room, const BSP_Tree& p_bounds)=0;
+ virtual BSP_Tree room_get_bounds(RID p_room) const=0;
+ virtual void room_set_transform(RID p_room, const Transform& p_transform)=0;
+ virtual Transform room_get_transform(RID p_room) const=0;
+
+ enum RoomParam {
+ ROOM_PARAM_SPEED_OF_SOUND_SCALE,
+ ROOM_PARAM_DOPPLER_FACTOR,
+ ROOM_PARAM_PITCH_SCALE,
+ ROOM_PARAM_VOLUME_SCALE_DB,
+ ROOM_PARAM_REVERB_SEND,
+ ROOM_PARAM_CHORUS_SEND,
+ ROOM_PARAM_ATTENUATION_SCALE,
+ ROOM_PARAM_ATTENUATION_HF_CUTOFF,
+ ROOM_PARAM_ATTENUATION_HF_FLOOR_DB,
+ ROOM_PARAM_ATTENUATION_HF_RATIO_EXP,
+ ROOM_PARAM_ATTENUATION_REVERB_SCALE,
+ ROOM_PARAM_MAX
+ };
+
+ virtual void room_set_param(RID p_room, RoomParam p_param, float p_value)=0;
+ virtual float room_get_param(RID p_room, RoomParam p_param) const=0;
+
+ enum RoomReverb {
+ ROOM_REVERB_SMALL,
+ ROOM_REVERB_MEDIUM,
+ ROOM_REVERB_LARGE,
+ ROOM_REVERB_HALL
+ };
+
+ virtual void room_set_reverb(RID p_room, RoomReverb p_reverb)=0;
+ virtual RoomReverb room_get_reverb(RID p_room) const=0;
+
+ virtual void room_set_level(RID p_room, int p_level)=0;
+ virtual int room_get_level(RID p_room) const=0;
+
+ //useful for underwater or rooms with very strange conditions
+ virtual void room_set_force_params_to_all_sources(RID p_room, bool p_force)=0;
+ virtual bool room_is_forcing_params_to_all_sources(RID p_room) const=0;
+
+ /* SOURCE */
+
+ virtual RID source_create(RID p_space)=0;
+
+ virtual void source_set_transform(RID p_source, const Transform& p_transform)=0;
+ virtual Transform source_get_transform(RID p_source) const=0;
+
+ virtual void source_set_polyphony(RID p_source,int p_voice_count)=0;
+ virtual int source_get_polyphony(RID p_source) const=0;
+
+ enum SourceParam {
+
+ SOURCE_PARAM_VOLUME_DB,
+ SOURCE_PARAM_PITCH_SCALE,
+ SOURCE_PARAM_ATTENUATION_MIN_DISTANCE,
+ SOURCE_PARAM_ATTENUATION_MAX_DISTANCE,
+ SOURCE_PARAM_ATTENUATION_DISTANCE_EXP,
+ SOURCE_PARAM_EMISSION_CONE_DEGREES,
+ SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB,
+ SOURCE_PARAM_MAX
+ };
+
+ virtual void source_set_param(RID p_source, SourceParam p_param, float p_value)=0;
+ virtual float source_get_param(RID p_source, SourceParam p_param) const=0;
+
+ virtual void source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream)=0; //null to unset
+ virtual SourceVoiceID source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice=SOURCE_NEXT_VOICE)=0;
+ //voices
+ virtual void source_voice_set_pitch_scale(RID p_source,SourceVoiceID p_voice, float p_pitch_scale)=0;
+ virtual void source_voice_set_volume_scale_db(RID p_source,SourceVoiceID p_voice, float p_volume_db)=0;
+
+ virtual bool source_is_voice_active(RID p_source,SourceVoiceID p_voice) const=0;
+ virtual void source_stop_voice(RID p_source,SourceVoiceID p_voice)=0;
+
+ /* LISTENER */
+
+ enum ListenerParam {
+
+ LISTENER_PARAM_VOLUME_SCALE_DB,
+ LISTENER_PARAM_PITCH_SCALE,
+ LISTENER_PARAM_ATTENUATION_SCALE,
+ LISTENER_PARAM_RECEPTION_CONE_DEGREES,
+ LISTENER_PARAM_RECEPTION_CONE_ATTENUATION_DB,
+ LISTENER_PARAM_MAX
+ };
+
+ virtual RID listener_create()=0;
+ virtual void listener_set_space(RID p_listener, RID p_space)=0;
+
+ virtual void listener_set_transform(RID p_listener, const Transform& p_transform)=0;
+ virtual Transform listener_get_transform(RID p_listener) const=0;
+
+ virtual void listener_set_param(RID p_listener, ListenerParam p_param, float p_value)=0;
+ virtual float listener_get_param(RID p_listener, ListenerParam p_param) const=0;
+
+ /* MISC */
+
+ virtual void free(RID p_id)=0;
+
+ virtual void init()=0;
+ virtual void update(float p_delta)=0;
+ virtual void finish()=0;
+
+ static SpatialSoundServer *get_singleton();
+
+ SpatialSoundServer();
+};
+
+#endif // SPATIAL_SOUND_SERVER_H
diff --git a/servers/visual/SCsub b/servers/visual/SCsub
new file mode 100644
index 0000000000..16fe3a59ac
--- /dev/null
+++ b/servers/visual/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/servers/visual/default_mouse_cursor.xpm b/servers/visual/default_mouse_cursor.xpm
new file mode 100644
index 0000000000..37d437dd15
--- /dev/null
+++ b/servers/visual/default_mouse_cursor.xpm
@@ -0,0 +1,23 @@
+/* XPM */
+static const char * default_mouse_cursor_xpm[] = {
+"16 16 4 1",
+" c None",
+". c #000000",
+"+ c #FF00FF",
+"@ c #FFFFFF",
+"...+++++++++++++",
+".@...+++++++++++",
+".@@@...+++++++++",
+".@@@@@....++++++",
+".@@@@@@@@...++++",
+".@@@@@@@@@@...++",
+".@@@@@@@@@@@@..+",
+".@@@@@@@@@@@@@..",
+".@@@@@@@@@@@@..+",
+".@@@@@@@@@@@..++",
+".@@@@@@@@@...+++",
+".@@@.....@@..+++",
+".....+++.@@@..++",
+"++++++++..@@@..+",
+"+++++++++..@@@.+",
+"++++++++++.....+"};
diff --git a/servers/visual/particle_system_sw.cpp b/servers/visual/particle_system_sw.cpp
new file mode 100644
index 0000000000..49a68f8e80
--- /dev/null
+++ b/servers/visual/particle_system_sw.cpp
@@ -0,0 +1,412 @@
+/*************************************************************************/
+/* particle_system_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "particle_system_sw.h"
+#include "sort.h"
+
+
+ParticleSystemSW::ParticleSystemSW() {
+
+ amount=8;
+ emitting=true;
+
+ for (int i=0;i<VS::PARTICLE_VAR_MAX;i++) {
+ particle_randomness[i]=0.0;
+ }
+
+ particle_vars[VS::PARTICLE_LIFETIME]=2.0;//
+ particle_vars[VS::PARTICLE_SPREAD]=0.2;//
+ particle_vars[VS::PARTICLE_GRAVITY]=9.8;//
+ particle_vars[VS::PARTICLE_LINEAR_VELOCITY]=0.2;//
+ particle_vars[VS::PARTICLE_ANGULAR_VELOCITY]=0.0;//
+ particle_vars[VS::PARTICLE_LINEAR_ACCELERATION]=0.0;//
+ particle_vars[VS::PARTICLE_RADIAL_ACCELERATION]=0.0;//
+ particle_vars[VS::PARTICLE_TANGENTIAL_ACCELERATION]=1.0;//
+ particle_vars[VS::PARTICLE_DAMPING]=0.0;//
+ particle_vars[VS::PARTICLE_INITIAL_SIZE]=1.0;
+ particle_vars[VS::PARTICLE_FINAL_SIZE]=0.8;
+ particle_vars[VS::PARTICLE_HEIGHT]=1;
+ particle_vars[VS::PARTICLE_HEIGHT_SPEED_SCALE]=1;
+
+ height_from_velocity=false;
+ local_coordinates=false;
+
+ particle_vars[VS::PARTICLE_INITIAL_ANGLE]=0.0;//
+
+ gravity_normal=Vector3(0,-1.0,0);
+ //emission_half_extents=Vector3(0.1,0.1,0.1);
+ emission_half_extents=Vector3(1,1,1);
+ color_phase_count=0;
+ color_phases[0].pos=0.0;
+ color_phases[0].color=Color(1.0,0.0,0.0);
+ visibility_aabb=AABB(Vector3(-64,-64,-64),Vector3(128,128,128));
+
+ attractor_count=0;
+
+}
+
+
+ParticleSystemSW::~ParticleSystemSW()
+{
+}
+
+
+#define DEFAULT_SEED 1234567
+
+_FORCE_INLINE_ static float _rand_from_seed(uint32_t *seed) {
+
+ uint32_t k;
+ uint32_t s = (*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = s;
+
+ float v=((float)((*seed) & 0xFFFFF))/(float)0xFFFFF;
+ v=v*2.0-1.0;
+ return v;
+}
+
+_FORCE_INLINE_ static uint32_t _irand_from_seed(uint32_t *seed) {
+
+ uint32_t k;
+ uint32_t s = (*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = s;
+
+ return s;
+}
+
+void ParticleSystemProcessSW::process(const ParticleSystemSW *p_system,const Transform& p_transform,float p_time) {
+
+ valid=false;
+ if (p_system->amount<=0) {
+ ERR_EXPLAIN("Invalid amount of particles: "+itos(p_system->amount));
+ ERR_FAIL_COND(p_system->amount<=0);
+ }
+ if (p_system->attractor_count<0 || p_system->attractor_count>VS::MAX_PARTICLE_ATTRACTORS) {
+ ERR_EXPLAIN("Invalid amount of particle attractors.");
+ ERR_FAIL_COND(p_system->attractor_count<0 || p_system->attractor_count>VS::MAX_PARTICLE_ATTRACTORS);
+ }
+ float lifetime = p_system->particle_vars[VS::PARTICLE_LIFETIME];
+ if (lifetime<CMP_EPSILON) {
+ ERR_EXPLAIN("Particle system lifetime too small.");
+ ERR_FAIL_COND(lifetime<CMP_EPSILON);
+ }
+ valid=true;
+ int particle_count=MIN(p_system->amount,ParticleSystemSW::MAX_PARTICLES);;
+
+
+ int emission_point_count = p_system->emission_points.size();
+ DVector<Vector3>::Read r;
+ if (emission_point_count)
+ r=p_system->emission_points.read();
+
+ if (particle_count!=particle_data.size()) {
+
+ //clear the whole system if particle amount changed
+ particle_data.clear();
+ particle_data.resize(p_system->amount);
+ particle_system_time=0;
+ }
+
+ float next_time = particle_system_time+p_time;
+
+ if (next_time > lifetime)
+ next_time=Math::fmod(next_time,lifetime);
+
+
+ ParticleData *pdata=&particle_data[0];
+ Vector3 attractor_positions[VS::MAX_PARTICLE_ATTRACTORS];
+
+ for(int i=0;i<p_system->attractor_count;i++) {
+
+ attractor_positions[i]=p_transform.xform(p_system->attractors[i].pos);
+ }
+
+
+ for(int i=0;i<particle_count;i++) {
+
+ ParticleData &p=pdata[i];
+
+ float restart_time = (i * lifetime / p_system->amount);
+
+ bool restart=false;
+
+ if ( next_time < particle_system_time ) {
+
+ if (restart_time > particle_system_time || restart_time < next_time )
+ restart=true;
+
+ } else if (restart_time > particle_system_time && restart_time < next_time ) {
+ restart=true;
+ }
+
+ if (restart) {
+
+
+ if (p_system->emitting) {
+ if (emission_point_count==0) { //use AABB
+ if (p_system->local_coordinates)
+ p.pos = p_system->emission_half_extents * Vector3( _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed) );
+ else
+ p.pos = p_transform.xform( p_system->emission_half_extents * Vector3( _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed) ) );
+ } else {
+ //use preset positions
+ if (p_system->local_coordinates)
+ p.pos = r[_irand_from_seed(&rand_seed)%emission_point_count];
+ else
+ p.pos = p_transform.xform( r[_irand_from_seed(&rand_seed)%emission_point_count] );
+ }
+
+
+ float angle1 = _rand_from_seed(&rand_seed)*p_system->particle_vars[VS::PARTICLE_SPREAD]*Math_PI;
+ float angle2 = _rand_from_seed(&rand_seed)*20.0*Math_PI; // make it more random like
+
+ Vector3 rot_xz=Vector3( Math::sin(angle1), 0.0, Math::cos(angle1) );
+ Vector3 rot = Vector3( Math::cos(angle2)*rot_xz.x,Math::sin(angle2)*rot_xz.x, rot_xz.z);
+
+ p.vel=(rot*p_system->particle_vars[VS::PARTICLE_LINEAR_VELOCITY]+rot*p_system->particle_randomness[VS::PARTICLE_LINEAR_VELOCITY]*_rand_from_seed(&rand_seed));
+ if (!p_system->local_coordinates)
+ p.vel=p_transform.basis.xform( p.vel );
+
+ p.vel+=p_system->emission_base_velocity;
+
+ p.rot=p_system->particle_vars[VS::PARTICLE_INITIAL_ANGLE]+p_system->particle_randomness[VS::PARTICLE_INITIAL_ANGLE]*_rand_from_seed(&rand_seed);
+ p.active=true;
+ for(int r=0;r<PARTICLE_RANDOM_NUMBERS;r++)
+ p.random[r]=_rand_from_seed(&rand_seed);
+
+ } else {
+
+ p.pos=Vector3();
+ p.rot=0;
+ p.vel=Vector3();
+ p.active=false;
+ }
+
+ } else {
+
+ if (!p.active)
+ continue;
+
+ Vector3 force;
+ //apply gravity
+ force=p_system->gravity_normal * (p_system->particle_vars[VS::PARTICLE_GRAVITY]+(p_system->particle_randomness[VS::PARTICLE_GRAVITY]*p.random[0]));
+ //apply linear acceleration
+ force+=p.vel.normalized() * (p_system->particle_vars[VS::PARTICLE_LINEAR_ACCELERATION]+p_system->particle_randomness[VS::PARTICLE_LINEAR_ACCELERATION]*p.random[1]);
+ //apply radial acceleration
+ Vector3 org;
+ if (!p_system->local_coordinates)
+ org=p_transform.origin;
+ force+=(p.pos-org).normalized() * (p_system->particle_vars[VS::PARTICLE_RADIAL_ACCELERATION]+p_system->particle_randomness[VS::PARTICLE_RADIAL_ACCELERATION]*p.random[2]);
+ //apply tangential acceleration
+ force+=(p.pos-org).cross(p_system->gravity_normal).normalized() * (p_system->particle_vars[VS::PARTICLE_TANGENTIAL_ACCELERATION]+p_system->particle_randomness[VS::PARTICLE_TANGENTIAL_ACCELERATION]*p.random[3]);
+ //apply attractor forces
+ for(int a=0;a<p_system->attractor_count;a++) {
+
+ force+=(p.pos-attractor_positions[a]).normalized() * p_system->attractors[a].force;
+ }
+
+
+ p.vel+=force * p_time;
+ if (p_system->particle_vars[VS::PARTICLE_DAMPING]) {
+
+ float v = p.vel.length();
+ float damp = p_system->particle_vars[VS::PARTICLE_DAMPING] + p_system->particle_vars[VS::PARTICLE_DAMPING] * p_system->particle_randomness[VS::PARTICLE_DAMPING];
+ v -= damp * p_time;
+ if (v<0) {
+ p.vel=Vector3();
+ } else {
+ p.vel=p.vel.normalized() * v;
+ }
+
+ }
+ p.rot+=(p_system->particle_vars[VS::PARTICLE_ANGULAR_VELOCITY]+p_system->particle_randomness[VS::PARTICLE_ANGULAR_VELOCITY]*p.random[4]) *p_time;
+ p.pos+=p.vel * p_time;
+ }
+ }
+
+ particle_system_time=Math::fmod( particle_system_time+p_time, lifetime );
+
+
+}
+
+ParticleSystemProcessSW::ParticleSystemProcessSW() {
+
+ particle_system_time=0;
+ rand_seed=1234567;
+ valid=false;
+}
+
+
+struct _ParticleSorterSW {
+
+
+ _FORCE_INLINE_ bool operator()(const ParticleSystemDrawInfoSW::ParticleDrawInfo *p_a,const ParticleSystemDrawInfoSW::ParticleDrawInfo *p_b) const {
+
+ return p_a->d > p_b->d; // draw from further away to closest
+ }
+};
+
+void ParticleSystemDrawInfoSW::prepare(const ParticleSystemSW *p_system,const ParticleSystemProcessSW *p_process,const Transform& p_system_transform,const Transform& p_camera_transform) {
+
+ ERR_FAIL_COND(p_process->particle_data.size() != p_system->amount);
+ ERR_FAIL_COND(p_system->amount<=0 || p_system->amount>=ParticleSystemSW::MAX_PARTICLES);
+
+ const ParticleSystemProcessSW::ParticleData *pdata=&p_process->particle_data[0];
+ float time_pos=p_process->particle_system_time/p_system->particle_vars[VS::PARTICLE_LIFETIME];
+
+ ParticleSystemSW::ColorPhase cphase[VS::MAX_PARTICLE_COLOR_PHASES];
+
+ float last=-1;
+ int col_count=0;
+
+ for(int i=0;i<p_system->color_phase_count;i++) {
+
+ if (p_system->color_phases[i].pos<=last)
+ break;
+ cphase[i]=p_system->color_phases[i];
+ col_count++;
+ }
+
+
+
+
+
+ Vector3 camera_z_axis = p_camera_transform.basis.get_axis(2);
+
+ for(int i=0;i<p_system->amount;i++) {
+
+ ParticleDrawInfo &pdi=draw_info[i];
+ pdi.data=&pdata[i];
+ pdi.transform.origin=pdi.data->pos;
+ if (p_system->local_coordinates)
+ pdi.transform.origin=p_system_transform.xform(pdi.transform.origin);
+
+ pdi.d=-camera_z_axis.dot(pdi.transform.origin);
+
+ // adjust particle size, color and rotation
+
+ float time = ((float)i / p_system->amount);
+ if (time<time_pos)
+ time=time_pos-time;
+ else
+ time=(1.0-time)+time_pos;
+
+ Vector3 up=p_camera_transform.basis.get_axis(1); // up determines the rotation
+ float up_scale=1.0;
+
+ if (p_system->height_from_velocity) {
+
+ Vector3 veld = pdi.data->vel;
+ Vector3 cam_z = camera_z_axis.normalized();
+ float vc = Math::abs(veld.normalized().dot(cam_z));
+
+ if (vc<(1.0-CMP_EPSILON)) {
+ up = Plane(cam_z,0).project(veld).normalized();
+ float h = p_system->particle_vars[VS::PARTICLE_HEIGHT]+p_system->particle_randomness[VS::PARTICLE_HEIGHT]*pdi.data->random[7];
+ float velh = veld.length();
+ h+=velh*(p_system->particle_vars[VS::PARTICLE_HEIGHT_SPEED_SCALE]+p_system->particle_randomness[VS::PARTICLE_HEIGHT_SPEED_SCALE]*pdi.data->random[7]);
+
+
+ up_scale=Math::lerp(1.0,h,(1.0-vc));
+ }
+
+ } else if (pdi.data->rot) {
+
+ up.rotate(camera_z_axis,pdi.data->rot);
+ }
+
+ {
+ // matrix
+ Vector3 v_z = (p_camera_transform.origin-pdi.transform.origin).normalized();
+// Vector3 v_z = (p_camera_transform.origin-pdi.data->pos).normalized();
+ Vector3 v_y = up;
+ Vector3 v_x = v_y.cross(v_z);
+ v_y = v_z.cross(v_x);
+ v_x.normalize();
+ v_y.normalize();
+
+
+ float initial_scale, final_scale;
+ initial_scale = p_system->particle_vars[VS::PARTICLE_INITIAL_SIZE]+p_system->particle_randomness[VS::PARTICLE_INITIAL_SIZE]*pdi.data->random[5];
+ final_scale = p_system->particle_vars[VS::PARTICLE_FINAL_SIZE]+p_system->particle_randomness[VS::PARTICLE_FINAL_SIZE]*pdi.data->random[6];
+ float scale = initial_scale + time * (final_scale - initial_scale);
+
+ pdi.transform.basis.set_axis(0,v_x * scale);
+ pdi.transform.basis.set_axis(1,v_y * scale * up_scale);
+ pdi.transform.basis.set_axis(2,v_z * scale);
+ }
+
+
+
+ int cpos=0;
+
+ while(cpos<col_count) {
+
+ if (cphase[cpos].pos > time)
+ break;
+ cpos++;
+ }
+
+ cpos--;
+
+
+ if (cpos==-1)
+ pdi.color=Color(1,1,1,1);
+ else {
+ if (cpos==col_count-1)
+ pdi.color=cphase[cpos].color;
+ else {
+ float diff = (cphase[cpos+1].pos-cphase[cpos].pos);
+ if (diff>0)
+ pdi.color=cphase[cpos].color.linear_interpolate(cphase[cpos+1].color, (time - cphase[cpos].pos) / diff );
+ else
+ pdi.color=cphase[cpos+1].color;
+ }
+ }
+
+
+ draw_info_order[i]=&pdi;
+
+ }
+
+
+ SortArray<ParticleDrawInfo*,_ParticleSorterSW> particle_sort;
+ particle_sort.sort(&draw_info_order[0],p_system->amount);
+
+}
diff --git a/servers/visual/particle_system_sw.h b/servers/visual/particle_system_sw.h
new file mode 100644
index 0000000000..2b18c7f4e3
--- /dev/null
+++ b/servers/visual/particle_system_sw.h
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* particle_system_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 PARTICLE_SYSTEM_SW_H
+#define PARTICLE_SYSTEM_SW_H
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+#include "servers/visual_server.h"
+
+struct ParticleSystemSW {
+ enum {
+
+ MAX_PARTICLES=1024
+ };
+
+ float particle_vars[VS::PARTICLE_VAR_MAX];
+ float particle_randomness[VS::PARTICLE_VAR_MAX];
+
+ Vector3 emission_half_extents;
+ DVector<Vector3> emission_points;
+ Vector3 gravity_normal;
+ Vector3 emission_base_velocity;
+ int amount;
+ bool emitting;
+ bool height_from_velocity;
+ AABB visibility_aabb;
+ bool sort;
+ bool local_coordinates;
+
+ struct ColorPhase {
+
+ float pos;
+ Color color;
+ ColorPhase() { pos=1.0; color=Color(0.0,0.0,1.0,1.0); }
+ };
+
+ int color_phase_count;
+ ColorPhase color_phases[VS::MAX_PARTICLE_COLOR_PHASES];
+
+
+ struct Attractor {
+
+ Vector3 pos;
+ float force;
+ };
+
+ int attractor_count;
+ Attractor attractors[VS::MAX_PARTICLE_ATTRACTORS];
+
+
+ ParticleSystemSW();
+ ~ParticleSystemSW();
+
+};
+
+
+struct ParticleSystemProcessSW {
+
+ enum {
+ PARTICLE_RANDOM_NUMBERS = 8,
+ };
+
+ struct ParticleData {
+
+ Vector3 pos;
+ Vector3 vel;
+ float rot;
+ bool active;
+ float random[PARTICLE_RANDOM_NUMBERS];
+
+ ParticleData() { active=0; rot=0; }
+ };
+
+
+ bool valid;
+ float particle_system_time;
+ uint32_t rand_seed;
+ Vector<ParticleData> particle_data;
+
+ void process(const ParticleSystemSW *p_system,const Transform& p_transform,float p_time);
+
+ ParticleSystemProcessSW();
+};
+
+struct ParticleSystemDrawInfoSW {
+
+ struct ParticleDrawInfo {
+
+ const ParticleSystemProcessSW::ParticleData *data;
+ float d;
+ Transform transform;
+ Color color;
+
+ };
+
+ ParticleDrawInfo draw_info[ParticleSystemSW::MAX_PARTICLES];
+ ParticleDrawInfo *draw_info_order[ParticleSystemSW::MAX_PARTICLES];
+
+ void prepare(const ParticleSystemSW *p_system,const ParticleSystemProcessSW *p_process,const Transform& p_system_transform,const Transform& p_camera_transform);
+
+};
+
+#endif
diff --git a/servers/visual/rasterizer.cpp b/servers/visual/rasterizer.cpp
new file mode 100644
index 0000000000..2de49d3d81
--- /dev/null
+++ b/servers/visual/rasterizer.cpp
@@ -0,0 +1,573 @@
+/*************************************************************************/
+/* rasterizer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "rasterizer.h"
+#include "print_string.h"
+#include "os/os.h"
+
+RID Rasterizer::create_default_material() {
+
+ return material_create();
+}
+
+
+/* Fixed MAterial SHADER API */
+
+RID Rasterizer::_create_shader(const FixedMaterialShaderKey& p_key) {
+
+ ERR_FAIL_COND_V(!p_key.valid,RID());
+ Map<FixedMaterialShaderKey,FixedMaterialShader>::Element *E=fixed_material_shaders.find(p_key);
+
+ if (E) {
+ E->get().refcount++;
+ return E->get().shader;
+ }
+
+ uint64_t t = OS::get_singleton()->get_ticks_usec();
+
+ FixedMaterialShader fms;
+ fms.refcount=1;
+ fms.shader=shader_create();
+
+ //create shader code
+
+
+ int texcoords_used=0;
+ String code;
+
+ static const char* _uv_str[4]={"UV","UV2","uv_xform","uv_sphere"};
+#define _TEXUVSTR(m_idx) String( _uv_str[(p_key.texcoord_mask>>(m_idx*2))&0x3] )
+
+
+ if (p_key.use_pointsize) {
+
+ code+="UV=POINT_COORD;\n";
+ }
+
+
+ for(int i=0;i<VS::FIXED_MATERIAL_PARAM_MAX;i++) {
+
+ if (p_key.texture_mask&(1<<i))
+ texcoords_used|=(1<<((p_key.texcoord_mask>>(i*2))&0x3));
+ }
+
+ if (texcoords_used&(1<<VS::FIXED_MATERIAL_TEXCOORD_UV_TRANSFORM)) {
+
+ code+="uniform mat4 fmp_uv_xform;\n";
+ code+="vec2 uv_xform = fmp_uv_xform * UV;\n";
+ }
+
+ /* HANDLE NORMAL MAPPING */
+
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_NORMAL)) {
+
+ String scode;
+ scode+="uniform float fmp_normal;\n";
+ scode+="uniform texture fmp_normal_tex;\n";
+ String uv_str;
+ if ((p_key.texcoord_mask>>(VS::FIXED_MATERIAL_PARAM_NORMAL*2))&0x3==VS::FIXED_MATERIAL_TEXCOORD_SPHERE) {
+ uv_str="uv"; //sorry not supported
+ } else {
+ uv_str=_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_NORMAL);
+ }
+ scode+="vec3 normal=tex( fmp_normal_tex,"+uv_str+").xyz * 2.0 - vec3(1.0,1.0,1.0);\n";
+ scode+="NORMAL = mix( NORMAL,mat3(TANGENT,BINORMAL,NORMAL) * normal, fmp_normal);\n";
+ code+=scode;
+ }
+
+ //handle sphere uv if used, do it here because it needs the normal, which may be transformed by a normal map
+
+ if (texcoords_used&(1<<VS::FIXED_MATERIAL_TEXCOORD_SPHERE)) {
+
+ String tcode;
+ tcode="vec3 eye_normal = normalize(VERTEX);\n";
+ tcode+="vec3 ref = (eye_normal - 2.0*dot(NORMAL, eye_normal)*NORMAL);\n";
+ tcode+="ref.z+=1.0;\n";
+ tcode+="vec2 uv_sphere = ref.xy*vec2(0.5,0.0-0.5)+vec2(0.5,0.0-0.5);\n";
+ code+=tcode;
+ }
+
+ /* HANDLE DIFFUSE LIGHTING */
+
+ code+="uniform color fmp_diffuse;\n";
+ code+="color diffuse=fmp_diffuse;\n";
+
+ if (p_key.use_color_array)
+ code+="diffuse*=COLOR;\n";
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_DIFFUSE)) {
+
+
+ code+="uniform texture fmp_diffuse_tex;\n";
+ code+="diffuse*=tex( fmp_diffuse_tex,"+_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_DIFFUSE)+");\n";
+ }
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_DETAIL)) {
+
+ String dcode;
+ dcode+="uniform texture fmp_detail_tex;\n";
+ dcode+="uniform float fmp_detail;\n";
+ dcode+="color detail=tex( fmp_detail_tex,"+_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_DETAIL)+");\n";
+
+ switch(p_key.detail_blend) {
+
+ case VS::MATERIAL_BLEND_MODE_MIX:
+
+ dcode+="diffuse=vec4(mix(diffuse.rgb,detail.rgb,detail.a*fmp_detail),diffuse.a);\n";
+ break;
+ case VS::MATERIAL_BLEND_MODE_ADD:
+ dcode+="diffuse=vec4(diffuse.rgb+detail.rgb*fmp_detail,diffuse.a);\n";
+ break;
+ case VS::MATERIAL_BLEND_MODE_SUB:
+ dcode+="diffuse=vec4(diffuse.rgb+detail.rgb*fmp_detail,diffuse.a);\n";
+ break;
+ case VS::MATERIAL_BLEND_MODE_MUL:
+ dcode+="diffuse=diffuse*mix(vec4(1,1,1,1),detail,fmp_detail);\n";
+ break;
+ }
+
+ code+=dcode;
+ }
+
+ if (p_key.use_alpha) {
+ code+="DIFFUSE_ALPHA=diffuse;\n";
+ } else {
+ code+="DIFFUSE=diffuse.rgb;\n";
+ }
+
+ /* HANDLE SPECULAR LIGHTING */
+
+ code+="uniform color fmp_specular;\n";
+ code+="color specular=fmp_specular;\n";
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_SPECULAR)) {
+
+ String scode;
+ scode+="uniform texture fmp_specular_tex;\n";
+ scode+="specular*=tex( fmp_specular_tex,"+_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_SPECULAR)+");\n";
+ code+=scode;
+ }
+
+ code+="SPECULAR=specular.rgb;\n";
+
+ code+="uniform float fmp_specular_exp;\n";
+ code+="float specular_exp=fmp_specular_exp;\n";
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP)) {
+
+ String scode;
+ scode+="uniform texture fmp_specular_exp_tex;\n";
+ scode+="specular_exp*=tex( fmp_specular_exp_tex,"+_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP)+").r;\n";
+ code+=scode;
+ }
+
+ code+="SPEC_EXP=specular_exp;\n";
+
+ /* HANDLE EMISSION LIGHTING */
+
+ code+="uniform color fmp_emission;\n";
+ code+="color emission=fmp_emission;\n";
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_EMISSION)) {
+
+ String scode;
+ scode+="uniform texture fmp_emission_tex;\n";
+ scode+="emission*=tex( fmp_emission_tex,"+_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_EMISSION)+");\n";
+ code+=scode;
+ }
+
+ code+="EMISSION=emission.rgb;\n";
+
+
+ /* HANDLE GLOW */
+
+ code+="uniform float fmp_glow;\n";
+ code+="float glow=fmp_glow;\n";
+
+ if (p_key.texture_mask&(1<<VS::FIXED_MATERIAL_PARAM_GLOW)) {
+
+ String scode;
+ scode+="uniform texture fmp_glow_tex;\n";
+ scode+="glow*=tex( fmp_glow_tex,"+_TEXUVSTR(VS::FIXED_MATERIAL_PARAM_GLOW)+").r;\n";
+ code+=scode;
+ }
+
+ code+="GLOW=glow;\n";
+
+ //print_line("**FRAGMENT SHADER GENERATED code: \n"+code);
+
+ String vcode;
+ vcode="uniform float "+_fixed_material_param_names[VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP]+";\n";
+ vcode+="SPEC_EXP="+_fixed_material_param_names[VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP]+";\n";
+ if (p_key.use_pointsize) {
+
+ vcode+="uniform float "+_fixed_material_point_size_name+";\n";
+ vcode+="POINT_SIZE="+_fixed_material_point_size_name+";\n";
+// vcode+="POINT_SIZE=10.0;\n";
+ }
+
+ //print_line("**VERTEX SHADER GENERATED code: \n"+vcode);
+
+ double tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0;
+// print_line("generate: "+rtos(tf));
+
+ shader_set_code(fms.shader,vcode,code,0,0);
+
+ fixed_material_shaders[p_key]=fms;
+ return fms.shader;
+}
+
+void Rasterizer::_free_shader(const FixedMaterialShaderKey& p_key) {
+
+ if (p_key.valid==0)
+ return; //not a valid key
+
+ Map<FixedMaterialShaderKey,FixedMaterialShader>::Element *E=fixed_material_shaders.find(p_key);
+
+ ERR_FAIL_COND(!E);
+ E->get().refcount--;
+ if (E->get().refcount==0) {
+ free(E->get().shader);
+ fixed_material_shaders.erase(E);
+ }
+
+}
+
+
+void Rasterizer::fixed_material_set_flag(RID p_material, VS::FixedMaterialFlags p_flag, bool p_enabled) {
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+
+ switch(p_flag) {
+
+ case VS::FIXED_MATERIAL_FLAG_USE_ALPHA: fm.use_alpha=p_enabled; break;
+ case VS::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY: fm.use_color_array=p_enabled; break;
+ case VS::FIXED_MATERIAL_FLAG_USE_POINT_SIZE: fm.use_pointsize=p_enabled; break;
+ }
+
+ if (!fm.dirty_list.in_list())
+ fixed_material_dirty_list.add( &fm.dirty_list );
+
+}
+
+bool Rasterizer::fixed_material_get_flag(RID p_material, VS::FixedMaterialFlags p_flag) const{
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,false);
+ const FixedMaterial &fm=*E->get();
+ switch(p_flag) {
+
+ case VS::FIXED_MATERIAL_FLAG_USE_ALPHA: return fm.use_alpha;; break;
+ case VS::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY: return fm.use_color_array;; break;
+ case VS::FIXED_MATERIAL_FLAG_USE_POINT_SIZE: return fm.use_pointsize;; break;
+ }
+
+
+ return false;
+}
+
+
+RID Rasterizer::fixed_material_create() {
+
+ RID mat = material_create();
+ fixed_materials[mat]=memnew( FixedMaterial() );
+ FixedMaterial &fm=*fixed_materials[mat];
+ fm.self=mat;
+ fm.get_key();
+ for(int i=0;i<VS::FIXED_MATERIAL_PARAM_MAX;i++) {
+
+ material_set_param(mat,_fixed_material_param_names[i],fm.param[i]); //must be there
+ }
+ fixed_material_dirty_list.add(&fm.dirty_list);
+ //print_line("FMC: "+itos(mat.get_id()));
+ return mat;
+}
+
+
+
+
+void Rasterizer::fixed_material_set_parameter(RID p_material, VS::FixedMaterialParam p_parameter, const Variant& p_value){
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+ RID material=E->key();
+ ERR_FAIL_INDEX(p_parameter,VS::FIXED_MATERIAL_PARAM_MAX);
+
+ if ((p_parameter==VS::FIXED_MATERIAL_PARAM_DIFFUSE || p_parameter==VS::FIXED_MATERIAL_PARAM_SPECULAR || p_parameter==VS::FIXED_MATERIAL_PARAM_EMISSION)) {
+
+ if (p_value.get_type()!=Variant::COLOR) {
+ ERR_EXPLAIN(String(_fixed_material_param_names[p_parameter])+" expects Color");
+ ERR_FAIL();
+ }
+ } else {
+
+ if (!p_value.is_num()) {
+ ERR_EXPLAIN(String(_fixed_material_param_names[p_parameter])+" expects scalar");
+ ERR_FAIL();
+ }
+ }
+
+ fm.param[p_parameter]=p_value;
+ VS::get_singleton()->material_set_param(material,_fixed_material_param_names[p_parameter],p_value);
+
+
+}
+Variant Rasterizer::fixed_material_get_parameter(RID p_material,VS::FixedMaterialParam p_parameter) const{
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,Variant());
+ const FixedMaterial &fm=*E->get();
+ ERR_FAIL_INDEX_V(p_parameter,VS::FIXED_MATERIAL_PARAM_MAX,Variant());
+ return fm.param[p_parameter];
+}
+
+void Rasterizer::fixed_material_set_texture(RID p_material,VS::FixedMaterialParam p_parameter, RID p_texture){
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ if (!E) {
+
+ print_line("Not found: "+itos(p_material.get_id()));
+ }
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+
+
+ ERR_FAIL_INDEX(p_parameter,VS::FIXED_MATERIAL_PARAM_MAX);
+ RID material=E->key();
+ fm.texture[p_parameter]=p_texture;
+ VS::get_singleton()->material_set_param(material,_fixed_material_tex_names[p_parameter],p_texture);
+
+ if (!fm.dirty_list.in_list())
+ fixed_material_dirty_list.add( &fm.dirty_list );
+
+
+
+
+}
+RID Rasterizer::fixed_material_get_texture(RID p_material,VS::FixedMaterialParam p_parameter) const{
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,RID());
+ const FixedMaterial &fm=*E->get();
+ ERR_FAIL_INDEX_V(p_parameter,VS::FIXED_MATERIAL_PARAM_MAX,RID());
+
+ return fm.texture[p_parameter];
+}
+
+void Rasterizer::fixed_material_set_detail_blend_mode(RID p_material,VS::MaterialBlendMode p_mode){
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+
+
+ fm.get_key();
+ ERR_FAIL_INDEX(p_mode,4);
+ fm.detail_blend=p_mode;
+ if (!fm.dirty_list.in_list())
+ fixed_material_dirty_list.add( &fm.dirty_list );
+
+}
+VS::MaterialBlendMode Rasterizer::fixed_material_get_detail_blend_mode(RID p_material) const{
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,VS::MATERIAL_BLEND_MODE_MIX);
+ const FixedMaterial &fm=*E->get();
+
+ return fm.detail_blend;
+}
+
+void Rasterizer::fixed_material_set_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter, VS::FixedMaterialTexCoordMode p_mode) {
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+ ERR_FAIL_INDEX(p_parameter,VS::FIXED_MATERIAL_PARAM_MAX);
+
+ fm.get_key();
+
+ fm.texture_tc[p_parameter]=p_mode;
+
+ if (!fm.dirty_list.in_list())
+ fixed_material_dirty_list.add( &fm.dirty_list );
+
+}
+
+VS::FixedMaterialTexCoordMode Rasterizer::fixed_material_get_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter) const {
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,VS::FIXED_MATERIAL_TEXCOORD_UV);
+ const FixedMaterial &fm=*E->get();
+ ERR_FAIL_INDEX_V(p_parameter,VS::FIXED_MATERIAL_PARAM_MAX,VS::FIXED_MATERIAL_TEXCOORD_UV);
+
+ return fm.texture_tc[p_parameter];
+}
+
+void Rasterizer::fixed_material_set_uv_transform(RID p_material,const Transform& p_transform) {
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+ RID material=E->key();
+
+ VS::get_singleton()->material_set_param(material,_fixed_material_uv_xform_name,p_transform);
+
+ fm.uv_xform=p_transform;
+
+}
+
+
+
+Transform Rasterizer::fixed_material_get_uv_transform(RID p_material) const {
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,Transform());
+ const FixedMaterial &fm=*E->get();
+
+ return fm.uv_xform;
+}
+
+void Rasterizer::fixed_material_set_point_size(RID p_material,float p_size) {
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND(!E);
+ FixedMaterial &fm=*E->get();
+ RID material=E->key();
+
+ VS::get_singleton()->material_set_param(material,_fixed_material_point_size_name,p_size);
+
+ fm.point_size=p_size;
+
+
+}
+
+float Rasterizer::fixed_material_get_point_size(RID p_material) const{
+
+ const Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+ ERR_FAIL_COND_V(!E,1.0);
+ const FixedMaterial &fm=*E->get();
+
+ return fm.point_size;
+
+}
+
+void Rasterizer::_update_fixed_materials() {
+
+
+ while(fixed_material_dirty_list.first()) {
+
+ FixedMaterial &fm=*fixed_material_dirty_list.first()->self();
+
+ FixedMaterialShaderKey new_key = fm.get_key();
+ if (new_key.key!=fm.current_key.key) {
+
+ _free_shader(fm.current_key);
+ RID new_rid = _create_shader(new_key);
+ fm.current_key=new_key;
+ material_set_shader(fm.self,new_rid);
+
+ if (fm.texture[VS::FIXED_MATERIAL_PARAM_DETAIL].is_valid()) {
+ //send these again just in case.
+ material_set_param(fm.self,_fixed_material_param_names[VS::FIXED_MATERIAL_PARAM_DETAIL],fm.param[VS::FIXED_MATERIAL_PARAM_DETAIL]);
+ }
+ if (fm.texture[VS::FIXED_MATERIAL_PARAM_NORMAL].is_valid()) {
+ //send these again just in case.
+ material_set_param(fm.self,_fixed_material_param_names[VS::FIXED_MATERIAL_PARAM_NORMAL],fm.param[VS::FIXED_MATERIAL_PARAM_NORMAL]);
+ }
+
+ material_set_param(fm.self,_fixed_material_uv_xform_name,fm.uv_xform);
+ if (fm.use_pointsize)
+ material_set_param(fm.self,_fixed_material_point_size_name,fm.point_size);
+ }
+
+ fixed_material_dirty_list.remove(fixed_material_dirty_list.first());
+ }
+}
+
+
+void Rasterizer::_free_fixed_material(const RID& p_material) {
+
+ Map<RID,FixedMaterial*>::Element *E = fixed_materials.find(p_material);
+
+ if (E) {
+
+ _free_shader(E->get()->current_key); //free shader
+ if (E->get()->dirty_list.in_list())
+ fixed_material_dirty_list.remove( &E->get()->dirty_list);
+ memdelete(E->get());
+ fixed_materials.erase(E); //free material
+ }
+
+
+}
+
+
+void Rasterizer::flush_frame() {
+
+ //not really necesary to implement
+}
+
+Rasterizer::Rasterizer() {
+
+ static const char* fm_names[VS::FIXED_MATERIAL_PARAM_MAX]={
+ "diffuse",
+ "detail",
+ "specular",
+ "emission",
+ "specular_exp",
+ "glow",
+ "normal",
+ "shade_param"};
+
+ for(int i=0;i<VS::FIXED_MATERIAL_PARAM_MAX;i++) {
+
+ _fixed_material_param_names[i]=String("fmp_")+fm_names[i];
+ _fixed_material_tex_names[i]=String("fmp_")+fm_names[i]+"_tex";
+ }
+
+ _fixed_material_uv_xform_name="fmp_uv_xform";
+ _fixed_material_point_size_name="fmp_point_size";
+
+}
+
+RID Rasterizer::create_overdraw_debug_material() {
+ RID mat = fixed_material_create();
+ fixed_material_set_parameter( mat,VisualServer::FIXED_MATERIAL_PARAM_SPECULAR,Color(0,0,0) );
+ fixed_material_set_parameter( mat,VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0.1,0.1,0.2) );
+ fixed_material_set_parameter( mat,VisualServer::FIXED_MATERIAL_PARAM_EMISSION,Color(0,0,0) );
+ fixed_material_set_flag( mat, VS::FIXED_MATERIAL_FLAG_USE_ALPHA, true);
+ material_set_flag( mat, VisualServer::MATERIAL_FLAG_UNSHADED, true );
+ material_set_blend_mode( mat,VisualServer::MATERIAL_BLEND_MODE_ADD );
+
+
+ return mat;
+}
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
new file mode 100644
index 0000000000..83031e981a
--- /dev/null
+++ b/servers/visual/rasterizer.h
@@ -0,0 +1,562 @@
+/*************************************************************************/
+/* rasterizer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 RASTERIZER_H
+#define RASTERIZER_H
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+#include "servers/visual_server.h"
+#include "camera_matrix.h"
+#include "map.h"
+#include "self_list.h"
+
+class Rasterizer {
+protected:
+
+ RID create_default_material();
+ RID create_overdraw_debug_material();
+
+
+ /* Fixed Material Shader API */
+
+ union FixedMaterialShaderKey {
+
+ struct {
+ uint16_t texcoord_mask;
+ uint8_t texture_mask;
+ uint8_t detail_blend:2;
+ bool use_alpha:1;
+ bool use_color_array:1;
+ bool use_pointsize:1;
+ bool valid:1;
+ };
+
+ uint32_t key;
+
+ _FORCE_INLINE_ bool operator<(const FixedMaterialShaderKey& p_key) const { return key<p_key.key; }
+ };
+
+ struct FixedMaterialShader {
+
+ int refcount;
+ RID shader;
+ };
+
+ Map<FixedMaterialShaderKey,FixedMaterialShader> fixed_material_shaders;
+
+ RID _create_shader(const FixedMaterialShaderKey& p_key);
+ void _free_shader(const FixedMaterialShaderKey& p_key);
+
+ struct FixedMaterial {
+
+
+ RID self;
+ bool use_alpha;
+ bool use_color_array;
+ bool use_pointsize;
+ float point_size;
+ Transform uv_xform;
+ VS::MaterialBlendMode detail_blend;
+ RID texture[VS::FIXED_MATERIAL_PARAM_MAX];
+ Variant param[VS::FIXED_MATERIAL_PARAM_MAX];
+ VS::FixedMaterialTexCoordMode texture_tc[VS::FIXED_MATERIAL_PARAM_MAX];
+
+ SelfList<FixedMaterial> dirty_list;
+
+ FixedMaterialShaderKey current_key;
+
+ _FORCE_INLINE_ FixedMaterialShaderKey get_key() const {
+
+
+ FixedMaterialShaderKey k;
+ k.key=0;
+ k.use_alpha=use_alpha;
+ k.use_color_array=use_color_array;
+ k.use_pointsize=use_pointsize;
+ k.detail_blend=detail_blend;
+ k.valid=true;
+ for(int i=0;i<VS::FIXED_MATERIAL_PARAM_MAX;i++) {
+ if (texture[i].is_valid()) {
+ //print_line("valid: "+itos(i));
+ k.texture_mask|=(1<<i);
+ k.texcoord_mask|=(texture_tc[i])<<(i*2);
+ }
+ }
+
+ return k;
+ }
+
+
+ FixedMaterial() : dirty_list(this) {
+
+ use_alpha=false;
+ use_color_array=false;
+ use_pointsize=false;
+ point_size=1.0;
+ detail_blend=VS::MATERIAL_BLEND_MODE_MIX;
+ for(int i=0;i<VS::FIXED_MATERIAL_PARAM_MAX;i++) {
+ texture_tc[i]=VS::FIXED_MATERIAL_TEXCOORD_UV;
+ }
+ param[VS::FIXED_MATERIAL_PARAM_DIFFUSE]=Color(1,1,1);
+ param[VS::FIXED_MATERIAL_PARAM_DETAIL]=1.0;
+ param[VS::FIXED_MATERIAL_PARAM_EMISSION]=Color(0,0,0);
+ param[VS::FIXED_MATERIAL_PARAM_GLOW]=0;
+ param[VS::FIXED_MATERIAL_PARAM_SHADE_PARAM]=0;
+ param[VS::FIXED_MATERIAL_PARAM_SPECULAR]=Color(0.0,0.0,0.0);
+ param[VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP]=40;
+ param[VS::FIXED_MATERIAL_PARAM_NORMAL]=1;
+
+ current_key.key=0;
+
+
+ }
+ };
+
+ StringName _fixed_material_param_names[VS::FIXED_MATERIAL_PARAM_MAX];
+ StringName _fixed_material_tex_names[VS::FIXED_MATERIAL_PARAM_MAX];
+ StringName _fixed_material_uv_xform_name;
+ StringName _fixed_material_point_size_name;
+
+ Map<RID,FixedMaterial*> fixed_materials;
+
+ SelfList<FixedMaterial>::List fixed_material_dirty_list;
+
+protected:
+ void _update_fixed_materials();
+ void _free_fixed_material(const RID& p_material);
+
+public:
+ /* TEXTURE API */
+
+ virtual RID texture_create()=0;
+ RID texture_create_from_image(const Image& p_image,uint32_t p_flags=VS::TEXTURE_FLAGS_DEFAULT); // helper
+ virtual void texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags=VS::TEXTURE_FLAGS_DEFAULT)=0;
+ virtual void texture_set_data(RID p_texture,const Image& p_image,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT)=0;
+ virtual Image texture_get_data(RID p_texture,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT) const=0;
+ virtual void texture_set_flags(RID p_texture,uint32_t p_flags)=0;
+ virtual uint32_t texture_get_flags(RID p_texture) const=0;
+ virtual Image::Format texture_get_format(RID p_texture) const=0;
+ virtual uint32_t texture_get_width(RID p_texture) const=0;
+ virtual uint32_t texture_get_height(RID p_texture) const=0;
+ virtual bool texture_has_alpha(RID p_texture) const=0;
+ virtual void texture_set_size_override(RID p_texture,int p_width, int p_height)=0;
+
+ virtual void texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const=0;
+
+ /* SHADER API */
+
+ virtual RID shader_create(VS::ShaderMode p_mode=VS::SHADER_MATERIAL)=0;
+
+ virtual void shader_set_mode(RID p_shader,VS::ShaderMode p_mode)=0;
+ virtual VS::ShaderMode shader_get_mode(RID p_shader) const=0;
+
+ virtual void shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs=0,int p_fragment_ofs=0)=0;
+ virtual String shader_get_fragment_code(RID p_shader) const=0;
+ virtual String shader_get_vertex_code(RID p_shader) const=0;
+
+ virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const=0;
+
+ /* COMMON MATERIAL API */
+
+ virtual RID material_create()=0;
+
+ virtual void material_set_shader(RID p_shader_material, RID p_shader)=0;
+ virtual RID material_get_shader(RID p_shader_material) const=0;
+
+ virtual void material_set_param(RID p_material, const StringName& p_param, const Variant& p_value)=0;
+ virtual Variant material_get_param(RID p_material, const StringName& p_param) const=0;
+
+ virtual void material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled)=0;
+ virtual bool material_get_flag(RID p_material,VS::MaterialFlag p_flag) const=0;
+
+ virtual void material_set_hint(RID p_material, VS::MaterialHint p_hint,bool p_enabled)=0;
+ virtual bool material_get_hint(RID p_material,VS::MaterialHint p_hint) const=0;
+
+ virtual void material_set_shade_model(RID p_material, VS::MaterialShadeModel p_model)=0;
+ virtual VS::MaterialShadeModel material_get_shade_model(RID p_material) const=0;
+
+ virtual void material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode)=0;
+ virtual VS::MaterialBlendMode material_get_blend_mode(RID p_material) const=0;
+
+ virtual void material_set_line_width(RID p_material,float p_line_width)=0;
+ virtual float material_get_line_width(RID p_material) const=0;
+
+
+ /* FIXED MATERIAL */
+
+ virtual RID fixed_material_create();
+
+ virtual void fixed_material_set_flag(RID p_material, VS::FixedMaterialFlags p_flag, bool p_enabled);
+ virtual bool fixed_material_get_flag(RID p_material, VS::FixedMaterialFlags p_flag) const;
+
+ virtual void fixed_material_set_parameter(RID p_material, VS::FixedMaterialParam p_parameter, const Variant& p_value);
+ virtual Variant fixed_material_get_parameter(RID p_material,VS::FixedMaterialParam p_parameter) const;
+
+ virtual void fixed_material_set_texture(RID p_material,VS::FixedMaterialParam p_parameter, RID p_texture);
+ virtual RID fixed_material_get_texture(RID p_material,VS::FixedMaterialParam p_parameter) const;
+
+ virtual void fixed_material_set_detail_blend_mode(RID p_material,VS::MaterialBlendMode p_mode);
+ virtual VS::MaterialBlendMode fixed_material_get_detail_blend_mode(RID p_material) const;
+
+ virtual void fixed_material_set_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter, VS::FixedMaterialTexCoordMode p_mode);
+ virtual VS::FixedMaterialTexCoordMode fixed_material_get_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter) const;
+
+ virtual void fixed_material_set_uv_transform(RID p_material,const Transform& p_transform);
+ virtual Transform fixed_material_get_uv_transform(RID p_material) const;
+
+ virtual void fixed_material_set_point_size(RID p_material,float p_size);
+ virtual float fixed_material_get_point_size(RID p_material) const;
+
+ /* MESH API */
+
+ virtual RID mesh_create()=0;
+
+
+ virtual void mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes=Array(),bool p_alpha_sort=false)=0;
+ virtual Array mesh_get_surface_arrays(RID p_mesh,int p_surface) const=0;
+ virtual Array mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const=0;
+
+ virtual void mesh_add_custom_surface(RID p_mesh,const Variant& p_dat)=0;
+
+ virtual void mesh_set_morph_target_count(RID p_mesh,int p_amount)=0;
+ virtual int mesh_get_morph_target_count(RID p_mesh) const=0;
+
+ virtual void mesh_set_morph_target_mode(RID p_mesh,VS::MorphTargetMode p_mode)=0;
+ virtual VS::MorphTargetMode mesh_get_morph_target_mode(RID p_mesh) const=0;
+
+ virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned=false)=0;
+ virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const=0;
+
+ virtual int mesh_surface_get_array_len(RID p_mesh, int p_surface) const=0;
+ virtual int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const=0;
+ virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const=0;
+ virtual VS::PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const=0;
+
+ virtual void mesh_remove_surface(RID p_mesh,int p_index)=0;
+ virtual int mesh_get_surface_count(RID p_mesh) const=0;
+
+ virtual AABB mesh_get_aabb(RID p_mesh) const=0;
+
+ /* MULTIMESH API */
+
+ virtual RID multimesh_create()=0;
+
+ virtual void multimesh_set_instance_count(RID p_multimesh,int p_count)=0;
+ virtual int multimesh_get_instance_count(RID p_multimesh) const=0;
+
+ virtual void multimesh_set_mesh(RID p_multimesh,RID p_mesh)=0;
+ virtual void multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb)=0;
+ virtual void multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform)=0;
+ virtual void multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color)=0;
+
+ virtual RID multimesh_get_mesh(RID p_multimesh) const=0;
+ virtual AABB multimesh_get_aabb(RID p_multimesh) const=0;;
+
+ virtual Transform multimesh_instance_get_transform(RID p_multimesh,int p_index) const=0;
+ virtual Color multimesh_instance_get_color(RID p_multimesh,int p_index) const=0;
+
+ virtual void multimesh_set_visible_instances(RID p_multimesh,int p_visible)=0;
+ virtual int multimesh_get_visible_instances(RID p_multimesh) const=0;
+
+
+ /* PARTICLES API */
+
+ virtual RID particles_create()=0;
+
+ virtual void particles_set_amount(RID p_particles, int p_amount)=0;
+ virtual int particles_get_amount(RID p_particles) const=0;
+
+ virtual void particles_set_emitting(RID p_particles, bool p_emitting)=0;
+ virtual bool particles_is_emitting(RID p_particles) const=0;
+
+ virtual void particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility)=0;
+ virtual AABB particles_get_visibility_aabb(RID p_particles) const=0;
+
+ virtual void particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents)=0;
+ virtual Vector3 particles_get_emission_half_extents(RID p_particles) const=0;
+
+ virtual void particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity)=0;
+ virtual Vector3 particles_get_emission_base_velocity(RID p_particles) const=0;
+
+ virtual void particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points)=0;
+ virtual DVector<Vector3> particles_get_emission_points(RID p_particles) const=0;
+
+ virtual void particles_set_gravity_normal(RID p_particles, const Vector3& p_normal)=0;
+ virtual Vector3 particles_get_gravity_normal(RID p_particles) const=0;
+
+ virtual void particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value)=0;
+ virtual float particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const=0;
+
+ virtual void particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness)=0;
+ virtual float particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const=0;
+
+ virtual void particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos)=0;
+ virtual float particles_get_color_phase_pos(RID p_particles, int p_phase) const=0;
+
+ virtual void particles_set_color_phases(RID p_particles, int p_phases)=0;
+ virtual int particles_get_color_phases(RID p_particles) const=0;
+
+ virtual void particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color)=0;
+ virtual Color particles_get_color_phase_color(RID p_particles, int p_phase) const=0;
+
+ virtual void particles_set_attractors(RID p_particles, int p_attractors)=0;
+ virtual int particles_get_attractors(RID p_particles) const=0;
+
+ virtual void particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos)=0;
+ virtual Vector3 particles_get_attractor_pos(RID p_particles,int p_attractor) const=0;
+
+ virtual void particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force)=0;
+ virtual float particles_get_attractor_strength(RID p_particles,int p_attractor) const=0;
+
+ virtual void particles_set_material(RID p_particles, RID p_material,bool p_owned=false)=0;
+ virtual RID particles_get_material(RID p_particles) const=0;
+
+ virtual AABB particles_get_aabb(RID p_particles) const=0;
+
+ virtual void particles_set_height_from_velocity(RID p_particles, bool p_enable)=0;
+ virtual bool particles_has_height_from_velocity(RID p_particles) const=0;
+
+ virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable)=0;
+ virtual bool particles_is_using_local_coordinates(RID p_particles) const=0;
+
+ /* SKELETON API */
+
+ virtual RID skeleton_create()=0;
+ virtual void skeleton_resize(RID p_skeleton,int p_bones)=0;
+ virtual int skeleton_get_bone_count(RID p_skeleton) const=0;
+ virtual void skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform)=0;
+ virtual Transform skeleton_bone_get_transform(RID p_skeleton,int p_bone)=0;
+
+
+ /* LIGHT API */
+
+ virtual RID light_create(VS::LightType p_type)=0;
+ virtual VS::LightType light_get_type(RID p_light) const=0;
+
+ virtual void light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color)=0;
+ virtual Color light_get_color(RID p_light,VS::LightColor p_type) const=0;
+
+ virtual void light_set_shadow(RID p_light,bool p_enabled)=0;
+ virtual bool light_has_shadow(RID p_light) const=0;
+
+ virtual void light_set_volumetric(RID p_light,bool p_enabled)=0;
+ virtual bool light_is_volumetric(RID p_light) const=0;
+
+ virtual void light_set_projector(RID p_light,RID p_texture)=0;
+ virtual RID light_get_projector(RID p_light) const=0;
+
+ virtual void light_set_var(RID p_light, VS::LightParam p_var, float p_value)=0;
+ virtual float light_get_var(RID p_light, VS::LightParam p_var) const=0;
+
+ virtual void light_set_operator(RID p_light,VS::LightOp p_op)=0;
+ virtual VS::LightOp light_get_operator(RID p_light) const=0;
+
+ virtual void light_omni_set_shadow_mode(RID p_light,VS::LightOmniShadowMode p_mode)=0;
+ virtual VS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) const=0;
+
+ virtual void light_directional_set_shadow_mode(RID p_light,VS::LightDirectionalShadowMode p_mode)=0;
+ virtual VS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) const=0;
+ virtual void light_directional_set_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param, float p_value)=0;
+ virtual float light_directional_get_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param) const=0;
+
+ virtual AABB light_get_aabb(RID p_poly) const=0;
+
+ virtual RID light_instance_create(RID p_light)=0;
+ virtual void light_instance_set_transform(RID p_light_instance,const Transform& p_transform)=0;
+
+
+ enum ShadowType {
+ SHADOW_NONE,
+ SHADOW_SIMPLE,
+ SHADOW_ORTHOGONAL,
+ SHADOW_DUAL_PARABOLOID,
+ SHADOW_CUBE,
+ SHADOW_PSSM, //parallel split shadow map
+ SHADOW_PSM //perspective shadow map
+ };
+
+ enum ShadowPass {
+ PASS_DUAL_PARABOLOID_FRONT=0,
+ PASS_DUAL_PARABOLOID_BACK=1,
+ PASS_CUBE_FRONT=0,
+ PASS_CUBE_BACK=1,
+ PASS_CUBE_TOP=2,
+ PASS_CUBE_BOTTOM=3,
+ PASS_CUBE_LEFT=4,
+ PASS_CUBE_RIGHT=5,
+ };
+
+ virtual ShadowType light_instance_get_shadow_type(RID p_light_instance,bool p_far=false) const=0;
+ virtual int light_instance_get_shadow_passes(RID p_light_instance) const=0;
+ virtual void light_instance_set_shadow_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near=0,float p_split_far=0)=0;
+ virtual int light_instance_get_shadow_size(RID p_light_instance, int p_index=0) const=0;
+
+ /* SHADOWS */
+
+ virtual void shadow_clear_near()=0;
+ virtual bool shadow_allocate_near(RID p_light)=0; //true on successful alloc
+ virtual bool shadow_allocate_far(RID p_light)=0; //true on successful alloc
+
+ /* PARTICLES INSTANCE */
+
+ virtual RID particles_instance_create(RID p_particles)=0;
+ virtual void particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform)=0;
+
+ /* RENDER API */
+ /* all calls (inside begin/end shadow) are always warranted to be in the following order: */
+
+ /* VIEWPORT API */
+
+ virtual RID viewport_data_create()=0;
+
+ virtual RID render_target_create()=0;
+ virtual void render_target_set_size(RID p_render_target, int p_width, int p_height)=0;
+ virtual RID render_target_get_texture(RID p_render_target) const=0;
+ virtual bool render_target_renedered_in_frame(RID p_render_target)=0;
+
+ virtual void begin_frame()=0;
+
+ virtual void set_viewport(const VS::ViewportRect& p_viewport)=0;
+ virtual void set_render_target(RID p_render_target)=0;
+ virtual void clear_viewport(const Color& p_color)=0;
+ virtual void capture_viewport(Image* r_capture)=0;
+
+ virtual void begin_scene(RID p_viewport_data,RID p_env,VS::ScenarioDebugMode p_debug)=0;
+ virtual void begin_shadow_map( RID p_light_instance, int p_shadow_pass )=0;
+
+ virtual void set_camera(const Transform& p_world,const CameraMatrix& p_projection)=0;
+
+ virtual void add_light( RID p_light_instance )=0; ///< all "add_light" calls happen before add_geometry calls
+
+ typedef Map<StringName,Variant> ParamOverrideMap;
+
+
+ struct InstanceData {
+
+ Transform transform;
+ RID skeleton;
+ RID material_override;
+ Vector<RID> light_instances;
+ Vector<float> morph_values;
+ bool mirror :8;
+ bool depth_scale :8;
+ bool billboard :8;
+ bool billboard_y :8;
+
+ };
+
+ virtual void add_mesh( const RID& p_mesh, const InstanceData *p_data)=0;
+ virtual void add_multimesh( const RID& p_multimesh, const InstanceData *p_data)=0;
+ virtual void add_particles( const RID& p_particle_instance, const InstanceData *p_data)=0;
+
+
+ virtual void end_scene()=0;
+ virtual void end_shadow_map()=0;
+
+ virtual void end_frame()=0;
+ virtual void flush_frame(); //not necesary in most cases
+
+ /* CANVAS API */
+
+ enum CanvasRectFlags {
+
+ CANVAS_RECT_REGION=1,
+ CANVAS_RECT_TILE=2,
+ CANVAS_RECT_FLIP_H=4,
+ CANVAS_RECT_FLIP_V=8
+ };
+
+ virtual void canvas_begin()=0;
+ virtual void canvas_set_opacity(float p_opacity)=0;
+ virtual void canvas_set_blend_mode(VS::MaterialBlendMode p_mode)=0;
+ virtual void canvas_begin_rect(const Matrix32& p_transform)=0;;
+ virtual void canvas_set_clip(bool p_clip, const Rect2& p_rect)=0;
+ virtual void canvas_end_rect()=0;
+ virtual void canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width)=0;
+ virtual void canvas_draw_rect(const Rect2& p_rect, int p_flags, const Rect2& p_source,RID p_texture,const Color& p_modulate)=0;
+ virtual void canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margins, bool p_draw_center=true,const Color& p_modulate=Color(1,1,1))=0;
+ virtual void canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width)=0;
+ virtual void canvas_draw_polygon(int p_vertex_count, const int* p_indices, const Vector2* p_vertices, const Vector2* p_uvs, const Color* p_colors,const RID& p_texture,bool p_singlecolor)=0;
+ virtual void canvas_set_transform(const Matrix32& p_transform)=0;
+
+ /* ENVIRONMENT */
+
+
+ virtual RID environment_create()=0;
+
+ virtual void environment_set_background(RID p_env,VS::EnvironmentBG p_bg)=0;
+ virtual VS::EnvironmentBG environment_get_background(RID p_env) const=0;
+
+ virtual void environment_set_background_param(RID p_env,VS::EnvironmentBGParam p_param, const Variant& p_value)=0;
+ virtual Variant environment_get_background_param(RID p_env,VS::EnvironmentBGParam p_param) const=0;
+
+ virtual void environment_set_enable_fx(RID p_env,VS::EnvironmentFx p_effect,bool p_enabled)=0;
+ virtual bool environment_is_fx_enabled(RID p_env,VS::EnvironmentFx p_effect) const=0;
+
+ virtual void environment_fx_set_param(RID p_env,VS::EnvironmentFxParam p_param,const Variant& p_value)=0;
+ virtual Variant environment_fx_get_param(RID p_env,VS::EnvironmentFxParam p_param) const=0;
+
+
+ /*MISC*/
+
+ virtual bool is_texture(const RID& p_rid) const=0;
+ virtual bool is_material(const RID& p_rid) const=0;
+ virtual bool is_mesh(const RID& p_rid) const=0;
+ virtual bool is_multimesh(const RID& p_rid) const=0;
+ virtual bool is_particles(const RID &p_beam) const=0;
+
+ virtual bool is_light(const RID& p_rid) const=0;
+ virtual bool is_light_instance(const RID& p_rid) const=0;
+ virtual bool is_particles_instance(const RID& p_rid) const=0;
+ virtual bool is_skeleton(const RID& p_rid) const=0;
+ virtual bool is_environment(const RID& p_rid) const=0;
+ virtual bool is_shader(const RID& p_rid) const=0;
+
+ virtual void free(const RID& p_rid)=0;
+
+ virtual void init()=0;
+ virtual void finish()=0;
+
+ virtual bool needs_to_draw_next_frame() const=0;
+
+ virtual void reload_vram() {}
+
+ virtual bool has_feature(VS::Features p_feature) const=0;
+
+
+ virtual int get_render_info(VS::RenderInfo p_info)=0;
+
+ Rasterizer();
+ virtual ~Rasterizer() {}
+};
+
+
+
+#endif
diff --git a/servers/visual/rasterizer_dummy.cpp b/servers/visual/rasterizer_dummy.cpp
new file mode 100644
index 0000000000..694d365d79
--- /dev/null
+++ b/servers/visual/rasterizer_dummy.cpp
@@ -0,0 +1,1786 @@
+/*************************************************************************/
+/* rasterizer_dummy.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "rasterizer_dummy.h"
+
+/* TEXTURE API */
+
+
+RID RasterizerDummy::texture_create() {
+
+ Texture *texture = memnew(Texture);
+ ERR_FAIL_COND_V(!texture,RID());
+ return texture_owner.make_rid( texture );
+
+}
+
+void RasterizerDummy::texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags) {
+
+
+ Texture *texture = texture_owner.get( p_texture );
+ ERR_FAIL_COND(!texture);
+ texture->width=p_width;
+ texture->height=p_height;
+ texture->format=p_format;
+ texture->flags=p_flags;
+}
+
+void RasterizerDummy::texture_set_data(RID p_texture,const Image& p_image,VS::CubeMapSide p_cube_side) {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND(!texture);
+ ERR_FAIL_COND(texture->format != p_image.get_format() );
+
+ texture->image[p_cube_side]=p_image;
+
+}
+
+Image RasterizerDummy::texture_get_data(RID p_texture,VS::CubeMapSide p_cube_side) const {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND_V(!texture,Image());
+
+ return texture->image[p_cube_side];
+}
+
+void RasterizerDummy::texture_set_flags(RID p_texture,uint32_t p_flags) {
+
+ Texture *texture = texture_owner.get( p_texture );
+ ERR_FAIL_COND(!texture);
+ uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP;
+ texture->flags=p_flags|cube; // can't remove a cube from being a cube
+
+}
+uint32_t RasterizerDummy::texture_get_flags(RID p_texture) const {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND_V(!texture,0);
+
+ return texture->flags;
+
+}
+Image::Format RasterizerDummy::texture_get_format(RID p_texture) const {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND_V(!texture,Image::FORMAT_GRAYSCALE);
+
+ return texture->format;
+}
+uint32_t RasterizerDummy::texture_get_width(RID p_texture) const {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND_V(!texture,0);
+
+ return texture->width;
+}
+uint32_t RasterizerDummy::texture_get_height(RID p_texture) const {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND_V(!texture,0);
+
+ return texture->height;
+}
+
+bool RasterizerDummy::texture_has_alpha(RID p_texture) const {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND_V(!texture,0);
+
+ return false;
+
+}
+
+void RasterizerDummy::texture_set_size_override(RID p_texture,int p_width, int p_height) {
+
+ Texture * texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND(!texture);
+
+ ERR_FAIL_COND(p_width<=0 || p_width>4096);
+ ERR_FAIL_COND(p_height<=0 || p_height>4096);
+ //real texture size is in alloc width and height
+// texture->width=p_width;
+// texture->height=p_height;
+
+}
+
+void RasterizerDummy::texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const {
+
+
+}
+
+/* SHADER API */
+
+/* SHADER API */
+
+RID RasterizerDummy::shader_create(VS::ShaderMode p_mode) {
+
+ Shader *shader = memnew( Shader );
+ shader->mode=p_mode;
+ shader->fragment_line=0;
+ shader->vertex_line=0;
+ RID rid = shader_owner.make_rid(shader);
+
+ return rid;
+
+}
+
+
+
+void RasterizerDummy::shader_set_mode(RID p_shader,VS::ShaderMode p_mode) {
+
+ ERR_FAIL_INDEX(p_mode,3);
+ Shader *shader=shader_owner.get(p_shader);
+ ERR_FAIL_COND(!shader);
+ shader->mode=p_mode;
+
+}
+VS::ShaderMode RasterizerDummy::shader_get_mode(RID p_shader) const {
+
+ Shader *shader=shader_owner.get(p_shader);
+ ERR_FAIL_COND_V(!shader,VS::SHADER_MATERIAL);
+ return shader->mode;
+}
+
+
+
+void RasterizerDummy::shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs,int p_fragment_ofs) {
+
+ Shader *shader=shader_owner.get(p_shader);
+ ERR_FAIL_COND(!shader);
+ shader->fragment_code=p_fragment;
+ shader->vertex_code=p_vertex;
+ shader->fragment_line=p_fragment_ofs;
+ shader->vertex_line=p_vertex_ofs;
+
+}
+
+
+String RasterizerDummy::shader_get_vertex_code(RID p_shader) const {
+
+ Shader *shader=shader_owner.get(p_shader);
+ ERR_FAIL_COND_V(!shader,String());
+ return shader->vertex_code;
+
+}
+
+String RasterizerDummy::shader_get_fragment_code(RID p_shader) const {
+
+ Shader *shader=shader_owner.get(p_shader);
+ ERR_FAIL_COND_V(!shader,String());
+ return shader->fragment_code;
+
+}
+
+void RasterizerDummy::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const {
+
+ Shader *shader=shader_owner.get(p_shader);
+ ERR_FAIL_COND(!shader);
+
+}
+
+/* COMMON MATERIAL API */
+
+
+RID RasterizerDummy::material_create() {
+
+ return material_owner.make_rid( memnew( Material ) );
+}
+
+void RasterizerDummy::material_set_shader(RID p_material, RID p_shader) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+ material->shader=p_shader;
+
+}
+
+RID RasterizerDummy::material_get_shader(RID p_material) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,RID());
+ return material->shader;
+}
+
+void RasterizerDummy::material_set_param(RID p_material, const StringName& p_param, const Variant& p_value) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+
+ if (p_value.get_type()==Variant::NIL)
+ material->shader_params.erase(p_param);
+ else
+ material->shader_params[p_param]=p_value;
+}
+Variant RasterizerDummy::material_get_param(RID p_material, const StringName& p_param) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,Variant());
+
+ if (material->shader_params.has(p_param))
+ return material->shader_params[p_param];
+ else
+ return Variant();
+}
+
+
+void RasterizerDummy::material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+ ERR_FAIL_INDEX(p_flag,VS::MATERIAL_FLAG_MAX);
+ material->flags[p_flag]=p_enabled;
+
+}
+bool RasterizerDummy::material_get_flag(RID p_material,VS::MaterialFlag p_flag) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,false);
+ ERR_FAIL_INDEX_V(p_flag,VS::MATERIAL_FLAG_MAX,false);
+ return material->flags[p_flag];
+
+
+}
+
+void RasterizerDummy::material_set_hint(RID p_material, VS::MaterialHint p_hint,bool p_enabled) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+ ERR_FAIL_INDEX(p_hint,VS::MATERIAL_HINT_MAX);
+ material->hints[p_hint]=p_enabled;
+
+}
+
+bool RasterizerDummy::material_get_hint(RID p_material,VS::MaterialHint p_hint) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,false);
+ ERR_FAIL_INDEX_V(p_hint,VS::MATERIAL_HINT_MAX,false);
+ return material->hints[p_hint];
+
+}
+
+void RasterizerDummy::material_set_shade_model(RID p_material, VS::MaterialShadeModel p_model) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+ material->shade_model=p_model;
+
+};
+
+VS::MaterialShadeModel RasterizerDummy::material_get_shade_model(RID p_material) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,VS::MATERIAL_SHADE_MODEL_LAMBERT);
+ return material->shade_model;
+};
+
+
+void RasterizerDummy::material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+ material->blend_mode=p_mode;
+
+}
+VS::MaterialBlendMode RasterizerDummy::material_get_blend_mode(RID p_material) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,VS::MATERIAL_BLEND_MODE_ADD);
+ return material->blend_mode;
+}
+
+void RasterizerDummy::material_set_line_width(RID p_material,float p_line_width) {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND(!material);
+ material->line_width=p_line_width;
+
+}
+float RasterizerDummy::material_get_line_width(RID p_material) const {
+
+ Material *material = material_owner.get(p_material);
+ ERR_FAIL_COND_V(!material,0);
+
+ return material->line_width;
+}
+
+/* MESH API */
+
+
+RID RasterizerDummy::mesh_create() {
+
+
+ return mesh_owner.make_rid( memnew( Mesh ) );
+}
+
+
+void RasterizerDummy::mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes,bool p_alpha_sort) {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND(!mesh);
+
+ ERR_FAIL_INDEX( p_primitive, VS::PRIMITIVE_MAX );
+ ERR_FAIL_COND(p_arrays.size()!=VS::ARRAY_MAX);
+
+ Surface s;
+
+
+ s.format=0;
+
+ for(int i=0;i<p_arrays.size();i++) {
+
+ if (p_arrays[i].get_type()==Variant::NIL)
+ continue;
+
+ s.format|=(1<<i);
+
+ if (i==VS::ARRAY_VERTEX) {
+
+ Vector3Array v = p_arrays[i];
+ int len = v.size();
+ ERR_FAIL_COND(len==0);
+ Vector3Array::Read r = v.read();
+
+
+ for(int i=0;i<len;i++) {
+
+ if (i==0)
+ s.aabb.pos=r[0];
+ else
+ s.aabb.expand_to(r[i]);
+ }
+
+ }
+ }
+
+ ERR_FAIL_COND((s.format&VS::ARRAY_FORMAT_VERTEX)==0); // mandatory
+
+ s.data=p_arrays;
+ s.morph_data=p_blend_shapes;
+ s.primitive=p_primitive;
+ s.alpha_sort=p_alpha_sort;
+ s.morph_target_count=mesh->morph_target_count;
+ s.morph_format=s.format;
+
+
+ Surface *surface = memnew( Surface );
+ *surface=s;
+
+ mesh->surfaces.push_back(surface);
+
+
+}
+
+
+
+void RasterizerDummy::mesh_add_custom_surface(RID p_mesh,const Variant& p_dat) {
+
+ ERR_EXPLAIN("Dummy Rasterizer does not support custom surfaces. Running on wrong platform?");
+ ERR_FAIL_V();
+}
+
+Array RasterizerDummy::mesh_get_surface_arrays(RID p_mesh,int p_surface) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,Array());
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Array() );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, Array() );
+
+ return surface->data;
+
+
+}
+Array RasterizerDummy::mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const{
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,Array());
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Array() );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, Array() );
+
+ return surface->morph_data;
+
+}
+
+
+void RasterizerDummy::mesh_set_morph_target_count(RID p_mesh,int p_amount) {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND(!mesh);
+ ERR_FAIL_COND( mesh->surfaces.size()!=0 );
+
+ mesh->morph_target_count=p_amount;
+
+}
+
+int RasterizerDummy::mesh_get_morph_target_count(RID p_mesh) const{
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,-1);
+
+ return mesh->morph_target_count;
+
+}
+
+void RasterizerDummy::mesh_set_morph_target_mode(RID p_mesh,VS::MorphTargetMode p_mode) {
+
+ ERR_FAIL_INDEX(p_mode,2);
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND(!mesh);
+
+ mesh->morph_target_mode=p_mode;
+
+}
+
+VS::MorphTargetMode RasterizerDummy::mesh_get_morph_target_mode(RID p_mesh) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,VS::MORPH_MODE_NORMALIZED);
+
+ return mesh->morph_target_mode;
+
+}
+
+
+
+void RasterizerDummy::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned) {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND(!mesh);
+ ERR_FAIL_INDEX(p_surface, mesh->surfaces.size() );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND( !surface);
+
+ if (surface->material_owned && surface->material.is_valid())
+ free(surface->material);
+
+ surface->material_owned=p_owned;
+ surface->material=p_material;
+}
+
+RID RasterizerDummy::mesh_surface_get_material(RID p_mesh, int p_surface) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,RID());
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), RID() );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, RID() );
+
+ return surface->material;
+}
+
+int RasterizerDummy::mesh_surface_get_array_len(RID p_mesh, int p_surface) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,-1);
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), -1 );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, -1 );
+
+ Vector3Array arr = surface->data[VS::ARRAY_VERTEX];
+ return arr.size();
+
+}
+
+int RasterizerDummy::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,-1);
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), -1 );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, -1 );
+
+ IntArray arr = surface->data[VS::ARRAY_INDEX];
+ return arr.size();
+
+}
+uint32_t RasterizerDummy::mesh_surface_get_format(RID p_mesh, int p_surface) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,0);
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0 );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, 0 );
+
+ return surface->format;
+}
+VS::PrimitiveType RasterizerDummy::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,VS::PRIMITIVE_POINTS);
+ ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), VS::PRIMITIVE_POINTS );
+ Surface *surface = mesh->surfaces[p_surface];
+ ERR_FAIL_COND_V( !surface, VS::PRIMITIVE_POINTS );
+
+ return surface->primitive;
+}
+
+void RasterizerDummy::mesh_remove_surface(RID p_mesh,int p_index) {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND(!mesh);
+ ERR_FAIL_INDEX(p_index, mesh->surfaces.size() );
+ Surface *surface = mesh->surfaces[p_index];
+ ERR_FAIL_COND( !surface);
+
+ memdelete( mesh->surfaces[p_index] );
+ mesh->surfaces.remove(p_index);
+
+}
+int RasterizerDummy::mesh_get_surface_count(RID p_mesh) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,-1);
+
+ return mesh->surfaces.size();
+}
+
+AABB RasterizerDummy::mesh_get_aabb(RID p_mesh) const {
+
+ Mesh *mesh = mesh_owner.get( p_mesh );
+ ERR_FAIL_COND_V(!mesh,AABB());
+
+ AABB aabb;
+
+ for (int i=0;i<mesh->surfaces.size();i++) {
+
+ if (i==0)
+ aabb=mesh->surfaces[i]->aabb;
+ else
+ aabb.merge_with(mesh->surfaces[i]->aabb);
+ }
+
+ return aabb;
+}
+
+/* MULTIMESH API */
+
+RID RasterizerDummy::multimesh_create() {
+
+ return multimesh_owner.make_rid( memnew( MultiMesh ));
+}
+
+void RasterizerDummy::multimesh_set_instance_count(RID p_multimesh,int p_count) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+
+ multimesh->elements.clear(); // make sure to delete everything, so it "fails" in all implementations
+ multimesh->elements.resize(p_count);
+
+}
+int RasterizerDummy::multimesh_get_instance_count(RID p_multimesh) const {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh,-1);
+
+ return multimesh->elements.size();
+}
+
+void RasterizerDummy::multimesh_set_mesh(RID p_multimesh,RID p_mesh) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+
+ multimesh->mesh=p_mesh;
+
+}
+void RasterizerDummy::multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+ multimesh->aabb=p_aabb;
+}
+void RasterizerDummy::multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+ ERR_FAIL_INDEX(p_index,multimesh->elements.size());
+ multimesh->elements[p_index].xform=p_transform;
+
+}
+void RasterizerDummy::multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND(!multimesh)
+ ERR_FAIL_INDEX(p_index,multimesh->elements.size());
+ multimesh->elements[p_index].color=p_color;
+
+}
+
+RID RasterizerDummy::multimesh_get_mesh(RID p_multimesh) const {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh,RID());
+
+ return multimesh->mesh;
+}
+AABB RasterizerDummy::multimesh_get_aabb(RID p_multimesh) const {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh,AABB());
+
+ return multimesh->aabb;
+}
+
+Transform RasterizerDummy::multimesh_instance_get_transform(RID p_multimesh,int p_index) const {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh,Transform());
+
+ ERR_FAIL_INDEX_V(p_index,multimesh->elements.size(),Transform());
+
+ return multimesh->elements[p_index].xform;
+
+}
+Color RasterizerDummy::multimesh_instance_get_color(RID p_multimesh,int p_index) const {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh,Color());
+ ERR_FAIL_INDEX_V(p_index,multimesh->elements.size(),Color());
+
+ return multimesh->elements[p_index].color;
+}
+
+void RasterizerDummy::multimesh_set_visible_instances(RID p_multimesh,int p_visible) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+ multimesh->visible=p_visible;
+
+}
+
+int RasterizerDummy::multimesh_get_visible_instances(RID p_multimesh) const {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh,-1);
+ return multimesh->visible;
+
+}
+
+
+/* PARTICLES API */
+
+RID RasterizerDummy::particles_create() {
+
+ Particles *particles = memnew( Particles );
+ ERR_FAIL_COND_V(!particles,RID());
+ return particles_owner.make_rid(particles);
+}
+
+void RasterizerDummy::particles_set_amount(RID p_particles, int p_amount) {
+
+ ERR_FAIL_COND(p_amount<1);
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.amount=p_amount;
+
+}
+
+int RasterizerDummy::particles_get_amount(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,-1);
+ return particles->data.amount;
+
+}
+
+void RasterizerDummy::particles_set_emitting(RID p_particles, bool p_emitting) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.emitting=p_emitting;;
+
+}
+bool RasterizerDummy::particles_is_emitting(RID p_particles) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,false);
+ return particles->data.emitting;
+
+}
+
+void RasterizerDummy::particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.visibility_aabb=p_visibility;
+
+}
+
+void RasterizerDummy::particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+
+ particles->data.emission_half_extents=p_half_extents;
+}
+Vector3 RasterizerDummy::particles_get_emission_half_extents(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,Vector3());
+
+ return particles->data.emission_half_extents;
+}
+
+void RasterizerDummy::particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+
+ particles->data.emission_base_velocity=p_base_velocity;
+}
+
+Vector3 RasterizerDummy::particles_get_emission_base_velocity(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,Vector3());
+
+ return particles->data.emission_base_velocity;
+}
+
+
+void RasterizerDummy::particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+
+ particles->data.emission_points=p_points;
+}
+
+DVector<Vector3> RasterizerDummy::particles_get_emission_points(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,DVector<Vector3>());
+
+ return particles->data.emission_points;
+
+}
+
+void RasterizerDummy::particles_set_gravity_normal(RID p_particles, const Vector3& p_normal) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+
+ particles->data.gravity_normal=p_normal;
+
+}
+Vector3 RasterizerDummy::particles_get_gravity_normal(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,Vector3());
+
+ return particles->data.gravity_normal;
+}
+
+
+AABB RasterizerDummy::particles_get_visibility_aabb(RID p_particles) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,AABB());
+ return particles->data.visibility_aabb;
+
+}
+
+void RasterizerDummy::particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value) {
+
+ ERR_FAIL_INDEX(p_variable,VS::PARTICLE_VAR_MAX);
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.particle_vars[p_variable]=p_value;
+
+}
+float RasterizerDummy::particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,-1);
+ return particles->data.particle_vars[p_variable];
+}
+
+void RasterizerDummy::particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.particle_randomness[p_variable]=p_randomness;
+
+}
+float RasterizerDummy::particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,-1);
+ return particles->data.particle_randomness[p_variable];
+
+}
+
+void RasterizerDummy::particles_set_color_phases(RID p_particles, int p_phases) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND( p_phases<0 || p_phases>VS::MAX_PARTICLE_COLOR_PHASES );
+ particles->data.color_phase_count=p_phases;
+
+}
+int RasterizerDummy::particles_get_color_phases(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,-1);
+ return particles->data.color_phase_count;
+}
+
+
+void RasterizerDummy::particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos) {
+
+ ERR_FAIL_INDEX(p_phase, VS::MAX_PARTICLE_COLOR_PHASES);
+ if (p_pos<0.0)
+ p_pos=0.0;
+ if (p_pos>1.0)
+ p_pos=1.0;
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.color_phases[p_phase].pos=p_pos;
+
+}
+float RasterizerDummy::particles_get_color_phase_pos(RID p_particles, int p_phase) const {
+
+ ERR_FAIL_INDEX_V(p_phase, VS::MAX_PARTICLE_COLOR_PHASES, -1.0);
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,-1);
+ return particles->data.color_phases[p_phase].pos;
+
+}
+
+void RasterizerDummy::particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color) {
+
+ ERR_FAIL_INDEX(p_phase, VS::MAX_PARTICLE_COLOR_PHASES);
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.color_phases[p_phase].color=p_color;
+
+ //update alpha
+ particles->has_alpha=false;
+ for(int i=0;i<VS::MAX_PARTICLE_COLOR_PHASES;i++) {
+ if (particles->data.color_phases[i].color.a<0.99)
+ particles->has_alpha=true;
+ }
+
+}
+
+Color RasterizerDummy::particles_get_color_phase_color(RID p_particles, int p_phase) const {
+
+ ERR_FAIL_INDEX_V(p_phase, VS::MAX_PARTICLE_COLOR_PHASES, Color());
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,Color());
+ return particles->data.color_phases[p_phase].color;
+
+}
+
+void RasterizerDummy::particles_set_attractors(RID p_particles, int p_attractors) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND( p_attractors<0 || p_attractors>VisualServer::MAX_PARTICLE_ATTRACTORS );
+ particles->data.attractor_count=p_attractors;
+
+}
+int RasterizerDummy::particles_get_attractors(RID p_particles) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,-1);
+ return particles->data.attractor_count;
+}
+
+void RasterizerDummy::particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ ERR_FAIL_INDEX(p_attractor,particles->data.attractor_count);
+ particles->data.attractors[p_attractor].pos=p_pos;;
+}
+Vector3 RasterizerDummy::particles_get_attractor_pos(RID p_particles,int p_attractor) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,Vector3());
+ ERR_FAIL_INDEX_V(p_attractor,particles->data.attractor_count,Vector3());
+ return particles->data.attractors[p_attractor].pos;
+}
+
+void RasterizerDummy::particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ ERR_FAIL_INDEX(p_attractor,particles->data.attractor_count);
+ particles->data.attractors[p_attractor].force=p_force;
+}
+
+float RasterizerDummy::particles_get_attractor_strength(RID p_particles,int p_attractor) const {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,0);
+ ERR_FAIL_INDEX_V(p_attractor,particles->data.attractor_count,0);
+ return particles->data.attractors[p_attractor].force;
+}
+
+void RasterizerDummy::particles_set_material(RID p_particles, RID p_material,bool p_owned) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ if (particles->material_owned && particles->material.is_valid())
+ free(particles->material);
+
+ particles->material_owned=p_owned;
+
+ particles->material=p_material;
+
+}
+RID RasterizerDummy::particles_get_material(RID p_particles) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,RID());
+ return particles->material;
+
+}
+
+void RasterizerDummy::particles_set_use_local_coordinates(RID p_particles, bool p_enable) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.local_coordinates=p_enable;
+
+}
+
+bool RasterizerDummy::particles_is_using_local_coordinates(RID p_particles) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,false);
+ return particles->data.local_coordinates;
+}
+bool RasterizerDummy::particles_has_height_from_velocity(RID p_particles) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,false);
+ return particles->data.height_from_velocity;
+}
+
+void RasterizerDummy::particles_set_height_from_velocity(RID p_particles, bool p_enable) {
+
+ Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND(!particles);
+ particles->data.height_from_velocity=p_enable;
+
+}
+
+AABB RasterizerDummy::particles_get_aabb(RID p_particles) const {
+
+ const Particles* particles = particles_owner.get( p_particles );
+ ERR_FAIL_COND_V(!particles,AABB());
+ return particles->data.visibility_aabb;
+}
+
+/* SKELETON API */
+
+RID RasterizerDummy::skeleton_create() {
+
+ Skeleton *skeleton = memnew( Skeleton );
+ ERR_FAIL_COND_V(!skeleton,RID());
+ return skeleton_owner.make_rid( skeleton );
+}
+void RasterizerDummy::skeleton_resize(RID p_skeleton,int p_bones) {
+
+ Skeleton *skeleton = skeleton_owner.get( p_skeleton );
+ ERR_FAIL_COND(!skeleton);
+ if (p_bones == skeleton->bones.size()) {
+ return;
+ };
+
+ skeleton->bones.resize(p_bones);
+
+}
+int RasterizerDummy::skeleton_get_bone_count(RID p_skeleton) const {
+
+ Skeleton *skeleton = skeleton_owner.get( p_skeleton );
+ ERR_FAIL_COND_V(!skeleton, -1);
+ return skeleton->bones.size();
+}
+void RasterizerDummy::skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform) {
+
+ Skeleton *skeleton = skeleton_owner.get( p_skeleton );
+ ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_INDEX( p_bone, skeleton->bones.size() );
+
+ skeleton->bones[p_bone] = p_transform;
+}
+
+Transform RasterizerDummy::skeleton_bone_get_transform(RID p_skeleton,int p_bone) {
+
+ Skeleton *skeleton = skeleton_owner.get( p_skeleton );
+ ERR_FAIL_COND_V(!skeleton, Transform());
+ ERR_FAIL_INDEX_V( p_bone, skeleton->bones.size(), Transform() );
+
+ // something
+ return skeleton->bones[p_bone];
+}
+
+
+/* LIGHT API */
+
+RID RasterizerDummy::light_create(VS::LightType p_type) {
+
+ Light *light = memnew( Light );
+ light->type=p_type;
+ return light_owner.make_rid(light);
+}
+
+VS::LightType RasterizerDummy::light_get_type(RID p_light) const {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND_V(!light,VS::LIGHT_OMNI);
+ return light->type;
+}
+
+void RasterizerDummy::light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color) {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND(!light);
+ ERR_FAIL_INDEX( p_type, 3 );
+ light->colors[p_type]=p_color;
+}
+Color RasterizerDummy::light_get_color(RID p_light,VS::LightColor p_type) const {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND_V(!light, Color());
+ ERR_FAIL_INDEX_V( p_type, 3, Color() );
+ return light->colors[p_type];
+}
+
+void RasterizerDummy::light_set_shadow(RID p_light,bool p_enabled) {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND(!light);
+ light->shadow_enabled=p_enabled;
+}
+
+bool RasterizerDummy::light_has_shadow(RID p_light) const {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND_V(!light,false);
+ return light->shadow_enabled;
+}
+
+void RasterizerDummy::light_set_volumetric(RID p_light,bool p_enabled) {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND(!light);
+ light->volumetric_enabled=p_enabled;
+
+}
+bool RasterizerDummy::light_is_volumetric(RID p_light) const {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND_V(!light,false);
+ return light->volumetric_enabled;
+}
+
+void RasterizerDummy::light_set_projector(RID p_light,RID p_texture) {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND(!light);
+ light->projector=p_texture;
+}
+RID RasterizerDummy::light_get_projector(RID p_light) const {
+
+ Light *light = light_owner.get(p_light);
+ ERR_FAIL_COND_V(!light,RID());
+ return light->projector;
+}
+
+void RasterizerDummy::light_set_var(RID p_light, VS::LightParam p_var, float p_value) {
+
+ Light * light = light_owner.get( p_light );
+ ERR_FAIL_COND(!light);
+ ERR_FAIL_INDEX( p_var, VS::LIGHT_PARAM_MAX );
+
+ light->vars[p_var]=p_value;
+}
+float RasterizerDummy::light_get_var(RID p_light, VS::LightParam p_var) const {
+
+ Light * light = light_owner.get( p_light );
+ ERR_FAIL_COND_V(!light,0);
+
+ ERR_FAIL_INDEX_V( p_var, VS::LIGHT_PARAM_MAX,0 );
+
+ return light->vars[p_var];
+}
+
+void RasterizerDummy::light_set_operator(RID p_light,VS::LightOp p_op) {
+
+ Light * light = light_owner.get( p_light );
+ ERR_FAIL_COND(!light);
+
+
+};
+
+VS::LightOp RasterizerDummy::light_get_operator(RID p_light) const {
+
+ return VS::LightOp(0);
+};
+
+void RasterizerDummy::light_omni_set_shadow_mode(RID p_light,VS::LightOmniShadowMode p_mode) {
+
+
+}
+
+VS::LightOmniShadowMode RasterizerDummy::light_omni_get_shadow_mode(RID p_light) const{
+
+ return VS::LightOmniShadowMode(0);
+}
+
+void RasterizerDummy::light_directional_set_shadow_mode(RID p_light,VS::LightDirectionalShadowMode p_mode) {
+
+
+}
+
+VS::LightDirectionalShadowMode RasterizerDummy::light_directional_get_shadow_mode(RID p_light) const {
+
+ return VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
+}
+
+void RasterizerDummy::light_directional_set_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param, float p_value) {
+
+
+}
+
+float RasterizerDummy::light_directional_get_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param) const {
+
+ return 0;
+}
+
+
+AABB RasterizerDummy::light_get_aabb(RID p_light) const {
+
+ Light *light = light_owner.get( p_light );
+ ERR_FAIL_COND_V(!light,AABB());
+
+ switch( light->type ) {
+
+ case VS::LIGHT_SPOT: {
+
+ float len=light->vars[VS::LIGHT_PARAM_RADIUS];
+ float size=Math::tan(Math::deg2rad(light->vars[VS::LIGHT_PARAM_SPOT_ANGLE]))*len;
+ return AABB( Vector3( -size,-size,-len ), Vector3( size*2, size*2, len ) );
+ } break;
+ case VS::LIGHT_OMNI: {
+
+ float r = light->vars[VS::LIGHT_PARAM_RADIUS];
+ return AABB( -Vector3(r,r,r), Vector3(r,r,r)*2 );
+ } break;
+ case VS::LIGHT_DIRECTIONAL: {
+
+ return AABB();
+ } break;
+ default: {}
+ }
+
+ ERR_FAIL_V( AABB() );
+}
+
+
+RID RasterizerDummy::light_instance_create(RID p_light) {
+
+ Light *light = light_owner.get( p_light );
+ ERR_FAIL_COND_V(!light, RID());
+
+ LightInstance *light_instance = memnew( LightInstance );
+
+ light_instance->light=p_light;
+ light_instance->base=light;
+
+
+ return light_instance_owner.make_rid( light_instance );
+}
+void RasterizerDummy::light_instance_set_transform(RID p_light_instance,const Transform& p_transform) {
+
+ LightInstance *lighti = light_instance_owner.get( p_light_instance );
+ ERR_FAIL_COND(!lighti);
+ lighti->transform=p_transform;
+
+}
+
+bool RasterizerDummy::light_instance_has_shadow(RID p_light_instance) const {
+
+ return false;
+
+}
+
+
+bool RasterizerDummy::light_instance_assign_shadow(RID p_light_instance) {
+
+ return false;
+
+}
+
+
+Rasterizer::ShadowType RasterizerDummy::light_instance_get_shadow_type(RID p_light_instance) const {
+
+ LightInstance *lighti = light_instance_owner.get( p_light_instance );
+ ERR_FAIL_COND_V(!lighti,Rasterizer::SHADOW_NONE);
+
+ switch(lighti->base->type) {
+
+ case VS::LIGHT_DIRECTIONAL: return SHADOW_PSM; break;
+ case VS::LIGHT_OMNI: return SHADOW_DUAL_PARABOLOID; break;
+ case VS::LIGHT_SPOT: return SHADOW_SIMPLE; break;
+ }
+
+ return Rasterizer::SHADOW_NONE;
+}
+
+Rasterizer::ShadowType RasterizerDummy::light_instance_get_shadow_type(RID p_light_instance,bool p_far) const {
+
+ return SHADOW_NONE;
+}
+void RasterizerDummy::light_instance_set_shadow_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near,float p_split_far) {
+
+
+}
+
+int RasterizerDummy::light_instance_get_shadow_passes(RID p_light_instance) const {
+
+ return 0;
+}
+
+void RasterizerDummy::light_instance_set_custom_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near,float p_split_far) {
+
+ LightInstance *lighti = light_instance_owner.get( p_light_instance );
+ ERR_FAIL_COND(!lighti);
+
+ ERR_FAIL_COND(lighti->base->type!=VS::LIGHT_DIRECTIONAL);
+ ERR_FAIL_INDEX(p_index,1);
+
+ lighti->custom_projection=p_camera;
+ lighti->custom_transform=p_transform;
+
+}
+void RasterizerDummy::shadow_clear_near() {
+
+
+}
+
+bool RasterizerDummy::shadow_allocate_near(RID p_light) {
+
+ return false;
+}
+
+bool RasterizerDummy::shadow_allocate_far(RID p_light) {
+
+ return false;
+}
+
+/* PARTICLES INSTANCE */
+
+RID RasterizerDummy::particles_instance_create(RID p_particles) {
+
+ ERR_FAIL_COND_V(!particles_owner.owns(p_particles),RID());
+ ParticlesInstance *particles_instance = memnew( ParticlesInstance );
+ ERR_FAIL_COND_V(!particles_instance, RID() );
+ particles_instance->particles=p_particles;
+ return particles_instance_owner.make_rid(particles_instance);
+}
+
+void RasterizerDummy::particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform) {
+
+ ParticlesInstance *particles_instance=particles_instance_owner.get(p_particles_instance);
+ ERR_FAIL_COND(!particles_instance);
+ particles_instance->transform=p_transform;
+}
+
+
+/* RENDER API */
+/* all calls (inside begin/end shadow) are always warranted to be in the following order: */
+
+
+RID RasterizerDummy::viewport_data_create() {
+
+ return RID();
+}
+
+RID RasterizerDummy::render_target_create(){
+
+ return RID();
+
+}
+void RasterizerDummy::render_target_set_size(RID p_render_target, int p_width, int p_height){
+
+
+}
+RID RasterizerDummy::render_target_get_texture(RID p_render_target) const{
+
+ return RID();
+
+}
+bool RasterizerDummy::render_target_renedered_in_frame(RID p_render_target){
+
+ return false;
+}
+
+
+void RasterizerDummy::begin_frame() {
+
+
+
+}
+
+void RasterizerDummy::capture_viewport(Image* r_capture) {
+
+
+}
+
+
+void RasterizerDummy::clear_viewport(const Color& p_color) {
+
+
+};
+
+void RasterizerDummy::set_viewport(const VS::ViewportRect& p_viewport) {
+
+
+
+}
+
+void RasterizerDummy::set_render_target(RID p_render_target) {
+
+
+}
+
+
+void RasterizerDummy::begin_scene(RID p_viewport_data,RID p_env,VS::ScenarioDebugMode p_debug) {
+
+
+};
+
+void RasterizerDummy::begin_shadow_map( RID p_light_instance, int p_shadow_pass ) {
+
+}
+
+void RasterizerDummy::set_camera(const Transform& p_world,const CameraMatrix& p_projection) {
+
+
+}
+
+void RasterizerDummy::add_light( RID p_light_instance ) {
+
+
+
+}
+
+
+
+
+void RasterizerDummy::add_mesh( const RID& p_mesh, const InstanceData *p_data) {
+
+
+}
+
+void RasterizerDummy::add_multimesh( const RID& p_multimesh, const InstanceData *p_data){
+
+
+
+
+}
+
+void RasterizerDummy::add_particles( const RID& p_particle_instance, const InstanceData *p_data){
+
+
+
+}
+
+
+
+void RasterizerDummy::end_scene() {
+
+
+}
+void RasterizerDummy::end_shadow_map() {
+
+}
+
+
+void RasterizerDummy::end_frame() {
+
+
+}
+
+/* CANVAS API */
+
+
+void RasterizerDummy::canvas_begin() {
+
+
+
+}
+void RasterizerDummy::canvas_set_opacity(float p_opacity) {
+
+
+}
+
+void RasterizerDummy::canvas_set_blend_mode(VS::MaterialBlendMode p_mode) {
+
+
+}
+
+
+void RasterizerDummy::canvas_begin_rect(const Matrix32& p_transform) {
+
+
+
+}
+
+void RasterizerDummy::canvas_set_clip(bool p_clip, const Rect2& p_rect) {
+
+
+
+
+}
+
+void RasterizerDummy::canvas_end_rect() {
+
+
+}
+
+void RasterizerDummy::canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width) {
+
+
+
+}
+
+void RasterizerDummy::canvas_draw_rect(const Rect2& p_rect, int p_flags, const Rect2& p_source,RID p_texture,const Color& p_modulate) {
+
+
+
+
+}
+void RasterizerDummy::canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margin, bool p_draw_center,const Color& p_modulate) {
+
+
+}
+void RasterizerDummy::canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width) {
+
+
+
+}
+
+
+void RasterizerDummy::canvas_draw_polygon(int p_vertex_count, const int* p_indices, const Vector2* p_vertices, const Vector2* p_uvs, const Color* p_colors,const RID& p_texture,bool p_singlecolor) {
+
+
+
+}
+
+void RasterizerDummy::canvas_set_transform(const Matrix32& p_transform) {
+
+
+}
+
+/* ENVIRONMENT */
+
+RID RasterizerDummy::environment_create() {
+
+ Environment * env = memnew( Environment );
+ return environment_owner.make_rid(env);
+}
+
+void RasterizerDummy::environment_set_background(RID p_env,VS::EnvironmentBG p_bg) {
+
+ ERR_FAIL_INDEX(p_bg,VS::ENV_BG_MAX);
+ Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND(!env);
+ env->bg_mode=p_bg;
+}
+
+VS::EnvironmentBG RasterizerDummy::environment_get_background(RID p_env) const{
+
+ const Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND_V(!env,VS::ENV_BG_MAX);
+ return env->bg_mode;
+}
+
+void RasterizerDummy::environment_set_background_param(RID p_env,VS::EnvironmentBGParam p_param, const Variant& p_value){
+
+ ERR_FAIL_INDEX(p_param,VS::ENV_BG_PARAM_MAX);
+ Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND(!env);
+ env->bg_param[p_param]=p_value;
+
+}
+Variant RasterizerDummy::environment_get_background_param(RID p_env,VS::EnvironmentBGParam p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,VS::ENV_BG_PARAM_MAX,Variant());
+ const Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND_V(!env,Variant());
+ return env->bg_param[p_param];
+
+}
+
+void RasterizerDummy::environment_set_enable_fx(RID p_env,VS::EnvironmentFx p_effect,bool p_enabled){
+
+ ERR_FAIL_INDEX(p_effect,VS::ENV_FX_MAX);
+ Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND(!env);
+ env->fx_enabled[p_effect]=p_enabled;
+}
+bool RasterizerDummy::environment_is_fx_enabled(RID p_env,VS::EnvironmentFx p_effect) const{
+
+ ERR_FAIL_INDEX_V(p_effect,VS::ENV_FX_MAX,false);
+ const Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND_V(!env,false);
+ return env->fx_enabled[p_effect];
+
+}
+
+void RasterizerDummy::environment_fx_set_param(RID p_env,VS::EnvironmentFxParam p_param,const Variant& p_value){
+
+ ERR_FAIL_INDEX(p_param,VS::ENV_FX_PARAM_MAX);
+ Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND(!env);
+ env->fx_param[p_param]=p_value;
+}
+Variant RasterizerDummy::environment_fx_get_param(RID p_env,VS::EnvironmentFxParam p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,VS::ENV_FX_PARAM_MAX,Variant());
+ const Environment * env = environment_owner.get(p_env);
+ ERR_FAIL_COND_V(!env,Variant());
+ return env->fx_param[p_param];
+
+}
+
+/*MISC*/
+
+bool RasterizerDummy::is_texture(const RID& p_rid) const {
+
+ return texture_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_material(const RID& p_rid) const {
+
+ return material_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_mesh(const RID& p_rid) const {
+
+ return mesh_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_multimesh(const RID& p_rid) const {
+
+ return multimesh_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_particles(const RID &p_beam) const {
+
+ return particles_owner.owns(p_beam);
+}
+
+bool RasterizerDummy::is_light(const RID& p_rid) const {
+
+ return light_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_light_instance(const RID& p_rid) const {
+
+ return light_instance_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_particles_instance(const RID& p_rid) const {
+
+ return particles_instance_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_skeleton(const RID& p_rid) const {
+
+ return skeleton_owner.owns(p_rid);
+}
+bool RasterizerDummy::is_environment(const RID& p_rid) const {
+
+ return environment_owner.owns(p_rid);
+}
+
+bool RasterizerDummy::is_shader(const RID& p_rid) const {
+
+ return false;
+}
+
+void RasterizerDummy::free(const RID& p_rid) {
+
+ if (texture_owner.owns(p_rid)) {
+
+ // delete the texture
+ Texture *texture = texture_owner.get(p_rid);
+ texture_owner.free(p_rid);
+ memdelete(texture);
+
+ } else if (shader_owner.owns(p_rid)) {
+
+ // delete the texture
+ Shader *shader = shader_owner.get(p_rid);
+ shader_owner.free(p_rid);
+ memdelete(shader);
+
+ } else if (material_owner.owns(p_rid)) {
+
+ Material *material = material_owner.get( p_rid );
+ material_owner.free(p_rid);
+ memdelete(material);
+
+ } else if (mesh_owner.owns(p_rid)) {
+
+ Mesh *mesh = mesh_owner.get(p_rid);
+
+ for (int i=0;i<mesh->surfaces.size();i++) {
+
+ memdelete( mesh->surfaces[i] );
+ };
+
+ mesh->surfaces.clear();
+ mesh_owner.free(p_rid);
+ memdelete(mesh);
+
+ } else if (multimesh_owner.owns(p_rid)) {
+
+ MultiMesh *multimesh = multimesh_owner.get(p_rid);
+ multimesh_owner.free(p_rid);
+ memdelete(multimesh);
+
+ } else if (particles_owner.owns(p_rid)) {
+
+ Particles *particles = particles_owner.get(p_rid);
+ particles_owner.free(p_rid);
+ memdelete(particles);
+ } else if (particles_instance_owner.owns(p_rid)) {
+
+ ParticlesInstance *particles_isntance = particles_instance_owner.get(p_rid);
+ particles_instance_owner.free(p_rid);
+ memdelete(particles_isntance);
+
+ } else if (skeleton_owner.owns(p_rid)) {
+
+ Skeleton *skeleton = skeleton_owner.get( p_rid );
+ skeleton_owner.free(p_rid);
+ memdelete(skeleton);
+
+ } else if (light_owner.owns(p_rid)) {
+
+ Light *light = light_owner.get( p_rid );
+ light_owner.free(p_rid);
+ memdelete(light);
+
+ } else if (light_instance_owner.owns(p_rid)) {
+
+ LightInstance *light_instance = light_instance_owner.get( p_rid );
+ light_instance_owner.free(p_rid);
+ memdelete( light_instance );
+
+
+ } else if (environment_owner.owns(p_rid)) {
+
+ Environment *env = environment_owner.get( p_rid );
+ environment_owner.free(p_rid);
+ memdelete( env );
+ };
+}
+
+
+void RasterizerDummy::custom_shade_model_set_shader(int p_model, RID p_shader) {
+
+
+};
+
+RID RasterizerDummy::custom_shade_model_get_shader(int p_model) const {
+
+ return RID();
+};
+
+void RasterizerDummy::custom_shade_model_set_name(int p_model, const String& p_name) {
+
+};
+
+String RasterizerDummy::custom_shade_model_get_name(int p_model) const {
+
+ return String();
+};
+
+void RasterizerDummy::custom_shade_model_set_param_info(int p_model, const List<PropertyInfo>& p_info) {
+
+};
+
+void RasterizerDummy::custom_shade_model_get_param_info(int p_model, List<PropertyInfo>* p_info) const {
+
+};
+
+
+
+void RasterizerDummy::init() {
+
+
+}
+
+void RasterizerDummy::finish() {
+
+
+}
+
+int RasterizerDummy::get_render_info(VS::RenderInfo p_info) {
+
+ return 0;
+}
+
+bool RasterizerDummy::needs_to_draw_next_frame() const {
+
+ return false;
+}
+
+
+bool RasterizerDummy::has_feature(VS::Features p_feature) const {
+
+ return false;
+
+}
+
+
+RasterizerDummy::RasterizerDummy() {
+
+};
+
+RasterizerDummy::~RasterizerDummy() {
+
+};
+
diff --git a/servers/visual/rasterizer_dummy.h b/servers/visual/rasterizer_dummy.h
new file mode 100644
index 0000000000..7ae03a1c0b
--- /dev/null
+++ b/servers/visual/rasterizer_dummy.h
@@ -0,0 +1,725 @@
+/*************************************************************************/
+/* rasterizer_dummy.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 RASTERIZER_DUMMY_H
+#define RASTERIZER_DUMMY_H
+
+#include "servers/visual/rasterizer.h"
+
+
+#include "image.h"
+#include "rid.h"
+#include "servers/visual_server.h"
+#include "list.h"
+#include "map.h"
+#include "camera_matrix.h"
+#include "sort.h"
+
+
+#include "servers/visual/particle_system_sw.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class RasterizerDummy : public Rasterizer {
+
+ struct Texture {
+
+ uint32_t flags;
+ int width,height;
+ Image::Format format;
+ Image image[6];
+ Texture() {
+
+ flags=width=height=0;
+ format=Image::FORMAT_GRAYSCALE;
+ }
+
+ ~Texture() {
+
+ }
+ };
+
+ mutable RID_Owner<Texture> texture_owner;
+
+ struct Shader {
+
+ String vertex_code;
+ String fragment_code;
+ VS::ShaderMode mode;
+ Map<StringName,Variant> params;
+ int fragment_line;
+ int vertex_line;
+ bool valid;
+ bool has_alpha;
+ bool use_world_transform;
+
+ };
+
+ mutable RID_Owner<Shader> shader_owner;
+
+
+ struct Material {
+
+ bool flags[VS::MATERIAL_FLAG_MAX];
+ bool hints[VS::MATERIAL_HINT_MAX];
+
+ VS::MaterialShadeModel shade_model;
+
+ VS::MaterialBlendMode blend_mode;
+
+ float line_width;
+ float point_size;
+
+ RID shader; // shader material
+
+ Map<StringName,Variant> shader_params;
+
+
+ Material() {
+
+
+ for(int i=0;i<VS::MATERIAL_FLAG_MAX;i++)
+ flags[i]=false;
+ flags[VS::MATERIAL_FLAG_VISIBLE]=true;
+ for(int i=0;i<VS::MATERIAL_HINT_MAX;i++)
+ hints[i]=false;
+
+ line_width=1;
+ blend_mode=VS::MATERIAL_BLEND_MODE_MIX;
+ point_size = 1.0;
+
+ }
+ };
+ mutable RID_Owner<Material> material_owner;
+
+ void _material_check_alpha(Material *p_material);
+
+
+ struct Geometry {
+
+ enum Type {
+ GEOMETRY_INVALID,
+ GEOMETRY_SURFACE,
+ GEOMETRY_POLY,
+ GEOMETRY_PARTICLES,
+ GEOMETRY_MULTISURFACE,
+ };
+
+ Type type;
+ RID material;
+ bool has_alpha;
+ bool material_owned;
+
+ Geometry() { has_alpha=false; material_owned = false; }
+ virtual ~Geometry() {};
+ };
+
+ struct GeometryOwner {
+
+ virtual ~GeometryOwner() {}
+ };
+
+ class Mesh;
+
+ struct Surface : public Geometry {
+
+ Array data;
+ Array morph_data;
+
+ bool packed;
+ bool alpha_sort;
+ int morph_target_count;
+ AABB aabb;
+
+ VS::PrimitiveType primitive;
+
+ uint32_t format;
+ uint32_t morph_format;
+
+ RID material;
+ bool material_owned;
+
+
+ Surface() {
+
+ packed=false;
+ morph_target_count=0;
+ material_owned=false;
+ format=0;
+ morph_format=0;
+
+ primitive=VS::PRIMITIVE_POINTS;
+ }
+
+ ~Surface() {
+
+ }
+ };
+
+
+ struct Mesh {
+
+ bool active;
+ Vector<Surface*> surfaces;
+ int morph_target_count;
+ VS::MorphTargetMode morph_target_mode;
+
+ mutable uint64_t last_pass;
+ Mesh() {
+ morph_target_mode=VS::MORPH_MODE_NORMALIZED;
+ morph_target_count=0;
+ last_pass=0;
+ active=false;
+ }
+ };
+ mutable RID_Owner<Mesh> mesh_owner;
+
+ struct MultiMesh;
+
+ struct MultiMeshSurface : public Geometry {
+
+ Surface *surface;
+ MultiMeshSurface() { type=GEOMETRY_MULTISURFACE; }
+ };
+
+ struct MultiMesh : public GeometryOwner {
+
+ struct Element {
+
+ Transform xform;
+ Color color;
+ };
+
+ AABB aabb;
+ RID mesh;
+ int visible;
+
+ //IDirect3DVertexBuffer9* instance_buffer;
+ Vector<Element> elements;
+
+ MultiMesh() {
+ visible=-1;
+ }
+
+
+ };
+
+ mutable RID_Owner<MultiMesh> multimesh_owner;
+
+ struct Particles : public Geometry {
+
+ ParticleSystemSW data; // software particle system
+
+ Particles() {
+ type=GEOMETRY_PARTICLES;
+
+ }
+ };
+
+ mutable RID_Owner<Particles> particles_owner;
+
+ struct ParticlesInstance : public GeometryOwner {
+
+ RID particles;
+
+ ParticleSystemProcessSW particles_process;
+ Transform transform;
+
+ ParticlesInstance() { }
+ };
+
+ mutable RID_Owner<ParticlesInstance> particles_instance_owner;
+ ParticleSystemDrawInfoSW particle_draw_info;
+
+ struct Skeleton {
+
+ Vector<Transform> bones;
+
+ };
+
+ mutable RID_Owner<Skeleton> skeleton_owner;
+
+
+ struct Light {
+
+ VS::LightType type;
+ float vars[VS::LIGHT_PARAM_MAX];
+ Color colors[3];
+ bool shadow_enabled;
+ RID projector;
+ bool volumetric_enabled;
+ Color volumetric_color;
+
+
+ Light() {
+
+ vars[VS::LIGHT_PARAM_SPOT_ATTENUATION]=1;
+ vars[VS::LIGHT_PARAM_SPOT_ANGLE]=45;
+ vars[VS::LIGHT_PARAM_ATTENUATION]=1.0;
+ vars[VS::LIGHT_PARAM_ENERGY]=1.0;
+ vars[VS::LIGHT_PARAM_RADIUS]=1.0;
+ vars[VS::LIGHT_PARAM_SHADOW_Z_OFFSET]=0.05;
+ colors[VS::LIGHT_COLOR_AMBIENT]=Color(0,0,0);
+ colors[VS::LIGHT_COLOR_DIFFUSE]=Color(1,1,1);
+ colors[VS::LIGHT_COLOR_SPECULAR]=Color(1,1,1);
+ shadow_enabled=false;
+ volumetric_enabled=false;
+ }
+ };
+
+
+ struct Environment {
+
+
+ VS::EnvironmentBG bg_mode;
+ Variant bg_param[VS::ENV_BG_PARAM_MAX];
+ bool fx_enabled[VS::ENV_FX_MAX];
+ Variant fx_param[VS::ENV_FX_PARAM_MAX];
+
+ Environment() {
+
+ bg_mode=VS::ENV_BG_DEFAULT_COLOR;
+ bg_param[VS::ENV_BG_PARAM_COLOR]=Color(0,0,0);
+ bg_param[VS::ENV_BG_PARAM_TEXTURE]=RID();
+ bg_param[VS::ENV_BG_PARAM_CUBEMAP]=RID();
+ bg_param[VS::ENV_BG_PARAM_ENERGY]=1.0;
+
+ for(int i=0;i<VS::ENV_FX_MAX;i++)
+ fx_enabled[i]=false;
+
+ fx_param[VS::ENV_FX_PARAM_GLOW_BLUR_PASSES]=1;
+ fx_param[VS::ENV_FX_PARAM_GLOW_BLOOM]=0.0;
+ fx_param[VS::ENV_FX_PARAM_GLOW_BLOOM_TRESHOLD]=0.5;
+ fx_param[VS::ENV_FX_PARAM_DOF_BLUR_PASSES]=1;
+ fx_param[VS::ENV_FX_PARAM_DOF_BLUR_BEGIN]=100.0;
+ fx_param[VS::ENV_FX_PARAM_DOF_BLUR_RANGE]=10.0;
+ fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE]=0.4;
+ fx_param[VS::ENV_FX_PARAM_HDR_SCALAR]=1.0;
+ fx_param[VS::ENV_FX_PARAM_HDR_GLOW_TRESHOLD]=0.95;
+ fx_param[VS::ENV_FX_PARAM_HDR_GLOW_SCALE]=0.2;
+ fx_param[VS::ENV_FX_PARAM_HDR_MIN_LUMINANCE]=0.4;
+ fx_param[VS::ENV_FX_PARAM_HDR_MAX_LUMINANCE]=8.0;
+ fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE_ADJUST_SPEED]=0.5;
+ fx_param[VS::ENV_FX_PARAM_FOG_BEGIN]=100.0;
+ fx_param[VS::ENV_FX_PARAM_FOG_ATTENUATION]=1.0;
+ fx_param[VS::ENV_FX_PARAM_FOG_BEGIN_COLOR]=Color(0,0,0);
+ fx_param[VS::ENV_FX_PARAM_FOG_END_COLOR]=Color(0,0,0);
+ fx_param[VS::ENV_FX_PARAM_FOG_BG]=true;
+ fx_param[VS::ENV_FX_PARAM_BCS_BRIGHTNESS]=1.0;
+ fx_param[VS::ENV_FX_PARAM_BCS_CONTRAST]=1.0;
+ fx_param[VS::ENV_FX_PARAM_BCS_SATURATION]=1.0;
+ fx_param[VS::ENV_FX_PARAM_GAMMA]=1.0;
+
+ }
+
+ };
+
+ mutable RID_Owner<Environment> environment_owner;
+
+ struct ShadowBuffer;
+
+ struct LightInstance {
+
+ struct SplitInfo {
+
+ CameraMatrix camera;
+ Transform transform;
+ float near;
+ float far;
+ };
+
+ RID light;
+ Light *base;
+ Transform transform;
+ CameraMatrix projection;
+
+ Transform custom_transform;
+ CameraMatrix custom_projection;
+
+ Vector3 light_vector;
+ Vector3 spot_vector;
+ float linear_att;
+
+
+ LightInstance() { linear_att=1.0; }
+
+ };
+
+ mutable RID_Owner<Light> light_owner;
+ mutable RID_Owner<LightInstance> light_instance_owner;
+
+
+ RID default_material;
+
+
+
+
+public:
+
+ /* TEXTURE API */
+
+ virtual RID texture_create();
+ virtual void texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags=VS::TEXTURE_FLAGS_DEFAULT);
+ virtual void texture_set_data(RID p_texture,const Image& p_image,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT);
+ virtual Image texture_get_data(RID p_texture,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT) const;
+ virtual void texture_set_flags(RID p_texture,uint32_t p_flags);
+ virtual uint32_t texture_get_flags(RID p_texture) const;
+ virtual Image::Format texture_get_format(RID p_texture) const;
+ virtual uint32_t texture_get_width(RID p_texture) const;
+ virtual uint32_t texture_get_height(RID p_texture) const;
+ virtual bool texture_has_alpha(RID p_texture) const;
+ virtual void texture_set_size_override(RID p_texture,int p_width, int p_height);
+ virtual void texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const;
+
+ /* SHADER API */
+
+ virtual RID shader_create(VS::ShaderMode p_mode=VS::SHADER_MATERIAL);
+
+ virtual void shader_set_mode(RID p_shader,VS::ShaderMode p_mode);
+ virtual VS::ShaderMode shader_get_mode(RID p_shader) const;
+
+ virtual void shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs=0,int p_fragment_ofs=0);
+ virtual String shader_get_fragment_code(RID p_shader) const;
+ virtual String shader_get_vertex_code(RID p_shader) const;
+
+ virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const;
+
+ /* COMMON MATERIAL API */
+
+ virtual RID material_create();
+
+ virtual void material_set_shader(RID p_shader_material, RID p_shader);
+ virtual RID material_get_shader(RID p_shader_material) const;
+
+ virtual void material_set_param(RID p_material, const StringName& p_param, const Variant& p_value);
+ virtual Variant material_get_param(RID p_material, const StringName& p_param) const;
+
+ virtual void material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled);
+ virtual bool material_get_flag(RID p_material,VS::MaterialFlag p_flag) const;
+
+ virtual void material_set_hint(RID p_material, VS::MaterialHint p_hint,bool p_enabled);
+ virtual bool material_get_hint(RID p_material,VS::MaterialHint p_hint) const;
+
+ virtual void material_set_shade_model(RID p_material, VS::MaterialShadeModel p_model);
+ virtual VS::MaterialShadeModel material_get_shade_model(RID p_material) const;
+
+ virtual void material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode);
+ virtual VS::MaterialBlendMode material_get_blend_mode(RID p_material) const;
+
+ virtual void material_set_line_width(RID p_material,float p_line_width);
+ virtual float material_get_line_width(RID p_material) const;
+
+ /* MESH API */
+
+
+ virtual RID mesh_create();
+
+ virtual void mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes=Array(),bool p_alpha_sort=false);
+ virtual Array mesh_get_surface_arrays(RID p_mesh,int p_surface) const;
+ virtual Array mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const;
+ virtual void mesh_add_custom_surface(RID p_mesh,const Variant& p_dat);
+
+ virtual void mesh_set_morph_target_count(RID p_mesh,int p_amount);
+ virtual int mesh_get_morph_target_count(RID p_mesh) const;
+
+ virtual void mesh_set_morph_target_mode(RID p_mesh,VS::MorphTargetMode p_mode);
+ virtual VS::MorphTargetMode mesh_get_morph_target_mode(RID p_mesh) const;
+
+ virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned=false);
+ virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const;
+
+ virtual int mesh_surface_get_array_len(RID p_mesh, int p_surface) const;
+ virtual int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const;
+ virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const;
+ virtual VS::PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const;
+
+ virtual void mesh_remove_surface(RID p_mesh,int p_index);
+ virtual int mesh_get_surface_count(RID p_mesh) const;
+
+ virtual AABB mesh_get_aabb(RID p_mesh) const;
+
+ /* MULTIMESH API */
+
+ virtual RID multimesh_create();
+
+ virtual void multimesh_set_instance_count(RID p_multimesh,int p_count);
+ virtual int multimesh_get_instance_count(RID p_multimesh) const;
+
+ virtual void multimesh_set_mesh(RID p_multimesh,RID p_mesh);
+ virtual void multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb);
+ virtual void multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform);
+ virtual void multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color);
+
+ virtual RID multimesh_get_mesh(RID p_multimesh) const;
+ virtual AABB multimesh_get_aabb(RID p_multimesh) const;;
+
+ virtual Transform multimesh_instance_get_transform(RID p_multimesh,int p_index) const;
+ virtual Color multimesh_instance_get_color(RID p_multimesh,int p_index) const;
+
+ virtual void multimesh_set_visible_instances(RID p_multimesh,int p_visible);
+ virtual int multimesh_get_visible_instances(RID p_multimesh) const;
+
+ /* PARTICLES API */
+
+ virtual RID particles_create();
+
+ virtual void particles_set_amount(RID p_particles, int p_amount);
+ virtual int particles_get_amount(RID p_particles) const;
+
+ virtual void particles_set_emitting(RID p_particles, bool p_emitting);
+ virtual bool particles_is_emitting(RID p_particles) const;
+
+ virtual void particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility);
+ virtual AABB particles_get_visibility_aabb(RID p_particles) const;
+
+ virtual void particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents);
+ virtual Vector3 particles_get_emission_half_extents(RID p_particles) const;
+
+ virtual void particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity);
+ virtual Vector3 particles_get_emission_base_velocity(RID p_particles) const;
+
+ virtual void particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points);
+ virtual DVector<Vector3> particles_get_emission_points(RID p_particles) const;
+
+ virtual void particles_set_gravity_normal(RID p_particles, const Vector3& p_normal);
+ virtual Vector3 particles_get_gravity_normal(RID p_particles) const;
+
+ virtual void particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value);
+ virtual float particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const;
+
+ virtual void particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness);
+ virtual float particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const;
+
+ virtual void particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos);
+ virtual float particles_get_color_phase_pos(RID p_particles, int p_phase) const;
+
+ virtual void particles_set_color_phases(RID p_particles, int p_phases);
+ virtual int particles_get_color_phases(RID p_particles) const;
+
+ virtual void particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color);
+ virtual Color particles_get_color_phase_color(RID p_particles, int p_phase) const;
+
+ virtual void particles_set_attractors(RID p_particles, int p_attractors);
+ virtual int particles_get_attractors(RID p_particles) const;
+
+ virtual void particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos);
+ virtual Vector3 particles_get_attractor_pos(RID p_particles,int p_attractor) const;
+
+ virtual void particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force);
+ virtual float particles_get_attractor_strength(RID p_particles,int p_attractor) const;
+
+ virtual void particles_set_material(RID p_particles, RID p_material,bool p_owned=false);
+ virtual RID particles_get_material(RID p_particles) const;
+
+ virtual AABB particles_get_aabb(RID p_particles) const;
+
+ virtual void particles_set_height_from_velocity(RID p_particles, bool p_enable);
+ virtual bool particles_has_height_from_velocity(RID p_particles) const;
+
+ virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable);
+ virtual bool particles_is_using_local_coordinates(RID p_particles) const;
+
+ /* SKELETON API */
+
+ virtual RID skeleton_create();
+ virtual void skeleton_resize(RID p_skeleton,int p_bones);
+ virtual int skeleton_get_bone_count(RID p_skeleton) const;
+ virtual void skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform);
+ virtual Transform skeleton_bone_get_transform(RID p_skeleton,int p_bone);
+
+
+ /* LIGHT API */
+
+ virtual RID light_create(VS::LightType p_type);
+ virtual VS::LightType light_get_type(RID p_light) const;
+
+ virtual void light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color);
+ virtual Color light_get_color(RID p_light,VS::LightColor p_type) const;
+
+ virtual void light_set_shadow(RID p_light,bool p_enabled);
+ virtual bool light_has_shadow(RID p_light) const;
+
+ virtual void light_set_volumetric(RID p_light,bool p_enabled);
+ virtual bool light_is_volumetric(RID p_light) const;
+
+ virtual void light_set_projector(RID p_light,RID p_texture);
+ virtual RID light_get_projector(RID p_light) const;
+
+ virtual void light_set_var(RID p_light, VS::LightParam p_var, float p_value);
+ virtual float light_get_var(RID p_light, VS::LightParam p_var) const;
+
+ virtual void light_set_operator(RID p_light,VS::LightOp p_op);
+ virtual VS::LightOp light_get_operator(RID p_light) const;
+
+ virtual void light_omni_set_shadow_mode(RID p_light,VS::LightOmniShadowMode p_mode);
+ virtual VS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) const;
+
+
+ virtual void light_directional_set_shadow_mode(RID p_light,VS::LightDirectionalShadowMode p_mode);
+ virtual VS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) const;
+ virtual void light_directional_set_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param, float p_value);
+ virtual float light_directional_get_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param) const;
+
+ virtual AABB light_get_aabb(RID p_poly) const;
+
+
+ virtual RID light_instance_create(RID p_light);
+ virtual void light_instance_set_transform(RID p_light_instance,const Transform& p_transform);
+
+ virtual bool light_instance_has_shadow(RID p_light_instance) const;
+ virtual bool light_instance_assign_shadow(RID p_light_instance);
+ virtual ShadowType light_instance_get_shadow_type(RID p_light_instance) const;
+ virtual int light_instance_get_shadow_passes(RID p_light_instance) const;
+ virtual void light_instance_set_custom_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near=0,float p_split_far=0);
+ virtual int light_instance_get_shadow_size(RID p_light_instance, int p_index=0) const { return 1; }
+
+ virtual ShadowType light_instance_get_shadow_type(RID p_light_instance,bool p_far=false) const;
+ virtual void light_instance_set_shadow_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near=0,float p_split_far=0);
+
+ virtual void shadow_clear_near();
+ virtual bool shadow_allocate_near(RID p_light);
+ virtual bool shadow_allocate_far(RID p_light);
+
+
+ /* PARTICLES INSTANCE */
+
+ virtual RID particles_instance_create(RID p_particles);
+ virtual void particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform);
+
+ /* VIEWPORT */
+
+ virtual RID viewport_data_create();
+
+ virtual RID render_target_create();
+ virtual void render_target_set_size(RID p_render_target, int p_width, int p_height);
+ virtual RID render_target_get_texture(RID p_render_target) const;
+ virtual bool render_target_renedered_in_frame(RID p_render_target);
+
+ /* RENDER API */
+ /* all calls (inside begin/end shadow) are always warranted to be in the following order: */
+
+ virtual void begin_frame();
+
+ virtual void set_viewport(const VS::ViewportRect& p_viewport);
+ virtual void set_render_target(RID p_render_target);
+ virtual void clear_viewport(const Color& p_color);
+ virtual void capture_viewport(Image* r_capture);
+
+
+ virtual void begin_scene(RID p_viewport_data,RID p_env,VS::ScenarioDebugMode p_debug);
+ virtual void begin_shadow_map( RID p_light_instance, int p_shadow_pass );
+
+ virtual void set_camera(const Transform& p_world,const CameraMatrix& p_projection);
+
+ virtual void add_light( RID p_light_instance ); ///< all "add_light" calls happen before add_geometry calls
+
+
+ virtual void add_mesh( const RID& p_mesh, const InstanceData *p_data);
+ virtual void add_multimesh( const RID& p_multimesh, const InstanceData *p_data);
+ virtual void add_particles( const RID& p_particle_instance, const InstanceData *p_data);
+
+ virtual void end_scene();
+ virtual void end_shadow_map();
+
+ virtual void end_frame();
+
+ /* CANVAS API */
+
+ virtual void canvas_begin();
+ virtual void canvas_set_opacity(float p_opacity);
+ virtual void canvas_set_blend_mode(VS::MaterialBlendMode p_mode);
+ virtual void canvas_begin_rect(const Matrix32& p_transform);
+ virtual void canvas_set_clip(bool p_clip, const Rect2& p_rect);
+ virtual void canvas_end_rect();
+ virtual void canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width);
+ virtual void canvas_draw_rect(const Rect2& p_rect, int p_flags, const Rect2& p_source,RID p_texture,const Color& p_modulate);
+ virtual void canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margins, bool p_draw_center=true,const Color& p_modulate=Color(1,1,1));
+ virtual void canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width);
+ virtual void canvas_draw_polygon(int p_vertex_count, const int* p_indices, const Vector2* p_vertices, const Vector2* p_uvs, const Color* p_colors,const RID& p_texture,bool p_singlecolor);
+ virtual void canvas_set_transform(const Matrix32& p_transform);
+
+ /* ENVIRONMENT */
+
+ virtual RID environment_create();
+
+ virtual void environment_set_background(RID p_env,VS::EnvironmentBG p_bg);
+ virtual VS::EnvironmentBG environment_get_background(RID p_env) const;
+
+ virtual void environment_set_background_param(RID p_env,VS::EnvironmentBGParam p_param, const Variant& p_value);
+ virtual Variant environment_get_background_param(RID p_env,VS::EnvironmentBGParam p_param) const;
+
+ virtual void environment_set_enable_fx(RID p_env,VS::EnvironmentFx p_effect,bool p_enabled);
+ virtual bool environment_is_fx_enabled(RID p_env,VS::EnvironmentFx p_effect) const;
+
+ virtual void environment_fx_set_param(RID p_env,VS::EnvironmentFxParam p_param,const Variant& p_value);
+ virtual Variant environment_fx_get_param(RID p_env,VS::EnvironmentFxParam p_param) const;
+
+
+ /*MISC*/
+
+ virtual bool is_texture(const RID& p_rid) const;
+ virtual bool is_material(const RID& p_rid) const;
+ virtual bool is_mesh(const RID& p_rid) const;
+ virtual bool is_multimesh(const RID& p_rid) const;
+ virtual bool is_particles(const RID &p_beam) const;
+
+ virtual bool is_light(const RID& p_rid) const;
+ virtual bool is_light_instance(const RID& p_rid) const;
+ virtual bool is_particles_instance(const RID& p_rid) const;
+ virtual bool is_skeleton(const RID& p_rid) const;
+ virtual bool is_environment(const RID& p_rid) const;
+
+ virtual bool is_shader(const RID& p_rid) const;
+
+ virtual void free(const RID& p_rid);
+
+ virtual void custom_shade_model_set_shader(int p_model, RID p_shader);
+ virtual RID custom_shade_model_get_shader(int p_model) const;
+ virtual void custom_shade_model_set_name(int p_model, const String& p_name);
+ virtual String custom_shade_model_get_name(int p_model) const;
+ virtual void custom_shade_model_set_param_info(int p_model, const List<PropertyInfo>& p_info);
+ virtual void custom_shade_model_get_param_info(int p_model, List<PropertyInfo>* p_info) const;
+
+
+ virtual void init();
+ virtual void finish();
+
+ virtual int get_render_info(VS::RenderInfo p_info);
+
+ virtual bool needs_to_draw_next_frame() const;
+
+ virtual bool has_feature(VS::Features p_feature) const;
+
+
+ RasterizerDummy();
+ virtual ~RasterizerDummy();
+};
+
+
+#endif // RASTERIZER_DUMMY_H
diff --git a/servers/visual/shader_compiler.cpp b/servers/visual/shader_compiler.cpp
new file mode 100644
index 0000000000..df2ffa6889
--- /dev/null
+++ b/servers/visual/shader_compiler.cpp
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* shader_compiler.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "shader_compiler.h"
+
diff --git a/servers/visual/shader_compiler.h b/servers/visual/shader_compiler.h
new file mode 100644
index 0000000000..6bb523fbce
--- /dev/null
+++ b/servers/visual/shader_compiler.h
@@ -0,0 +1,142 @@
+/*************************************************************************/
+/* shader_compiler.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SHADER_COMPILER_H
+#define SHADER_COMPILER_H
+
+#include "map.h"
+#include "list.h"
+#include "vector.h"
+#if 0
+class ShaderSyntax {
+public:
+
+
+ enum DataType {
+ TYPE_BOOL,
+ TYPE_FLOAT,
+ TYPE_VEC3,
+ TYPE_TRANSFORM,
+ TYPE_TEXTURE
+ };
+
+ enum Operator {
+ OP_ASSIGN,
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_NEG,
+ OP_CMP_EQ,
+ OP_CMP_NEQ,
+ OP_CMP_LEQ,
+ OP_CMP_GEQ,
+ OP_CMP_OR,
+ OP_CMP_AND,
+ OP_CALL
+ };
+
+ struct Node {
+
+ enum Type {
+ TYPE_PROGRAM,
+ TYPE_FUNCTION,
+ TYPE_BLOCK,
+ TYPE_VARIABLE,
+ TYPE_OPERATOR,
+ TYPE_IF,
+ };
+
+ Node * parent;
+ Type type;
+
+ virtual ~Node() {}
+ };
+
+
+ struct OperatorNode : public Node {
+
+ Operator op;
+ Vector<Node*> arguments;
+ OperatorNode() { type=TYPE_OPERATOR; }
+ };
+
+ struct VariableNode : public Node {
+
+ StringName variable;
+ VariableNode() { type=TYPE_VARIABLE; }
+ };
+
+ struct BlockNode : public Node {
+
+ Map<StringName,DataType> variables;
+ List<Node*> subnodes;
+ BlockNode() { type=TYPE_BLOCK; }
+ };
+
+ struct ConditionalNode : public Node {
+
+ Node *test;
+ Node *do_if;
+ Node *do_else;
+ ConditionalNode() { type=TYPE_CONDITIONAL; }
+ };
+
+
+ struct FunctionNode : public Node {
+
+ struct Argument {
+
+ StringName name;
+ DataType type;
+ };
+
+ Vector<Argument> arguments;
+ Node *body;
+
+ FunctionNode() { type=TYPE_FUNCTION; }
+
+ };
+
+
+ struct ProgramNode : public Node {
+
+ Vector<FunctionNode*> functions;
+ Node *body;
+
+ ProgramNode() { type=TYPE_PROGRAM; }
+ };
+
+
+
+
+ ShaderCompiler();
+};
+
+#endif // SHADER_COMPILER_H
+#endif
diff --git a/servers/visual/shader_graph.cpp b/servers/visual/shader_graph.cpp
new file mode 100644
index 0000000000..8fb7196276
--- /dev/null
+++ b/servers/visual/shader_graph.cpp
@@ -0,0 +1,455 @@
+/*************************************************************************/
+/* shader_graph.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "shader_graph.h"
+
+#if 0
+
+
+struct _ConnectionKey {
+
+ int node;
+ int slot;
+
+ _FORCE_INLINE_ _ConnectionKey(int p_node=0,int p_slot=0) { node=p_node; slot=p_slot; }
+
+ _FORCE_INLINE_ bool operator<(const _ConnectionKey& p_other) const {
+
+ if (node<p_other.node)
+ return true;
+ else if (node>p_other.node)
+ return false;
+ else
+ return slot<p_other.slot;
+ }
+};
+
+Error ShaderGraph::generate(ShaderCodeGenerator * p_generator) const {
+
+ Map<int,Node>::Element *E = node_map.front();
+ int i=0;
+ while(E) {
+
+ E->get().order=i++;
+ E->get().out_valid=false;
+ E->get().in_valid=false;
+ E=E->next();
+ }
+
+ int worst_case=connections.size() * connections.size(); // worst bubble case
+ int iterations=0;
+ int swaps;
+
+ do {
+ swaps=0;
+ const List<Connection>::Element *E=connections.front();
+
+ while(E) {
+
+ const Connection &c = E->get();
+
+ const Node *src = &node_map[c.src_id];
+ const Node *dst = &node_map[c.dst_id];
+
+ if (src->order > dst->order) {
+
+ SWAP(src->order, dst->order);
+ swaps++;
+ }
+
+ E=E->next();
+ }
+
+
+ iterations++;
+
+ } while (iterations<=worst_case && swaps>0);
+
+ ERR_FAIL_COND_V( swaps != 0 , ERR_CYCLIC_LINK );
+
+ //node array
+ Vector<const Node*> nodes;
+ nodes.resize(node_map.size());
+
+ E = node_map.front();
+ while(E) {
+
+ ERR_FAIL_INDEX_V( E->get().order, nodes.size(), ERR_BUG);
+ nodes[E->get().order]=&E->get();
+ E=E->next();
+ }
+
+ //connection set
+
+ Map<_ConnectionKey,int> in_connection_map;
+ Map<_ConnectionKey,List<int> > out_connection_map;
+ Map<_ConnectionKey,int> in_node_map;
+ Map<_ConnectionKey,List<int> > out_node_map;
+
+ const List<Connection>::Element *CE=connections.front();
+ i=0;
+ while(CE) {
+ const Connection &c = CE->get();
+
+ _ConnectionKey in_k;
+ in_k.node=node_map[c.dst_id].order;
+ in_k.slot=c.dst_slot;
+ in_connection_map[in_k]=i;
+ in_node_map[in_k]=node_map[c.src_id].order;
+
+ _ConnectionKey out_k;
+ out_k.node=node_map[c.src_id].order;
+ out_k.slot=c.src_slot;
+ if (!out_connection_map.has(out_k))
+ out_connection_map[out_k]=List<int>();
+ out_connection_map[out_k].push_back(i);
+ if(!out_node_map.has(out_k))
+ out_node_map[out_k]=List<int>();
+ out_node_map[out_k].push_back(node_map[c.dst_id].order);
+
+ i++;
+ CE=CE->next();
+ }
+
+ // validate nodes if they are connected to an output
+
+ for(int i=nodes.size()-1;i>=0;i--) {
+
+ if (VisualServer::shader_get_output_count(nodes[i]->type)==0) {
+ // an actual graph output
+
+ _ConnectionKey in_k;
+ in_k.node=nodes[i]->order;
+ in_k.slot=0;
+
+ if (in_node_map.has(in_k)) {
+ nodes[i]->out_valid=true;
+ }
+ } else {
+ // regular node
+
+ bool valid=false;
+ for(int j=0;j<VS::shader_get_output_count(nodes[i]->type);j++) {
+
+ _ConnectionKey key(nodes[i]->order,j);
+
+ if (out_node_map.has(key)) {
+ for(List<int>::Element *CE=out_node_map[key].front();CE;CE=CE->next()) {
+
+ int to_node=CE->get();
+ ERR_CONTINUE(to_node<0 || to_node >=nodes.size());
+ if (nodes[to_node]->out_valid) {
+ valid=true;
+ break;
+ }
+
+
+ }
+ }
+ if (valid)
+ break;
+
+ }
+
+ nodes[i]->out_valid=valid;
+ }
+ }
+
+ // validate nodes if they are connected to an input
+
+ for(int i=0;i<nodes.size();i++) {
+
+ if (VisualServer::shader_get_input_count(nodes[i]->type)==0) {
+ // an actual graph input
+
+ int out_count=VisualServer::shader_get_output_count(nodes[i]->type);
+
+
+ for(int j=0;j<out_count;j++) {
+
+ _ConnectionKey out_k;
+ out_k.node=nodes[i]->order;
+ out_k.slot=j;
+ if (out_node_map.has(out_k)) {
+ nodes[i]->in_valid=true;
+ break;
+ }
+ }
+
+ } else {
+ // regular node
+ // this is very important.. for a node to be valid, all its inputs need to be valid
+ bool valid=true;
+ for(int j=0;j<VS::shader_get_input_count(nodes[i]->type);j++) {
+
+
+ bool in_valid=false;
+ _ConnectionKey key(nodes[i]->order,j);
+ if (in_node_map.has(key)) {
+
+ int from_node=in_node_map[key];
+ ERR_CONTINUE(from_node<0 || from_node>=nodes.size());
+ if (nodes[from_node]->in_valid)
+ in_valid=true;
+
+ }
+
+ if (!in_valid) {
+ valid=false;
+ break;
+ }
+
+ }
+
+ nodes[i]->in_valid=valid;
+ }
+ }
+
+ // write code
+
+ p_generator->begin();
+
+ for(int i=0;i<nodes.size();i++) {
+
+
+ if (!nodes[i]->out_valid || !nodes[i]->in_valid) // valid in both ways
+ continue; // skip node
+
+ Vector<int> in_indices;
+ in_indices.resize(VS::shader_get_input_count(nodes[i]->type));
+ Vector<int> out_indices;
+ Vector<int> out_slot_indices;
+
+ for(int j=0;j<in_indices.size();j++) {
+
+ _ConnectionKey key(nodes[i]->order,j);
+ if (in_connection_map.has(key))
+ in_indices[j]=in_connection_map[key];
+ else
+ in_indices[j]=-1;
+ }
+
+ for(int j=0;j<VS::shader_get_output_count(nodes[i]->type);j++) {
+
+ _ConnectionKey key(nodes[i]->order,j);
+ if (out_connection_map.has(key)) {
+ for(List<int>::Element *CE=out_connection_map[key].front();CE;CE=CE->next()) {
+
+ out_indices.push_back(CE->get());
+ out_slot_indices.push_back(j);
+ }
+ }
+ }
+
+ Error err = p_generator->add_node(nodes[i]->type,i,nodes[i]->id,nodes[i]->param,in_indices,out_indices,out_slot_indices);
+ ERR_FAIL_COND_V( err, err );
+ }
+
+ p_generator->end();
+
+
+ return OK;
+}
+
+void ShaderGraph::node_add(VS::ShaderNodeType p_type,int p_id) {
+
+
+ ERR_FAIL_COND( node_map.has(p_id ) );
+ ERR_FAIL_INDEX( p_type, VS::NODE_TYPE_MAX );
+ Node node;
+
+ node.type=p_type;
+ node.id=p_id;
+ node.x=0;
+ node.y=0;
+
+ node_map[p_id]=node;
+
+}
+
+void ShaderGraph::node_set_pos(int p_id, int p_x,int p_y) {
+
+ ERR_FAIL_COND(!node_map.has(p_id));
+ node_map[p_id].x=p_x;
+ node_map[p_id].y=p_y;
+}
+int ShaderGraph::node_get_pos_x(int p_id) const {
+
+ ERR_FAIL_COND_V(!node_map.has(p_id),-1);
+ return node_map[p_id].x;
+}
+int ShaderGraph::node_get_pos_y(int p_id) const {
+
+ ERR_FAIL_COND_V(!node_map.has(p_id),-1);
+ return node_map[p_id].y;
+}
+
+void ShaderGraph::node_remove(int p_id) {
+
+ ERR_FAIL_COND(!node_map.has(p_id));
+
+ //erase connections associated with node
+ List<Connection>::Element *N,*E=connections.front();
+ while(E) {
+ N=E->next();
+ const Connection &c = E->get();
+ if (c.src_id==p_id || c.dst_id==p_id) {
+
+ connections.erase(E);
+ }
+ E=N;
+ }
+
+ node_map.erase(p_id);
+}
+
+void ShaderGraph::node_change_type(int p_id, VS::ShaderNodeType p_type) {
+
+ ERR_FAIL_COND(!node_map.has(p_id));
+ node_map[p_id].type=p_type;
+ node_map[p_id].param=Variant();
+
+}
+
+void ShaderGraph::node_set_param(int p_id, const Variant& p_value) {
+
+ ERR_FAIL_COND(!node_map.has(p_id));
+ node_map[p_id].param=p_value;
+}
+
+void ShaderGraph::get_node_list(List<int> *p_node_list) const {
+
+ Map<int,Node>::Element *E = node_map.front();
+
+ while(E) {
+
+ p_node_list->push_back(E->key());
+ E=E->next();
+ }
+}
+
+
+VS::ShaderNodeType ShaderGraph::node_get_type(int p_id) const {
+
+ ERR_FAIL_COND_V(!node_map.has(p_id),VS::NODE_TYPE_MAX);
+ return node_map[p_id].type;
+}
+
+Variant ShaderGraph::node_get_param(int p_id) const {
+
+ ERR_FAIL_COND_V(!node_map.has(p_id),Variant());
+ return node_map[p_id].param;
+}
+
+
+Error ShaderGraph::connect(int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) {
+
+ ERR_FAIL_COND_V(p_src_id==p_dst_id, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!node_map.has(p_src_id), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!node_map.has(p_dst_id), ERR_INVALID_PARAMETER);
+ VisualServer::ShaderNodeType type_src=node_map[p_src_id].type;
+ VisualServer::ShaderNodeType type_dst=node_map[p_dst_id].type;
+ ERR_FAIL_INDEX_V( p_src_slot, VisualServer::shader_get_output_count(type_src), ERR_INVALID_PARAMETER );
+ ERR_FAIL_INDEX_V( p_dst_slot, VisualServer::shader_get_input_count(type_dst), ERR_INVALID_PARAMETER );
+ ERR_FAIL_COND_V(VisualServer::shader_is_output_vector(type_src,p_src_slot) != VisualServer::shader_is_input_vector(type_dst,p_dst_slot), ERR_INVALID_PARAMETER );
+
+
+ List<Connection>::Element *E=connections.front();
+ while(E) {
+ const Connection &c = E->get();
+ ERR_FAIL_COND_V(c.dst_slot==p_dst_slot && c.dst_id == p_dst_id, ERR_ALREADY_EXISTS);
+
+ E=E->next();
+ }
+
+ Connection c;
+ c.src_slot=p_src_slot;
+ c.src_id=p_src_id;
+ c.dst_slot=p_dst_slot;
+ c.dst_id=p_dst_id;
+
+ connections.push_back(c);
+
+ return OK;
+}
+
+bool ShaderGraph::is_connected(int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) const {
+
+ const List<Connection>::Element *E=connections.front();
+ while(E) {
+ const Connection &c = E->get();
+ if (c.dst_slot==p_dst_slot && c.dst_id == p_dst_id && c.src_slot==p_src_slot && c.src_id == p_src_id)
+ return true;
+
+ E=E->next();
+ }
+
+ return false;
+}
+
+void ShaderGraph::disconnect(int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) {
+
+ List<Connection>::Element *N,*E=connections.front();
+ while(E) {
+ N=E->next();
+ const Connection &c = E->get();
+ if (c.src_slot==p_src_slot && c.src_id==p_src_id && c.dst_slot==p_dst_slot && c.dst_id == p_dst_id) {
+
+ connections.erase(E);
+ }
+ E=N;
+ }
+
+
+}
+
+
+void ShaderGraph::clear() {
+
+ connections.clear();
+ node_map.clear();
+}
+
+List<ShaderGraph::Connection> ShaderGraph::get_connection_list() const {
+
+ return connections;
+
+}
+
+ShaderGraph::ShaderGraph() {
+
+
+}
+
+
+ShaderGraph::~ShaderGraph() {
+
+}
+
+
+#endif
diff --git a/servers/visual/shader_graph.h b/servers/visual/shader_graph.h
new file mode 100644
index 0000000000..5c5079202f
--- /dev/null
+++ b/servers/visual/shader_graph.h
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* shader_graph.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SHADER_GRAPH_H
+#define SHADER_GRAPH_H
+
+#if 0
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+#include "servers/visual_server.h"
+#include "map.h"
+
+
+class ShaderCodeGenerator {
+public:
+
+ virtual void begin()=0;
+ virtual Error add_node(VS::ShaderNodeType p_type,int p_node_pos,int p_id,const Variant& p_param,const Vector<int>& p_in_connections,const Vector<int>& p_out_connections,const Vector<int>& p_out_connection_outputs)=0;
+ virtual void end()=0;
+
+ virtual ~ShaderCodeGenerator() {}
+};
+
+class ShaderGraph {
+public:
+
+
+ struct Connection {
+
+ int src_id;
+ int src_slot;
+ int dst_id;
+ int dst_slot;
+ };
+
+private:
+ struct Node {
+
+ int16_t x,y;
+ VS::ShaderNodeType type;
+ Variant param;
+ int id;
+ mutable int order; // used for sorting
+ mutable bool out_valid;
+ mutable bool in_valid;
+ };
+
+ Map<int,Node> node_map;
+
+ List<Connection> connections;
+
+public:
+
+ Error generate(ShaderCodeGenerator * p_generator) const;
+
+ void node_add(VS::ShaderNodeType p_type,int p_id);
+ void node_remove(int p_id);
+ void node_change_type(int p_id, VS::ShaderNodeType p_type);
+ void node_set_param(int p_id, const Variant& p_value);
+
+ void node_set_pos(int p_id, int p_x,int p_y);
+ int node_get_pos_x(int p_id) const;
+ int node_get_pos_y(int p_id) const;
+
+ void get_node_list(List<int> *p_node_list) const;
+ void get_sorted_node_list(List<int> *p_node_list) const;
+ VS::ShaderNodeType node_get_type(int p_id) const;
+ Variant node_get_param(int p_id) const;
+
+ Error connect(int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot);
+ bool is_connected(int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) const;
+ void disconnect(int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot);
+
+ void clear();
+
+ List<Connection> get_connection_list() const;
+
+
+ ShaderGraph();
+ ~ShaderGraph();
+
+};
+#endif
+#endif
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
new file mode 100644
index 0000000000..3061c2ddff
--- /dev/null
+++ b/servers/visual/shader_language.cpp
@@ -0,0 +1,2385 @@
+/*************************************************************************/
+/* shader_language.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "shader_language.h"
+#include "print_string.h"
+#include "os/os.h"
+static bool _is_text_char(CharType c) {
+
+ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
+}
+
+static bool _is_number(CharType c) {
+
+ return (c>='0' && c<='9');
+}
+
+static bool _is_hex(CharType c) {
+
+ return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F');
+}
+
+const char * ShaderLanguage::token_names[TK_MAX]={
+ "EMPTY",
+ "INDENTIFIER",
+ "TRUE",
+ "FALSE",
+ "REAL_CONSTANT",
+ "TYPE_VOID",
+ "TYPE_BOOL",
+ "TYPE_FLOAT",
+ "TYPE_VEC2",
+ "TYPE_VEC3",
+ "TYPE_VEC4",
+ "TYPE_MAT3",
+ "TYPE_MAT4",
+ "TYPE_TEXTURE",
+ "TYPE_CUBEMAP",
+ "TYPE_COLOR",
+ "OP_EQUAL",
+ "OP_NOT_EQUAL",
+ "OP_LESS",
+ "OP_LESS_EQUAL",
+ "OP_GREATER",
+ "OP_GREATER_EQUAL",
+ "OP_AND",
+ "OP_OR",
+ "OP_NOT",
+ "OP_ADD",
+ "OP_SUB",
+ "OP_MUL",
+ "OP_DIV",
+ "OP_NEG",
+ "OP_ASSIGN",
+ "OP_ASSIGN_ADD",
+ "OP_ASSIGN_SUB",
+ "OP_ASSIGN_MUL",
+ "OP_ASSIGN_DIV",
+ "CF_IF",
+ "CF_ELSE",
+ "CF_RETURN",
+ "BRACKET_OPEN",
+ "BRACKET_CLOSE",
+ "CURLY_BRACKET_OPEN",
+ "CURLY_BRACKET_CLOSE",
+ "PARENTHESIS_OPEN",
+ "PARENTHESIS_CLOSE",
+ "COMMA",
+ "SEMICOLON",
+ "PERIOD",
+ "UNIFORM",
+ "ERROR",
+};
+
+ShaderLanguage::Token ShaderLanguage::read_token(const CharType* p_text,int p_len,int &r_line,int &r_chars) {
+
+#define GETCHAR(m_idx) ((m_idx<p_len)?p_text[m_idx]:CharType(0))
+
+ r_chars=1; //by default everything eats one char
+ switch(GETCHAR(0)) {
+
+ case '\t':
+ case '\r':
+ case ' ':
+ return Token();
+ case '\n':
+ r_line++;
+ return Token();
+ case '/': {
+
+ switch(GETCHAR(1)) {
+ case '*': { // block comment
+
+
+ while(true) {
+ if (GETCHAR(r_chars+1)=='0')
+ break;
+ if (GETCHAR(r_chars+1)=='*' && GETCHAR(r_chars+2)=='/')
+ break;
+ if (GETCHAR(r_chars+1)=='\n')
+ r_line++;
+ r_chars++;
+ }
+
+ return Token();
+
+ } break;
+ case '/': { // line comment skip
+
+ while(GETCHAR(r_chars+1)!='\n' && GETCHAR(r_chars+1)!=0) {
+ r_chars++;
+ }
+
+ return Token();
+
+ } break;
+ case '=': { // diveq
+
+ r_chars=2;
+ return Token(TK_OP_ASSIGN_DIV);
+
+ } break;
+ default:
+ return Token(TK_OP_DIV);
+
+ }
+ } break;
+ case '=': {
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_EQUAL);
+ }
+
+ return Token(TK_OP_ASSIGN);
+
+ } break;
+ case '<': {
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_LESS_EQUAL);
+ } /*else if (GETCHAR(1)=='<') {
+ r_chars++;
+ if (GETCHAR(2)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_SHIFT_LEFT);
+ }
+
+ return Token(TK_OP_SHIFT_LEFT);
+ }*/
+
+ return Token(TK_OP_LESS);
+
+ } break;
+ case '>': {
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_GREATER_EQUAL);
+ }/* else if (GETCHAR(1)=='<') {
+ r_chars++;
+ if (GETCHAR(2)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_SHIFT_RIGHT);
+ }
+
+ return Token(TK_OP_SHIFT_RIGHT);
+ }*/
+
+ return Token(TK_OP_GREATER);
+
+ } break;
+ case '!': {
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_NOT_EQUAL);
+ }
+
+ return Token(TK_OP_NOT);
+
+ } break;
+ //case '"' //string - no strings in shader
+ //case '\'' //string - no strings in shader
+ case '{':
+ return Token(TK_CURLY_BRACKET_OPEN);
+ case '}':
+ return Token(TK_CURLY_BRACKET_CLOSE);
+ //case '[':
+ // return Token(TK_BRACKET_OPEN);
+ //case ']':
+ // return Token(TK_BRACKET_CLOSE);
+ case '(':
+ return Token(TK_PARENTHESIS_OPEN);
+ case ')':
+ return Token(TK_PARENTHESIS_CLOSE);
+ case ',':
+ return Token(TK_COMMA);
+ case ';':
+ return Token(TK_SEMICOLON);
+ //case '?':
+ // return Token(TK_QUESTION_MARK);
+ //case ':':
+ // return Token(TK_COLON); //for methods maybe but now useless.
+ //case '^':
+ // return Token(TK_OP_BIT_XOR);
+ //case '~':
+ // return Token(TK_OP_BIT_INVERT);
+ case '&': {
+
+ if (GETCHAR(1)=='&') {
+
+ r_chars++;
+ return Token(TK_OP_AND);
+ }
+
+ return Token(TK_ERROR,"Unknown character");
+
+/*
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_BIT_AND);
+ } else if (GETCHAR(1)=='&') {
+ r_chars++;
+ return Token(TK_OP_AND);
+ }
+ return TK_OP_BIT_AND;*/
+ } break;
+ case '|': {
+
+ if (GETCHAR(1)=='|') {
+
+ r_chars++;
+ return Token(TK_OP_OR);
+ }
+
+ return Token(TK_ERROR,"Unknown character");
+
+ /*
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_BIT_OR);
+ } else if (GETCHAR(1)=='|') {
+ r_chars++;
+ return Token(TK_OP_OR);
+ }
+ return TK_OP_BIT_OR;
+ */
+ } break;
+ case '*': {
+
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_MUL);
+ }
+ return TK_OP_MUL;
+ } break;
+ case '+': {
+
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_ADD);
+ } /*else if (GETCHAR(1)=='+') {
+
+ r_chars++;
+ return Token(TK_OP_PLUS_PLUS);
+ }*/
+
+ return TK_OP_ADD;
+ } break;
+ case '-': {
+
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_SUB);
+ }/* else if (GETCHAR(1)=='-') {
+
+ r_chars++;
+ return Token(TK_OP_MINUS_MINUS);
+ }*/
+
+ return TK_OP_SUB;
+ } break;
+ /*case '%': {
+
+ if (GETCHAR(1)=='=') {
+ r_chars++;
+ return Token(TK_OP_ASSIGN_MOD);
+ }
+
+ return TK_OP_MOD;
+ } break;*/
+ default: {
+
+ if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) {
+ // parse number
+ bool period_found=false;
+ bool exponent_found=false;
+ bool hexa_found=false;
+ bool sign_found=false;
+
+ String str;
+ int i=0;
+
+ while(true) {
+ if (GETCHAR(i)=='.') {
+ if (period_found || exponent_found)
+ return Token(TK_ERROR,"Invalid numeric constant");
+ period_found=true;
+ } else if (GETCHAR(i)=='x') {
+ if (hexa_found || str.length()!=1 || str[0]!='0')
+ return Token(TK_ERROR,"Invalid numeric constant");
+ hexa_found=true;
+ } else if (GETCHAR(i)=='e') {
+ if (hexa_found || exponent_found)
+ return Token(TK_ERROR,"Invalid numeric constant");
+ exponent_found=true;
+ } else if (_is_number(GETCHAR(i))) {
+ //all ok
+ } else if (hexa_found && _is_hex(GETCHAR(i))) {
+
+ } else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) {
+ if (sign_found)
+ return Token(TK_ERROR,"Invalid numeric constant");
+ sign_found=true;
+ } else
+ break;
+
+ str+=CharType(GETCHAR(i));
+ i++;
+ }
+
+ if (!_is_number(str[str.length()-1]))
+ return Token(TK_ERROR,"Invalid numeric constant");
+
+ r_chars+=str.length()-1;
+ return Token(TK_REAL_CONSTANT,str);
+ /*
+ if (period_found)
+ return Token(TK_NUMBER_REAL,str);
+ else
+ return Token(TK_NUMBER_INTEGER,str);*/
+
+ }
+
+ if (GETCHAR(0)=='.') {
+ //parse period
+ return Token(TK_PERIOD);
+ }
+
+ if (_is_text_char(GETCHAR(0))) {
+ // parse identifier
+ String str;
+ str+=CharType(GETCHAR(0));
+
+ while(_is_text_char(GETCHAR(r_chars))) {
+
+ str+=CharType(GETCHAR(r_chars));
+ r_chars++;
+ }
+
+ //see if keyword
+ struct _kws { TokenType token; const char *text;};
+ static const _kws keyword_list[]={
+ {TK_TRUE,"true"},
+ {TK_FALSE,"false"},
+ {TK_TYPE_VOID,"void"},
+ {TK_TYPE_BOOL,"bool"},
+ /*{TK_TYPE_INT,"int"},
+ {TK_TYPE_INT2,"int2"},
+ {TK_TYPE_INT3,"int3"},
+ {TK_TYPE_INT4,"int4"},*/
+ {TK_TYPE_FLOAT,"float"},
+ /*{TK_TYPE_FLOAT2,"float2"},
+ {TK_TYPE_FLOAT3,"float3"},
+ {TK_TYPE_FLOAT4,"float4"},*/
+ {TK_TYPE_VEC2,"vec2"},
+ {TK_TYPE_VEC3,"vec3"},
+ {TK_TYPE_VEC4,"vec4"},
+ {TK_TYPE_TEXTURE,"texture"},
+ {TK_TYPE_CUBEMAP,"cubemap"},
+ {TK_TYPE_COLOR,"color"},
+ /*
+ {TK_TYPE_MAT2,"mat2"},
+ {TK_TYPE_MAT3,"mat3"},
+ {TK_TYPE_MAT4,"mat3"},*/
+ {TK_TYPE_MAT3,"mat3"},
+ {TK_TYPE_MAT4,"mat4"},
+ {TK_CF_IF,"if"},
+ {TK_CF_ELSE,"else"},
+ /*
+ {TK_CF_FOR,"for"},
+ {TK_CF_WHILE,"while"},
+ {TK_CF_DO,"do"},
+ {TK_CF_SWITCH,"switch"},
+ {TK_CF_BREAK,"break"},
+ {TK_CF_CONTINUE,"continue"},*/
+ {TK_CF_RETURN,"return"},
+ {TK_UNIFORM,"uniform"},
+ {TK_ERROR,NULL}
+ };
+
+ int idx=0;
+
+ while(keyword_list[idx].text) {
+
+ if (str==keyword_list[idx].text)
+ return Token(keyword_list[idx].token);
+ idx++;
+ }
+
+
+ return Token(TK_INDENTIFIER,str);
+ }
+
+ return Token(TK_ERROR,"Unknown character");
+
+ } break;
+ }
+
+ ERR_PRINT("BUG");
+ return Token();
+}
+
+Error ShaderLanguage::tokenize(const String& p_text,Vector<Token> *p_tokens,String *r_error,int *r_err_line,int *r_err_column) {
+
+
+ int len =p_text.length();
+ int pos=0;
+
+ int line=0;
+
+ while(pos<len) {
+
+ int advance=0;
+ int prev_line=line;
+ Token t = read_token(&p_text[pos],len-pos,line,advance);
+ t.line=prev_line;
+
+ if (t.type==TK_ERROR) {
+
+ if (r_error) {
+ return ERR_COMPILATION_FAILED;
+ *r_error=t.text;
+ *r_err_line=line;
+ }
+ }
+
+ if (t.type!=TK_EMPTY)
+ p_tokens->push_back(t);
+ //if (prev_line!=line)
+ // p_tokens->push_back(Token(TK_LINE,itos(line)));
+
+ pos+=advance;
+
+ }
+
+ return OK;
+
+}
+
+String ShaderLanguage::lex_debug(const String& p_code) {
+
+ Vector<Token> tokens;
+ String error;
+ int errline,errcol;
+ if (tokenize(p_code,&tokens,&error,&errline,&errcol)!=OK)
+ return error;
+ String ret;
+ for(int i=0;i<tokens.size();i++) {
+ ret+=String(token_names[tokens[i].type])+":"+itos(tokens[i].line)+":"+itos(tokens[i].col)+":"+tokens[i].text+"\n";
+ }
+
+ return ret;
+
+}
+
+bool ShaderLanguage::is_token_datatype(TokenType p_type) {
+
+ return
+ (p_type==TK_TYPE_VOID) ||
+ (p_type==TK_TYPE_BOOL) ||
+ (p_type==TK_TYPE_FLOAT) ||
+ (p_type==TK_TYPE_VEC2) ||
+ (p_type==TK_TYPE_VEC3) ||
+ (p_type==TK_TYPE_VEC4) ||
+ (p_type==TK_TYPE_COLOR) ||
+ (p_type==TK_TYPE_MAT3) ||
+ (p_type==TK_TYPE_MAT4) ||
+ (p_type==TK_TYPE_CUBEMAP) ||
+ (p_type==TK_TYPE_TEXTURE);
+
+}
+
+ShaderLanguage::DataType ShaderLanguage::get_token_datatype(TokenType p_type) {
+
+ switch(p_type) {
+
+ case TK_TYPE_VOID: return TYPE_VOID;
+ case TK_TYPE_BOOL: return TYPE_BOOL;
+ case TK_TYPE_FLOAT: return TYPE_FLOAT;
+ case TK_TYPE_VEC2: return TYPE_VEC2;
+ case TK_TYPE_VEC3: return TYPE_VEC3;
+ case TK_TYPE_VEC4: return TYPE_VEC4;
+ case TK_TYPE_COLOR: return TYPE_VEC4;
+ case TK_TYPE_MAT3: return TYPE_MAT3;
+ case TK_TYPE_MAT4: return TYPE_MAT4;
+ case TK_TYPE_TEXTURE: return TYPE_TEXTURE;
+ case TK_TYPE_CUBEMAP: return TYPE_CUBEMAP;
+ default: return TYPE_VOID;
+ }
+
+ return TYPE_VOID;
+}
+
+
+String ShaderLanguage::get_datatype_name(DataType p_type) {
+
+ switch(p_type) {
+
+ case TYPE_VOID: return "void";
+ case TYPE_BOOL: return "bool";
+ case TYPE_FLOAT: return "float";
+ case TYPE_VEC2: return "vec2";
+ case TYPE_VEC3: return "vec3";
+ case TYPE_VEC4: return "vec4";
+ case TYPE_MAT3: return "mat3";
+ case TYPE_MAT4: return "mat4";
+ case TYPE_TEXTURE: return "texture";
+ case TYPE_CUBEMAP: return "cubemap";
+ default: return "";
+ }
+
+ return "";
+}
+
+
+bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) {
+
+ return
+ (p_type==TK_TYPE_BOOL) ||
+ (p_type==TK_TYPE_FLOAT) ||
+ (p_type==TK_TYPE_VEC2) ||
+ (p_type==TK_TYPE_VEC3) ||
+ (p_type==TK_TYPE_VEC4) ||
+ (p_type==TK_TYPE_COLOR) ||
+ (p_type==TK_TYPE_MAT3) ||
+ (p_type==TK_TYPE_MAT4) ||
+ (p_type==TK_TYPE_TEXTURE) ||
+ (p_type==TK_TYPE_CUBEMAP);
+
+}
+
+
+bool ShaderLanguage::parser_is_at_function(Parser& parser) {
+
+ return (is_token_datatype(parser.get_token_type(0)) && parser.get_token_type(1)==TK_INDENTIFIER && parser.get_token_type(2)==TK_PARENTHESIS_OPEN);
+}
+
+
+
+bool ShaderLanguage::test_existing_identifier(Node *p_node,const StringName p_identifier,bool p_func,bool p_var,bool p_builtin) {
+
+ Node *node = p_node;
+
+ while(node) {
+
+ if (node->type==Node::TYPE_BLOCK) {
+
+ BlockNode *block = (BlockNode*)node;
+ if (block->variables.has(p_identifier))
+ return true;
+ } else if (node->type==Node::TYPE_PROGRAM) {
+
+ ProgramNode *program = (ProgramNode*)node;
+ for(int i=0;i<program->functions.size();i++) {
+
+ if (program->functions[i].name==p_identifier) {
+ return true;
+ }
+ }
+
+ if(program->builtin_variables.has(p_identifier)) {
+ return true;
+ }
+ if(program->uniforms.has(p_identifier)) {
+ return true;
+ }
+
+ } else if (node->type==Node::TYPE_FUNCTION) {
+
+ FunctionNode *func=(FunctionNode*)node;
+ for(int i=0;i<func->arguments.size();i++)
+ if (func->arguments[i].name==p_identifier)
+ return true;
+ }
+
+ node=node->parent;
+ }
+
+ // try keywords
+
+ int idx=0;
+
+ //todo optimize
+ while(intrinsic_func_defs[idx].name) {
+
+ if (p_identifier.operator String()==intrinsic_func_defs[idx].name)
+ return true;
+ idx++;
+ }
+
+
+ return false;
+
+}
+
+
+Error ShaderLanguage::parse_function(Parser& parser,BlockNode *p_block) {
+
+ if (!p_block->parent || p_block->parent->type!=Node::TYPE_PROGRAM) {
+ parser.set_error("Misplaced function");
+ return ERR_PARSE_ERROR;
+
+ }
+
+
+ ProgramNode *program = (ProgramNode*)p_block->parent;
+
+ StringName name = parser.get_token(1).text;
+
+ if (test_existing_identifier(p_block,name)) {
+
+ parser.set_error("Duplicate Identifier (existing variable/builtin/function): "+name);
+ return ERR_PARSE_ERROR;
+
+ }
+
+
+ FunctionNode *function = parser.create_node<FunctionNode>(program);
+ function->body = parser.create_node<BlockNode>(function);
+
+ function->name=name;
+
+ function->return_type=get_token_datatype(parser.get_token_type(0));
+
+ { //add to programnode
+ ProgramNode::Function f;
+ f.name=name;
+ f.function=function;
+ program->functions.push_back(f);
+ }
+
+ int ofs=3;
+
+ while(true) {
+
+ //end of arguments
+ if (parser.get_token_type(ofs)==TK_PARENTHESIS_CLOSE) {
+ ofs++;
+ break;
+ }
+ //next argument awaits
+ if (parser.get_token_type(ofs)==TK_COMMA) {
+ if (!is_token_nonvoid_datatype(parser.get_token_type(ofs+1))) {
+ parser.set_error("Expected Identifier or ')' following ','");
+ return ERR_PARSE_ERROR;
+ }
+ ofs++;
+ continue;
+ }
+
+
+
+ if (!is_token_nonvoid_datatype(parser.get_token_type(ofs+0))) {
+ parser.set_error("Invalid Argument Type");
+ return ERR_PARSE_ERROR;
+ }
+
+ DataType identtype=get_token_datatype(parser.get_token_type(ofs+0));
+
+ if (parser.get_token_type(ofs+1)!=TK_INDENTIFIER) {
+ parser.set_error("Expected Argument Identifier");
+ return ERR_PARSE_ERROR;
+ }
+
+ StringName identname=parser.get_token(ofs+1).text;
+
+ if (test_existing_identifier(function,identname)) {
+ parser.set_error("Duplicate Argument Identifier: "+identname);
+ return ERR_DUPLICATE_SYMBOL;
+ }
+
+ FunctionNode::Argument arg;
+ arg.name=identname;
+ arg.type=identtype;
+ //function->body->variables[arg.name]=arg.type;
+ function->arguments.push_back(arg);
+
+ ofs+=2;
+ }
+
+ parser.advance(ofs);
+ // match {
+ if (parser.get_token_type()!=TK_CURLY_BRACKET_OPEN) {
+ parser.set_error("Expected '{'");
+ return ERR_PARSE_ERROR;
+ }
+
+ parser.advance();
+ Error err = parse_block(parser,function->body);
+
+ if (err)
+ return err;
+
+ // make sure that if the function has a return type, it does return something..
+ if (function->return_type!=TYPE_VOID) {
+ bool found=false;
+ for(int i=0;i<function->body->statements.size();i++) {
+ if (function->body->statements[i]->type==Node::TYPE_CONTROL_FLOW) {
+
+ ControlFlowNode *cf = (ControlFlowNode*)function->body->statements[i];
+ if (cf->flow_op==FLOW_OP_RETURN) {
+ // type of return was already checked when inserted
+ // no need to check here
+ found=true;
+ }
+ }
+ }
+
+ if (!found) {
+ parser.set_error("Function must return a value (use the main block)");
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ return OK;
+}
+
+
+const ShaderLanguage::IntrinsicFuncDef ShaderLanguage::intrinsic_func_defs[]={
+ //constructors
+ {"bool",TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}},
+ {"float",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"vec2",TYPE_VEC2,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"vec3",TYPE_VEC3,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}},
+ {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}},
+ {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}},
+ {"vec4",TYPE_VEC4,{TYPE_VEC2,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}},
+ {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}},
+ {"vec4",TYPE_VEC4,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}},
+ {"mat3",TYPE_MAT3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"mat4",TYPE_MAT4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ //intrinsics - trigonometry
+ {"sin",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"cos",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"tan",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"asin",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"acos",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"atan",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"atan2",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"sinh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"cosh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"tanh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ //intrinsics - exponential
+ {"pow",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"pow",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}},
+ {"pow",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"pow",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}},
+ {"pow",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"pow",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}},
+ {"pow",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"exp",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"exp",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"exp",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"exp",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"log",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"log",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"log",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"log",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"sqrt",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"sqrt",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"sqrt",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"sqrt",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ //intrinsics - common
+ {"abs",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"abs",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"abs",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"abs",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"sign",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"sign",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"sign",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"sign",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"floor",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"floor",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"floor",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"floor",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"trunc",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"trunc",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"trunc",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"trunc",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"round",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"round",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"round",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"round",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"ceil",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"ceil",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"ceil",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"ceil",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"fract",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {"fract",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"fract",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"fract",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"mod",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"mod",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"mod",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"mod",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"min",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"min",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"min",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"min",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"max",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"max",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"max",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"max",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"clamp",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"clamp",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"clamp",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"clamp",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"mix",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}},
+ {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}},
+ {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}},
+ {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"step",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}},
+ {"step",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ //intrinsics - geometric
+ {"length",TYPE_FLOAT,{TYPE_VEC2,TYPE_VOID}},
+ {"length",TYPE_FLOAT,{TYPE_VEC3,TYPE_VOID}},
+ {"length",TYPE_FLOAT,{TYPE_VEC4,TYPE_VOID}},
+ {"distance",TYPE_FLOAT,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"distance",TYPE_FLOAT,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"distance",TYPE_FLOAT,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"dot",TYPE_FLOAT,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}},
+ {"dot",TYPE_FLOAT,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"dot",TYPE_FLOAT,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}},
+ {"cross",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ {"normalize",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {"normalize",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {"normalize",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {"reflect",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}},
+ //intrinsics - texture
+ {"tex",TYPE_VEC4,{TYPE_TEXTURE,TYPE_VEC2,TYPE_VOID}},
+ {"texcube",TYPE_VEC4,{TYPE_CUBEMAP,TYPE_VEC3,TYPE_VOID}},
+ {"texscreen",TYPE_VEC3,{TYPE_VEC2,TYPE_VOID}},
+ {"texpos",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+
+ {NULL,TYPE_VOID,{TYPE_VOID}}
+
+};
+
+const ShaderLanguage::OperatorDef ShaderLanguage::operator_defs[]={
+
+ {OP_ASSIGN,TYPE_VOID,{TYPE_BOOL,TYPE_BOOL}},
+ {OP_ASSIGN,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_ASSIGN,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_ASSIGN,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_ASSIGN,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_ASSIGN,TYPE_VOID,{TYPE_MAT3,TYPE_MAT3}},
+ {OP_ASSIGN,TYPE_VOID,{TYPE_MAT4,TYPE_MAT4}},
+ {OP_ADD,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_ADD,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_ADD,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_ADD,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_SUB,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_SUB,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_SUB,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_SUB,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_MUL,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT}},
+ {OP_MUL,TYPE_VEC2,{TYPE_FLOAT,TYPE_VEC2}},
+ {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_MAT3}},
+ {OP_MUL,TYPE_VEC2,{TYPE_MAT3,TYPE_VEC2}},
+ {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_MAT4}},
+ {OP_MUL,TYPE_VEC2,{TYPE_MAT4,TYPE_VEC2}},
+ {OP_MUL,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_MUL,TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT}},
+ {OP_MUL,TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC3}},
+ {OP_MUL,TYPE_VEC3,{TYPE_MAT3,TYPE_VEC3}},
+ {OP_MUL,TYPE_VEC3,{TYPE_MAT4,TYPE_VEC3}},
+ {OP_MUL,TYPE_VEC3,{TYPE_VEC3,TYPE_MAT3}},
+ {OP_MUL,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_MUL,TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT}},
+ {OP_MUL,TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC4}},
+ {OP_MUL,TYPE_VEC4,{TYPE_MAT4,TYPE_VEC4}},
+ {OP_MUL,TYPE_VEC4,{TYPE_VEC4,TYPE_MAT4}},
+ {OP_MUL,TYPE_MAT3,{TYPE_MAT3,TYPE_MAT3}},
+ {OP_MUL,TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4}},
+ {OP_DIV,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_DIV,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_DIV,TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT}},
+ {OP_DIV,TYPE_VEC2,{TYPE_FLOAT,TYPE_VEC2}},
+ {OP_DIV,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_DIV,TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT}},
+ {OP_DIV,TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC3}},
+ {OP_DIV,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_DIV,TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT}},
+ {OP_DIV,TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC4}},
+ {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC2,TYPE_FLOAT}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT3,TYPE_VEC2}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_VEC2}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC3,TYPE_FLOAT}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT3,TYPE_VEC3}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_VEC3}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC4,TYPE_FLOAT}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_VEC4}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT3,TYPE_MAT3}},
+ {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_MAT4}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC2,TYPE_FLOAT}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC3,TYPE_FLOAT}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}},
+ {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC4,TYPE_FLOAT}},
+ {OP_NEG,TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}},
+ {OP_NEG,TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}},
+ {OP_NEG,TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}},
+ {OP_NEG,TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}},
+ {OP_NOT,TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}},
+ {OP_CMP_EQ,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}},
+ {OP_CMP_EQ,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_CMP_EQ,TYPE_VEC2,{TYPE_VEC3,TYPE_VEC2}},
+ {OP_CMP_EQ,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_CMP_EQ,TYPE_VEC4,{TYPE_VEC3,TYPE_VEC4}},
+ //{OP_CMP_EQ,TYPE_MAT3,{TYPE_MAT4,TYPE_MAT3}}, ??
+ //{OP_CMP_EQ,TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4}}, ??
+ {OP_CMP_NEQ,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}},
+ {OP_CMP_NEQ,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_CMP_NEQ,TYPE_VEC3,{TYPE_VEC2,TYPE_VEC2}},
+ {OP_CMP_NEQ,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}},
+ {OP_CMP_NEQ,TYPE_VEC3,{TYPE_VEC4,TYPE_VEC4}},
+ //{OP_CMP_NEQ,TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4}}, //?
+ {OP_CMP_LEQ,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_CMP_GEQ,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_CMP_LESS,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_CMP_GREATER,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}},
+ {OP_CMP_OR,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}},
+ {OP_CMP_AND,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}},
+ {OP_MAX,TYPE_VOID,{TYPE_VOID,TYPE_VOID}}
+};
+
+
+const ShaderLanguage::BuiltinsDef ShaderLanguage::vertex_builtins_defs[]={
+
+ { "VERTEX", TYPE_VEC3},
+ { "NORMAL", TYPE_VEC3},
+ { "TANGENT", TYPE_VEC3},
+ { "BINORMAL", TYPE_VEC3},
+ { "UV", TYPE_VEC2},
+ { "UV2", TYPE_VEC2},
+ { "COLOR", TYPE_VEC4},
+ { "BONES", TYPE_VEC4},
+ { "WEIGHTS", TYPE_VEC4},
+ { "VAR1", TYPE_VEC4},
+ { "VAR2", TYPE_VEC4},
+ { "SPEC_EXP", TYPE_FLOAT},
+ { "POINT_SIZE", TYPE_FLOAT},
+
+ //builtins
+ { "WORLD_MATRIX", TYPE_MAT4},
+ { "INV_CAMERA_MATRIX", TYPE_MAT4},
+ { "PROJECTION_MATRIX", TYPE_MAT4},
+ { "INSTANCE_ID", TYPE_FLOAT},
+ { "TIME", TYPE_FLOAT},
+ { NULL, TYPE_VOID},
+};
+const ShaderLanguage::BuiltinsDef ShaderLanguage::fragment_builtins_defs[]={
+
+ { "VERTEX", TYPE_VEC3},
+ { "POSITION", TYPE_VEC3},
+ { "NORMAL", TYPE_VEC3},
+ { "TANGENT", TYPE_VEC3},
+ { "BINORMAL", TYPE_VEC3},
+ { "UV", TYPE_VEC2},
+ { "UV2", TYPE_VEC2},
+ { "COLOR", TYPE_VEC4},
+ { "NORMAL", TYPE_VEC3},
+ { "VAR1", TYPE_VEC4},
+ { "VAR2", TYPE_VEC4},
+ { "DIFFUSE", TYPE_VEC3},
+ { "DIFFUSE_ALPHA", TYPE_VEC4},
+ { "SPECULAR", TYPE_VEC3},
+ { "EMISSION", TYPE_VEC3},
+ { "SPEC_EXP", TYPE_FLOAT},
+ { "GLOW", TYPE_FLOAT},
+ { "SHADE_PARAM", TYPE_FLOAT},
+ { "DISCARD", TYPE_FLOAT},
+ { "SCREEN_UV", TYPE_VEC2},
+ { "POINT_COORD", TYPE_VEC2},
+ { "INV_CAMERA_MATRIX", TYPE_MAT4},
+
+// { "SCREEN_POS", TYPE_VEC2},
+// { "SCREEN_TEXEL_SIZE", TYPE_VEC2},
+ { "TIME", TYPE_FLOAT},
+ { NULL, TYPE_VOID}
+
+};
+const ShaderLanguage::BuiltinsDef ShaderLanguage::postprocess_fragment_builtins_defs[]={
+
+ { "IN_COLOR", TYPE_VEC3},
+ { "IN_POSITION", TYPE_VEC3},
+ { "OUT_COLOR", TYPE_VEC3},
+ { "SCREEN_POS", TYPE_VEC2},
+ { "SCREEN_TEXEL_SIZE", TYPE_VEC2},
+ { "TIME", TYPE_FLOAT},
+ { NULL, TYPE_VOID}
+};
+
+
+
+ShaderLanguage::DataType ShaderLanguage::compute_node_type(Node *p_node) {
+
+ switch(p_node->type) {
+
+ case Node::TYPE_PROGRAM: ERR_FAIL_V(TYPE_VOID);
+ case Node::TYPE_FUNCTION: return static_cast<FunctionNode*>(p_node)->return_type;
+ case Node::TYPE_BLOCK: ERR_FAIL_V(TYPE_VOID);
+ case Node::TYPE_VARIABLE: return static_cast<VariableNode*>(p_node)->datatype_cache;
+ case Node::TYPE_CONSTANT: return static_cast<ConstantNode*>(p_node)->datatype;
+ case Node::TYPE_OPERATOR: return static_cast<OperatorNode*>(p_node)->return_cache;
+ case Node::TYPE_CONTROL_FLOW: ERR_FAIL_V(TYPE_VOID);
+ case Node::TYPE_MEMBER: return static_cast<MemberNode*>(p_node)->datatype;
+ }
+
+ return TYPE_VOID;
+}
+
+
+ShaderLanguage::Node* ShaderLanguage::validate_function_call(Parser&parser, OperatorNode *p_func) {
+
+ ERR_FAIL_COND_V(p_func->op!=OP_CALL && p_func->op!=OP_CONSTRUCT,NULL);
+
+
+ Vector<DataType> args;
+
+ ERR_FAIL_COND_V( p_func->arguments[0]->type!=Node::TYPE_VARIABLE, NULL );
+
+ String name = static_cast<VariableNode*>(p_func->arguments[0])->name.operator String();
+
+ bool all_const=true;
+ for(int i=1;i<p_func->arguments.size();i++) {
+ if (p_func->arguments[i]->type!=Node::TYPE_CONSTANT)
+ all_const=false;
+ args.push_back(compute_node_type(p_func->arguments[i]));
+ }
+
+ int argcount=args.size();
+
+ bool found_intrinsic=false;
+
+ if (argcount<=4) {
+ // test intrinsics
+ int idx=0;
+
+ while (intrinsic_func_defs[idx].name) {
+
+ if (name==intrinsic_func_defs[idx].name) {
+
+ bool fail=false;
+ for(int i=0;i<argcount;i++) {
+
+ if (args[i]!=intrinsic_func_defs[idx].args[i]) {
+ fail=true;
+ break;
+ }
+ }
+
+ if (!fail && argcount<4 && intrinsic_func_defs[idx].args[argcount]!=TYPE_VOID)
+ fail=true; //make sure the number of arguments matches
+
+ if (!fail) {
+ p_func->return_cache=intrinsic_func_defs[idx].rettype;
+ found_intrinsic=true;
+ break;
+
+ }
+
+ }
+
+ idx++;
+ }
+ }
+
+
+ if (found_intrinsic) {
+
+ if (p_func->op==OP_CONSTRUCT && all_const) {
+
+ bool all_const=false;
+ Vector<float> cdata;
+ for(int i=0;i<argcount;i++) {
+
+ Variant v = static_cast<ConstantNode*>(p_func->arguments[i+1])->value;
+ switch(v.get_type()) {
+
+ case Variant::REAL: cdata.push_back(v); break;
+ case Variant::VECTOR2: { Vector2 v2=v; cdata.push_back(v2.x); cdata.push_back(v2.y); } break;
+ case Variant::VECTOR3: { Vector3 v3=v; cdata.push_back(v3.x); cdata.push_back(v3.y); cdata.push_back(v3.z);} break;
+ case Variant::PLANE: { Plane v4=v; cdata.push_back(v4.normal.x); cdata.push_back(v4.normal.y); cdata.push_back(v4.normal.z); cdata.push_back(v4.d); } break;
+ default: ERR_FAIL_V(NULL);
+
+ }
+
+ }
+
+ ConstantNode *cn = parser.create_node<ConstantNode>(p_func->parent);
+ Variant data;
+ switch(p_func->return_cache) {
+ case TYPE_FLOAT: data = cdata[0]; break;
+ case TYPE_VEC2: data = Vector2(cdata[0],cdata[1]); break;
+ case TYPE_VEC3: data = Vector3(cdata[0],cdata[1],cdata[2]); break;
+ case TYPE_VEC4: data = Plane(cdata[0],cdata[1],cdata[2],cdata[3]); break;
+ }
+
+ cn->datatype=p_func->return_cache;
+ cn->value=data;
+ return cn;
+
+ }
+ return p_func;
+ }
+
+ // try existing functions..
+
+ FunctionNode *exclude_function=NULL; //exclude current function (in case inside one)
+
+
+ Node *node = p_func;
+
+ while(node->parent) {
+
+ if (node->type==Node::TYPE_FUNCTION) {
+
+ exclude_function = (FunctionNode*)node;
+ }
+
+ node=node->parent;
+ }
+
+ ERR_FAIL_COND_V(node->type!=Node::TYPE_PROGRAM,NULL);
+ ProgramNode *program = (ProgramNode*)node;
+
+ for(int i=0;i<program->functions.size();i++) {
+
+ if (program->functions[i].function==exclude_function)
+ continue;
+
+ FunctionNode *pfunc = program->functions[i].function;
+
+ if (pfunc->arguments.size()!=args.size())
+ continue;
+
+ bool fail=false;
+
+ for(int i=0;i<args.size();i++) {
+ if (args[i]!=pfunc->arguments[i].type) {
+ fail=true;
+ break;
+ }
+ }
+
+ if (!fail) {
+ p_func->return_cache=pfunc->return_type;
+ return p_func;
+ }
+ }
+
+ return NULL;
+}
+
+
+ShaderLanguage::Node * ShaderLanguage::validate_operator(Parser& parser,OperatorNode *p_func) {
+
+ int argcount = p_func->arguments.size();
+ ERR_FAIL_COND_V(argcount>2,NULL);
+
+ DataType argtype[2]={TYPE_VOID,TYPE_VOID};
+ bool all_const=true;
+
+ for(int i=0;i<argcount;i++) {
+
+ argtype[i]=compute_node_type(p_func->arguments[i]);
+ if (p_func->arguments[i]->type!=Node::TYPE_CONSTANT)
+ all_const=false;
+
+ }
+ int idx=0;
+
+ bool valid=false;
+ while(operator_defs[idx].op!=OP_MAX) {
+
+ if (p_func->op==operator_defs[idx].op) {
+
+
+
+ if (operator_defs[idx].args[0]==argtype[0] && operator_defs[idx].args[1]==argtype[1]) {
+
+ p_func->return_cache=operator_defs[idx].rettype;
+ valid=true;
+ break;
+ }
+ }
+
+ idx++;
+ }
+
+ if (!valid)
+ return NULL;
+
+#define _RCO2(m_op,m_vop)\
+case m_op: {\
+ ConstantNode *cn = parser.create_node<ConstantNode>(p_func->parent);\
+ cn->datatype=p_func->return_cache; \
+ Variant::evaluate(m_vop,static_cast<ConstantNode*>(p_func->arguments[0])->value,static_cast<ConstantNode*>(p_func->arguments[1])->value,cn->value,valid);\
+ if (!valid)\
+ return NULL;\
+ return cn;\
+} break;
+
+#define _RCO1(m_op,m_vop)\
+case m_op: {\
+ ConstantNode *cn = parser.create_node<ConstantNode>(p_func->parent);\
+ cn->datatype=p_func->return_cache; \
+ Variant::evaluate(m_vop,static_cast<ConstantNode*>(p_func->arguments[0])->value,Variant(),cn->value,valid);\
+ if (!valid)\
+ return NULL;\
+ return cn;\
+} break;
+
+ if (all_const) {
+ //reduce constant operator
+ switch(p_func->op) {
+ _RCO2(OP_ADD,Variant::OP_ADD);
+ _RCO2(OP_SUB,Variant::OP_SUBSTRACT);
+ _RCO2(OP_MUL,Variant::OP_MULTIPLY);
+ _RCO2(OP_DIV,Variant::OP_DIVIDE);
+ _RCO1(OP_NEG,Variant::OP_NEGATE);
+ _RCO1(OP_NOT,Variant::OP_NOT);
+ _RCO2(OP_CMP_EQ,Variant::OP_EQUAL);
+ _RCO2(OP_CMP_NEQ,Variant::OP_NOT_EQUAL);
+ _RCO2(OP_CMP_LEQ,Variant::OP_LESS_EQUAL);
+ _RCO2(OP_CMP_GEQ,Variant::OP_GREATER_EQUAL);
+ _RCO2(OP_CMP_LESS,Variant::OP_LESS);
+ _RCO2(OP_CMP_GREATER,Variant::OP_GREATER);
+ _RCO2(OP_CMP_OR,Variant::OP_OR);
+ _RCO2(OP_CMP_AND,Variant::OP_AND);
+ default: {}
+ }
+ }
+
+
+ return p_func;
+
+}
+
+bool ShaderLanguage::is_token_operator(TokenType p_type) {
+
+ return (p_type==TK_OP_EQUAL) ||
+ (p_type==TK_OP_NOT_EQUAL) ||
+ (p_type==TK_OP_LESS) ||
+ (p_type==TK_OP_LESS_EQUAL) ||
+ (p_type==TK_OP_GREATER) ||
+ (p_type==TK_OP_GREATER_EQUAL) ||
+ (p_type==TK_OP_AND) ||
+ (p_type==TK_OP_OR) ||
+ (p_type==TK_OP_NOT) ||
+ (p_type==TK_OP_ADD) ||
+ (p_type==TK_OP_SUB) ||
+ (p_type==TK_OP_MUL) ||
+ (p_type==TK_OP_DIV) ||
+ (p_type==TK_OP_NEG) ||
+ (p_type==TK_OP_ASSIGN) ||
+ (p_type==TK_OP_ASSIGN_ADD) ||
+ (p_type==TK_OP_ASSIGN_SUB) ||
+ (p_type==TK_OP_ASSIGN_MUL) ||
+ (p_type==TK_OP_ASSIGN_DIV);
+
+}
+ShaderLanguage::Operator ShaderLanguage::get_token_operator(TokenType p_type) {
+
+ switch(p_type) {
+ case TK_OP_EQUAL: return OP_CMP_EQ ;
+ case TK_OP_NOT_EQUAL: return OP_CMP_NEQ;
+ case TK_OP_LESS: return OP_CMP_LESS ;
+ case TK_OP_LESS_EQUAL: return OP_CMP_LEQ ;
+ case TK_OP_GREATER: return OP_CMP_GREATER ;
+ case TK_OP_GREATER_EQUAL: return OP_CMP_GEQ ;
+ case TK_OP_AND: return OP_CMP_AND ;
+ case TK_OP_OR: return OP_CMP_OR ;
+ case TK_OP_NOT: return OP_NOT ;
+ case TK_OP_ADD: return OP_ADD ;
+ case TK_OP_SUB: return OP_SUB ;
+ case TK_OP_MUL: return OP_MUL ;
+ case TK_OP_DIV: return OP_DIV ;
+ case TK_OP_NEG: return OP_NEG ;
+ case TK_OP_ASSIGN: return OP_ASSIGN ;
+ case TK_OP_ASSIGN_ADD: return OP_ASSIGN_ADD ;
+ case TK_OP_ASSIGN_SUB: return OP_ASSIGN_SUB ;
+ case TK_OP_ASSIGN_MUL: return OP_ASSIGN_MUL ;
+ case TK_OP_ASSIGN_DIV: return OP_ASSIGN_DIV ;
+ default: ERR_FAIL_V(OP_MAX);
+ }
+
+ return OP_MAX;
+}
+
+
+Error ShaderLanguage::parse_expression(Parser& parser,Node *p_parent,Node **r_expr) {
+
+ Vector<Node*> expressions;
+ Vector<TokenType> operators;
+
+ while(true) {
+
+ Node *expr=NULL;
+
+
+ if (parser.get_token_type()==TK_PARENTHESIS_OPEN) {
+ //handle subexpression
+ parser.advance();
+ Error err = parse_expression(parser,p_parent,&expr);
+ if (err)
+ return err;
+
+ if (parser.get_token_type()!=TK_PARENTHESIS_CLOSE) {
+
+ parser.set_error("Expected ')' in expression");
+ return ERR_PARSE_ERROR;
+ }
+
+ parser.advance();
+
+ } else if (parser.get_token_type()==TK_REAL_CONSTANT) {
+
+
+ ConstantNode *constant = parser.create_node<ConstantNode>(p_parent);
+ constant->value=parser.get_token().text.operator String().to_double();
+ constant->datatype=TYPE_FLOAT;
+ expr=constant;
+ parser.advance();
+ } else if (parser.get_token_type()==TK_TRUE) {
+ //print_line("found true");
+
+ //handle true constant
+ ConstantNode *constant = parser.create_node<ConstantNode>(p_parent);
+ constant->value=true;
+ constant->datatype=TYPE_BOOL;
+ expr=constant;
+ parser.advance();
+ } else if (parser.get_token_type()==TK_FALSE) {
+
+ //handle false constant
+ ConstantNode *constant = parser.create_node<ConstantNode>(p_parent);
+ constant->value=false;
+ constant->datatype=TYPE_BOOL;
+ expr=constant;
+ parser.advance();
+ } else if (parser.get_token_type()==TK_TYPE_VOID) {
+
+ //make sure void is not used in expression
+ parser.set_error("Void value not allowed in Expression");
+ return ERR_PARSE_ERROR;
+ } else if (parser.get_token_type(1)==TK_PARENTHESIS_OPEN && (is_token_nonvoid_datatype(parser.get_token_type()) || parser.get_token_type()==TK_INDENTIFIER)) {
+
+
+ //function or constructor
+ StringName name;
+ DataType constructor=TYPE_VOID;
+ if (is_token_nonvoid_datatype(parser.get_token_type())) {
+
+ constructor=get_token_datatype(parser.get_token_type());
+ switch(get_token_datatype(parser.get_token_type())) {
+ case TYPE_BOOL: name="bool"; break;
+ case TYPE_FLOAT: name="float"; break;
+ case TYPE_VEC2: name="vec2"; break;
+ case TYPE_VEC3: name="vec3"; break;
+ case TYPE_VEC4: name="vec4"; break;
+ case TYPE_MAT3: name="mat3"; break;
+ case TYPE_MAT4: name="mat4"; break;
+ default: ERR_FAIL_V(ERR_BUG);
+ }
+ } else {
+
+ name=parser.get_token().text;
+ }
+
+ if (!test_existing_identifier(p_parent,name)) {
+
+ parser.set_error("Unknown identifier in expression: "+name);
+ return ERR_PARSE_ERROR;
+ }
+
+ parser.advance(2);
+
+ OperatorNode *func = parser.create_node<OperatorNode>(p_parent);
+
+ func->op=constructor!=TYPE_VOID?OP_CONSTRUCT:OP_CALL;
+
+ VariableNode *funcname = parser.create_node<VariableNode>(func);
+ funcname->name=name;
+ func->arguments.push_back(funcname);
+
+ //parse parameters
+
+ if (parser.get_token_type()==TK_PARENTHESIS_CLOSE) {
+ parser.advance();
+ } else {
+
+ while(true) {
+
+
+ Node *arg=NULL;
+ Error err = parse_expression(parser,func,&arg);
+ if (err)
+ return err;
+ func->arguments.push_back(arg);
+
+ if (parser.get_token_type()==TK_PARENTHESIS_CLOSE) {
+ parser.advance();
+ break;
+
+ } else if (parser.get_token_type()==TK_COMMA) {
+
+ if (parser.get_token_type(1)==TK_PARENTHESIS_CLOSE) {
+
+ parser.set_error("Expression expected");
+ return ERR_PARSE_ERROR;
+ }
+
+ parser.advance();
+ } else {
+ // something is broken
+ parser.set_error("Expected ',' or ')'");
+ return ERR_PARSE_ERROR;
+ }
+
+ }
+ }
+
+ expr=validate_function_call(parser,func);
+ if (!expr) {
+
+ parser.set_error("Invalid arguments to function/constructor: "+StringName(name));
+ return ERR_PARSE_ERROR;
+
+ }
+
+ } else if (parser.get_token_type()==TK_INDENTIFIER) {
+ //probably variable
+
+
+ Node *node =p_parent;
+ bool existing=false;
+ DataType datatype;
+ StringName identifier=parser.get_token().text;
+
+ while(node) {
+
+ if (node->type==Node::TYPE_BLOCK) {
+
+ BlockNode *block = (BlockNode*)node;
+
+ if (block->variables.has(identifier)) {
+ existing=true;
+ datatype=block->variables[identifier];
+ break;
+ }
+ }
+
+ if (node->type==Node::TYPE_FUNCTION) {
+
+ FunctionNode *function=(FunctionNode*)node;
+ for(int i=0;i<function->arguments.size();i++) {
+ if (function->arguments[i].name==identifier) {
+ existing=true;
+ datatype=function->arguments[i].type;
+ break;
+ }
+ }
+
+ if (existing)
+ break;
+
+ }
+
+ if (node->type==Node::TYPE_PROGRAM) {
+
+ ProgramNode *program = (ProgramNode*)node;
+ if (program->builtin_variables.has(identifier)) {
+ datatype = program->builtin_variables[identifier];
+ existing=true;
+ break;
+ }
+ if (program->uniforms.has(identifier)) {
+ datatype = program->uniforms[identifier].type;
+ existing=true;
+ break;
+ }
+
+ }
+
+ node=node->parent;
+ }
+
+ if (!existing) {
+
+ parser.set_error("Unexisting identifier in expression: "+identifier);
+ return ERR_PARSE_ERROR;
+
+ }
+
+ VariableNode *varname = parser.create_node<VariableNode>(p_parent);
+ varname->name=identifier;
+ varname->datatype_cache=datatype;
+ parser.advance();
+ expr=varname;
+
+ } else if (parser.get_token_type()==TK_OP_NEG || parser.get_token_type()==TK_OP_NOT) {
+
+ //single prefix operators
+ TokenType token_type=parser.get_token_type();
+ parser.advance();
+ Node *subexpr=NULL;
+ Error err = parse_expression(parser,p_parent,&subexpr);
+ if (err)
+ return err;
+
+ OperatorNode *op = parser.create_node<OperatorNode>(p_parent);
+ switch(token_type) {
+ case TK_OP_NEG: op->op=OP_NEG; break;
+ case TK_OP_NOT: op->op=OP_NOT; break;
+ //case TK_OP_PLUS_PLUS: op->op=OP_PLUS_PLUS; break;
+ //case TK_OP_MINUS_MINUS: op->op=OP_MINUS_MINUS; break;
+ default: ERR_FAIL_V(ERR_BUG);
+ }
+
+ op->arguments.push_back(subexpr);
+
+ expr=validate_operator(parser,op);
+
+ if (!expr) {
+
+ parser.set_error("Invalid argument for negation operator");
+ return ERR_PARSE_ERROR;
+ }
+
+ } else {
+ print_line("found bug?");
+ print_line("misplaced token: "+String(token_names[parser.get_token_type()]));
+
+ parser.set_error("Error parsing expression, misplaced: "+String(token_names[parser.get_token_type()]));
+ return ERR_PARSE_ERROR;
+ //nothing
+ }
+
+ ERR_FAIL_COND_V(!expr,ERR_BUG);
+
+
+ /* OK now see what's NEXT to the operator.. */
+ /* OK now see what's NEXT to the operator.. */
+ /* OK now see what's NEXT to the operator.. */
+
+
+ if (parser.get_token_type()==TK_PERIOD) {
+
+ if (parser.get_token_type(1)!=TK_INDENTIFIER) {
+ parser.set_error("Expected identifier as member");
+ return ERR_PARSE_ERROR;
+ }
+
+ DataType dt = compute_node_type(expr);
+ String ident = parser.get_token(1).text;
+
+ bool ok=true;
+ DataType member_type;
+ switch(dt) {
+ case TYPE_VEC2: {
+
+ int l = ident.length();
+ if (l==1) {
+ member_type=TYPE_FLOAT;
+ } else if (l==2) {
+ member_type=TYPE_VEC2;
+ } else {
+ ok=false;
+ break;
+ }
+
+ const CharType *c=ident.ptr();
+ for(int i=0;i<l;i++) {
+
+ switch(c[i]) {
+ case 'r':
+ case 'g':
+ case 'x':
+ case 'y':
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ }
+
+ } break;
+ case TYPE_VEC3: {
+
+ int l = ident.length();
+ if (l==1) {
+ member_type=TYPE_FLOAT;
+ } else if (l==2) {
+ member_type=TYPE_VEC2;
+ } else if (l==3) {
+ member_type=TYPE_VEC3;
+ } else {
+ ok=false;
+ break;
+ }
+
+ const CharType *c=ident.ptr();
+ for(int i=0;i<l;i++) {
+
+ switch(c[i]) {
+ case 'r':
+ case 'g':
+ case 'b':
+ case 'x':
+ case 'y':
+ case 'z':
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ }
+
+ } break;
+ case TYPE_VEC4: {
+
+ int l = ident.length();
+ if (l==1) {
+ member_type=TYPE_FLOAT;
+ } else if (l==2) {
+ member_type=TYPE_VEC2;
+ } else if (l==3) {
+ member_type=TYPE_VEC3;
+ } else if (l==4) {
+ member_type=TYPE_VEC4;
+ } else {
+ ok=false;
+ break;
+ }
+
+ const CharType *c=ident.ptr();
+ for(int i=0;i<l;i++) {
+
+ switch(c[i]) {
+ case 'r':
+ case 'g':
+ case 'b':
+ case 'a':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'w':
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ }
+
+ } break;
+ case TYPE_MAT3: ok=(ident=="x" || ident=="y" || ident=="z" ); member_type=TYPE_VEC3; break;
+ case TYPE_MAT4: ok=(ident=="x" || ident=="y" || ident=="z" || ident=="w"); member_type=TYPE_VEC4; break;
+ default: {}
+ }
+
+ if (!ok) {
+
+ parser.set_error("Invalid member for expression: ."+ident);
+ return ERR_PARSE_ERROR;
+ }
+
+ MemberNode *mn = parser.create_node<MemberNode>(p_parent);
+ mn->basetype=dt;
+ mn->datatype=member_type;
+ mn->name=ident;
+ mn->owner=expr;
+ expr=mn;
+
+ parser.advance(2);
+ //todo
+ //member (period) has priority over any operator
+ //creates a subindexing expression in place
+
+
+ } else if (parser.get_token_type()==TK_BRACKET_OPEN) {
+ //todo
+ //subindexing has priority over any operator
+ //creates a subindexing expression in place
+
+
+ } /*else if (parser.get_token_type()==TK_OP_PLUS_PLUS || parser.get_token_type()==TK_OP_MINUS_MINUS) {
+ //todo
+ //inc/dec operators have priority over any operator
+ //creates a subindexing expression in place
+ //return OK; //wtfs
+
+ } */
+
+
+ expressions.push_back(expr);
+
+
+ if (is_token_operator(parser.get_token_type())) {
+
+ operators.push_back(parser.get_token_type());
+ parser.advance();
+ } else {
+ break;
+ }
+ }
+
+ ERR_FAIL_COND_V(expressions.size()!=(operators.size()+1),ERR_BUG);
+
+ /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
+
+ while(expressions.size()>1) {
+
+ int next_op=-1;
+ int min_priority=0xFFFFF;
+
+ for(int i=0;i<operators.size();i++) {
+
+ int priority;
+ switch(operators[i]) {
+
+ case TK_OP_MUL: priority=0; break;
+ case TK_OP_DIV: priority=0; break;
+
+ case TK_OP_ADD: priority=1; break;
+ case TK_OP_SUB: priority=1; break;
+
+ // shift left/right =2
+
+ case TK_OP_LESS: priority=3; break;
+ case TK_OP_LESS_EQUAL: priority=3; break;
+ case TK_OP_GREATER: priority=3; break;
+ case TK_OP_GREATER_EQUAL: priority=3; break;
+
+ case TK_OP_EQUAL: priority=4; break;
+ case TK_OP_NOT_EQUAL: priority=4; break;
+
+ //bit and =5
+ //bit xor =6
+ //bit or=7
+
+ case TK_OP_AND: priority=8; break;
+ case TK_OP_OR: priority=9; break;
+
+ // ?: = 10
+
+ case TK_OP_ASSIGN_ADD: priority=11; break;
+ case TK_OP_ASSIGN_SUB: priority=11; break;
+ case TK_OP_ASSIGN_MUL: priority=11; break;
+ case TK_OP_ASSIGN_DIV: priority=11; break;
+ case TK_OP_ASSIGN: priority=11; break;
+
+ default: ERR_FAIL_V(ERR_BUG); //unexpected operator
+
+ }
+
+ if (priority<min_priority) {
+ // < is used for left to right (default)
+ // <= is used for right to left
+ next_op=i;
+ min_priority=priority;
+ }
+
+ }
+
+ ERR_FAIL_COND_V(next_op==-1,ERR_BUG);
+
+ // OK! create operator..
+
+ OperatorNode *op = parser.create_node<OperatorNode>(p_parent);
+ op->op=get_token_operator(operators[next_op]);
+
+ op->arguments.push_back(expressions[next_op]); //expression goes as left
+ op->arguments.push_back(expressions[next_op+1]); //next expression goes as right
+
+ expressions[next_op]=validate_operator(parser,op);
+ if (!expressions[next_op]) {
+
+ String at;
+ for(int i=0;i<op->arguments.size();i++) {
+ if (i>0)
+ at+=" and ";
+ at+=get_datatype_name(compute_node_type(op->arguments[i]));
+
+ }
+ parser.set_error("Invalid arguments to operator "+String(token_names[operators[next_op]])+": "+at);
+ return ERR_PARSE_ERROR;
+ }
+
+
+ expressions.remove(next_op+1);
+ operators.remove(next_op);
+
+ }
+
+ *r_expr=expressions[0];
+
+ return OK;
+
+/*
+ TokenType token_type=parser.get_token_type();
+ OperatorNode *op = parser.create_node<OperatorNode>(p_parent);
+ op->op=get_token_operator(parser.get_token_type());
+
+ op->arguments.push_back(*r_expr); //expression goes as left
+ parser.advance();
+ Node *right_expr=NULL;
+ Error err = parse_expression(parser,p_parent,&right_expr);
+ if (err)
+ return err;
+ op->arguments.push_back(right_expr);
+
+ if (!validate_operator(op)) {
+
+ parser.set_error("Invalid arguments to operator "+String(token_names[token_type]));
+ return ERR_PARSE_ERROR;
+ }
+
+*/
+
+}
+
+Error ShaderLanguage::parse_variable_declaration(Parser& parser,BlockNode *p_block) {
+
+ bool uniform = parser.get_token(-1).type==TK_UNIFORM;
+
+ DataType type=get_token_datatype(parser.get_token_type(0));
+ bool iscolor = parser.get_token_type(0)==TK_TYPE_COLOR;
+
+ if (type==TYPE_VOID) {
+
+ parser.set_error("Cannot Declare a 'void' Variable");
+ return ERR_PARSE_ERROR;
+ }
+
+ if (type==TYPE_TEXTURE && !uniform) {
+
+ parser.set_error("Cannot Declare a Non-Uniform Texture");
+ return ERR_PARSE_ERROR;
+ }
+ if (type==TYPE_CUBEMAP && !uniform) {
+
+ parser.set_error("Cannot Declare a Non-Uniform Cubemap");
+ return ERR_PARSE_ERROR;
+ }
+
+
+ parser.advance();
+ int found=0;
+
+ while(true) {
+
+
+ if (found && parser.get_token_type()!=TK_COMMA) {
+ break;
+ }
+
+ if (parser.get_token_type()!=TK_INDENTIFIER) {
+
+ parser.set_error("Identifier Expected");
+ return ERR_PARSE_ERROR;
+
+ }
+
+ StringName name = parser.get_token().text;
+
+ if (test_existing_identifier(p_block,name)) {
+ parser.set_error("Duplicate Identifier (existing variable/function): "+name);
+ return ERR_PARSE_ERROR;
+ }
+
+ found=true;
+
+ parser.advance();
+ //see if declaration has an initializer
+ if (parser.get_token_type()==TK_OP_ASSIGN) {
+ parser.advance();
+ OperatorNode * op = parser.create_node<OperatorNode>(p_block);
+ VariableNode * var = parser.create_node<VariableNode>(op);
+ var->name=name;
+ var->datatype_cache=type;
+ var->uniform=uniform;
+ Node *expr;
+ Error err = parse_expression(parser,p_block,&expr);
+
+ if (err)
+ return err;
+
+ if (var->uniform) {
+
+ if (expr->type!=Node::TYPE_CONSTANT) {
+
+ parser.set_error("Uniform can only be initialized to a constant.");
+ return ERR_PARSE_ERROR;
+ }
+
+ Uniform u;
+ u.order=parser.program->uniforms.size();
+ u.type=type;
+ u.default_value=static_cast<ConstantNode*>(expr)->value;
+ if (iscolor && u.default_value.get_type()==Variant::PLANE) {
+ Color c;
+ Plane p = u.default_value;
+ c=Color(p.normal.x,p.normal.y,p.normal.z,p.d);
+ u.default_value=c;
+ }
+ parser.program->uniforms[var->name]=u;
+ } else {
+ op->op=OP_ASSIGN;
+ op->arguments.push_back(var);
+ op->arguments.push_back(expr);
+ Node *n=validate_operator(parser,op);
+ if (!n) {
+ parser.set_error("Invalid initializer for variable: "+name);
+ return ERR_PARSE_ERROR;
+ }
+ p_block->statements.push_back(n);
+ }
+
+ } else {
+ //initialize it EMPTY
+
+ OperatorNode * op = parser.create_node<OperatorNode>(p_block);
+ VariableNode * var = parser.create_node<VariableNode>(op);
+ ConstantNode * con = parser.create_node<ConstantNode>(op);
+
+ var->name=name;
+ var->datatype_cache=type;
+ var->uniform=uniform;
+ con->datatype=type;
+
+ switch(type) {
+ case TYPE_BOOL: con->value=false; break;
+ case TYPE_FLOAT: con->value=0.0; break;
+ case TYPE_VEC2: con->value=Vector2(); break;
+ case TYPE_VEC3: con->value=Vector3(); break;
+ case TYPE_VEC4: con->value=iscolor?Variant(Color()):Variant(Plane()); break;
+ case TYPE_MAT3: con->value=Matrix3(); break;
+ case TYPE_MAT4: con->value=Transform(); break;
+ case TYPE_TEXTURE:
+ case TYPE_CUBEMAP: con->value=RID(); break;
+ default: {}
+ }
+
+ if (uniform) {
+ Uniform u;
+ u.type=type;
+ u.default_value=con->value;
+ u.order=parser.program->uniforms.size();
+ parser.program->uniforms[var->name]=u;
+
+ } else {
+ op->op=OP_ASSIGN;
+ op->arguments.push_back(var);
+ op->arguments.push_back(con);
+ p_block->statements.push_back(op);
+ }
+
+ }
+
+ if (!uniform)
+ p_block->variables[name]=type;
+
+ }
+
+ if (parser.get_token_type()!=TK_SEMICOLON) {
+ parser.set_error("Expected ';'");
+ return ERR_PARSE_ERROR;
+ }
+
+
+ return OK;
+
+}
+
+Error ShaderLanguage::parse_flow_if(Parser& parser,Node *p_parent,Node **r_statement) {
+
+ ControlFlowNode *cf = parser.create_node<ControlFlowNode>(p_parent);
+
+ cf->flow_op=FLOW_OP_IF;
+
+ parser.advance();
+
+ if (parser.get_token_type()!=TK_PARENTHESIS_OPEN) {
+ parser.set_error("Expected '(' after 'if'");
+ return ERR_PARSE_ERROR;
+ }
+ parser.advance();
+
+ Node *expression=NULL;
+ Error err = parse_expression(parser,cf,&expression);
+ if (err)
+ return err;
+
+ if (compute_node_type(expression)!=TYPE_BOOL) {
+
+ parser.set_error("Expression for 'if' is not boolean");
+ return ERR_PARSE_ERROR;
+ }
+
+ cf->statements.push_back(expression);
+
+ if (parser.get_token_type()!=TK_PARENTHESIS_CLOSE) {
+ parser.set_error("Expected ')' after expression");
+ return ERR_PARSE_ERROR;
+ }
+
+ parser.advance();
+
+ Node *substatement=NULL;
+ err = parse_statement(parser,cf,&substatement);
+ if (err)
+ return err;
+
+
+ cf->statements.push_back(substatement);
+
+
+
+ if (parser.get_token_type()==TK_CF_ELSE) {
+
+ parser.advance();
+ substatement=NULL;
+ err = parse_statement(parser,cf,&substatement);
+ if (err)
+ return err;
+
+ cf->statements.push_back(substatement);
+ }
+
+
+
+ *r_statement=cf;
+
+ return OK;
+}
+
+Error ShaderLanguage::parse_flow_return(Parser& parser,Node *p_parent,Node **r_statement) {
+
+
+ FunctionNode *function=NULL;
+
+ Node *parent=p_parent;
+
+ while(parent) {
+
+ if (parent->type==Node::TYPE_FUNCTION) {
+
+ function=(FunctionNode*)parent;
+ break;
+ }
+
+ parent=parent->parent;
+ }
+
+ if (!function) {
+
+ parser.set_error("'return' must be inside a function");
+ return ERR_PARSE_ERROR;
+ }
+
+ ControlFlowNode *cf = parser.create_node<ControlFlowNode>(p_parent);
+
+ cf->flow_op=FLOW_OP_RETURN;
+
+ parser.advance();
+
+ if (function->return_type!=TYPE_VOID) {
+ // should expect a return expression.
+
+ Node *expr=NULL;
+ Error err = parse_expression(parser,cf,&expr);
+ if (err)
+ return err;
+
+ if (compute_node_type(expr)!=function->return_type) {
+ parser.set_error("Invalid type for 'return' expression");
+ return ERR_PARSE_ERROR;
+ }
+ cf->statements.push_back(expr);
+ }
+
+ *r_statement=cf;
+
+
+ if (parser.get_token_type()!=TK_SEMICOLON) {
+ parser.set_error("Expected ';'");
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
+Error ShaderLanguage::parse_statement(Parser& parser,Node *p_parent,Node **r_statement) {
+
+ *r_statement=NULL;
+
+ TokenType token_type = parser.get_token_type();
+
+ if (token_type==TK_CURLY_BRACKET_OPEN) {
+ //sub-block
+ parser.advance();
+ BlockNode *block = parser.create_node<BlockNode>(p_parent);
+
+ *r_statement=block;
+ return parse_block(parser,block);
+ } else if (token_type==TK_SEMICOLON) {
+ // empty ;
+ parser.advance();
+ return OK;
+ } else if (token_type==TK_CF_IF) {
+ return parse_flow_if(parser,p_parent,r_statement);
+
+ } else if (token_type==TK_CF_RETURN) {
+ return parse_flow_return(parser,p_parent,r_statement);
+ } else {
+ Error err=parse_expression(parser,p_parent,r_statement);
+
+ if (err)
+ return err;
+
+ if (parser.get_token_type()!=TK_SEMICOLON) {
+ parser.set_error("Expected ';'");
+ return ERR_PARSE_ERROR;
+ }
+
+ }
+
+ return OK;
+}
+
+Error ShaderLanguage::parse_block(Parser& parser,BlockNode *p_block) {
+
+ while(true) {
+
+ if (parser.is_at_end()) {
+ if (p_block->parent->type!=Node::TYPE_PROGRAM) {
+ parser.set_error("Unexpected End of File");
+ return ERR_PARSE_ERROR;
+ }
+ return OK; //bye
+ }
+
+ TokenType token_type = parser.get_token_type();
+
+ if (token_type==TK_CURLY_BRACKET_CLOSE) {
+ if (p_block->parent->type==Node::TYPE_PROGRAM) {
+ parser.set_error("Unexpected '}'");
+ return ERR_PARSE_ERROR;
+ }
+ parser.advance();
+ return OK; // exit block
+
+ } else if (token_type==TK_UNIFORM) {
+
+ if (p_block!=parser.program->body) {
+
+ parser.set_error("Uniform only allowed in main program body.");
+ return ERR_PARSE_ERROR;
+ }
+ parser.advance();
+ Error err=parse_variable_declaration(parser,p_block);
+ if (err)
+ return err;
+
+ } else if (is_token_datatype(token_type)) {
+
+ Error err=OK;
+ if (parser_is_at_function(parser))
+ err = parse_function(parser,p_block);
+ else {
+ err = parse_variable_declaration(parser,p_block);
+ }
+
+ if (err)
+ return err;
+
+ } else {
+ // must be a statement
+ Node *statement=NULL;
+
+ Error err = parse_statement(parser,p_block,&statement);
+ if (err)
+ return err;
+ if (statement) {
+ p_block->statements.push_back(statement);
+ }
+
+ }
+ }
+
+ return OK;
+}
+
+
+
+Error ShaderLanguage::parse(const Vector<Token>& p_tokens,ShaderType p_type,CompileFunc p_compile_func,void *p_userdata,String *r_error,int *r_err_line,int *r_err_column) {
+
+
+ uint64_t t = OS::get_singleton()->get_ticks_usec();
+
+ Parser parser(p_tokens);
+ parser.program = parser.create_node<ProgramNode>(NULL);
+ parser.program->body = parser.create_node<BlockNode>(parser.program);
+
+
+ //add builtins
+ switch(p_type) {
+ case SHADER_MATERIAL_VERTEX: {
+ int idx=0;
+ while (vertex_builtins_defs[idx].name) {
+ parser.program->builtin_variables[vertex_builtins_defs[idx].name]=vertex_builtins_defs[idx].type;
+ idx++;
+ }
+ } break;
+ case SHADER_MATERIAL_FRAGMENT: {
+ int idx=0;
+ while (fragment_builtins_defs[idx].name) {
+ parser.program->builtin_variables[fragment_builtins_defs[idx].name]=fragment_builtins_defs[idx].type;
+ idx++;
+ }
+ } break;
+ case SHADER_POST_PROCESS: {
+ int idx=0;
+ while (postprocess_fragment_builtins_defs[idx].name) {
+ parser.program->builtin_variables[postprocess_fragment_builtins_defs[idx].name]=postprocess_fragment_builtins_defs[idx].type;
+ idx++;
+ }
+ } break;
+ }
+
+ Error err = parse_block(parser,parser.program->body);
+ if (err) {
+ parser.get_error(r_error,r_err_line,r_err_column);
+ return err;
+ }
+
+ double tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0;
+ //print_line("parse time: "+rtos(tf));
+
+ t = OS::get_singleton()->get_ticks_usec();
+
+ if (p_compile_func)
+ p_compile_func(p_userdata,parser.program);
+
+ tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0;
+ //print_line("compile time: "+rtos(tf));
+
+ //clean up nodes created
+ while(parser.nodegc.size()) {
+
+ memdelete( parser.nodegc.front()->get() );
+ parser.nodegc.pop_front();
+ }
+ return OK;
+}
+
+Error ShaderLanguage::compile(const String& p_code,ShaderType p_type,CompileFunc p_compile_func,void *p_userdata,String *r_error,int *r_err_line,int *r_err_column) {
+
+ *r_error="";
+ *r_err_line=0;
+ *r_err_column=0;
+ Vector<Token> tokens;
+
+ uint64_t t = OS::get_singleton()->get_ticks_usec();
+
+ Error err = tokenize(p_code,&tokens,r_error,r_err_line,r_err_column);
+
+ double tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0;
+ //print_line("tokenize time: "+rtos(tf));
+
+ if (err!=OK) {
+ return err;
+ }
+ err = parse(tokens,p_type,p_compile_func,p_userdata,r_error,r_err_line,r_err_column);
+ if (err!=OK) {
+ //print_line("LDEBUG: "+lex_debug(p_code));
+ return err;
+ }
+ return OK;
+}
+
+
+void ShaderLanguage::get_keyword_list(ShaderType p_type, List<String> *p_keywords) {
+
+ int idx=0;
+
+ while(intrinsic_func_defs[idx].name) {
+
+ p_keywords->push_back(intrinsic_func_defs[idx].name);
+ idx++;
+ }
+
+ switch(p_type) {
+ case SHADER_MATERIAL_VERTEX: {
+ idx=0;
+ while (vertex_builtins_defs[idx].name) {
+ p_keywords->push_back(vertex_builtins_defs[idx].name);
+ idx++;
+ }
+ } break;
+ case SHADER_MATERIAL_FRAGMENT: {
+ idx=0;
+ while (fragment_builtins_defs[idx].name) {
+ p_keywords->push_back(fragment_builtins_defs[idx].name);
+ idx++;
+ }
+ } break;
+ case SHADER_POST_PROCESS: {
+ idx=0;
+ while (postprocess_fragment_builtins_defs[idx].name) {
+ p_keywords->push_back(postprocess_fragment_builtins_defs[idx].name);
+ idx++;
+ }
+ } break;
+
+ }
+
+}
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
new file mode 100644
index 0000000000..9455e677cf
--- /dev/null
+++ b/servers/visual/shader_language.h
@@ -0,0 +1,403 @@
+/*************************************************************************/
+/* shader_language.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 SHADER_LANGUAGE_H
+#define SHADER_LANGUAGE_H
+
+#include "typedefs.h"
+#include "ustring.h"
+#include "list.h"
+#include "string_db.h"
+#include "map.h"
+#include "variant.h"
+
+
+class ShaderLanguage {
+public:
+
+
+ /* COMPILER */
+
+ enum ShaderType {
+ SHADER_MATERIAL_VERTEX,
+ SHADER_MATERIAL_FRAGMENT,
+ SHADER_POST_PROCESS,
+ };
+
+ enum DataType {
+ TYPE_VOID,
+ TYPE_BOOL,
+ TYPE_FLOAT,
+ TYPE_VEC2,
+ TYPE_VEC3,
+ TYPE_VEC4,
+ TYPE_MAT3,
+ TYPE_MAT4,
+ TYPE_TEXTURE,
+ TYPE_CUBEMAP,
+ };
+
+ enum Operator {
+ OP_ASSIGN,
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_ASSIGN_ADD,
+ OP_ASSIGN_SUB,
+ OP_ASSIGN_MUL,
+ OP_ASSIGN_DIV,
+ OP_NEG,
+ OP_NOT,
+ OP_CMP_EQ,
+ OP_CMP_NEQ,
+ OP_CMP_LEQ,
+ OP_CMP_GEQ,
+ OP_CMP_LESS,
+ OP_CMP_GREATER,
+ OP_CMP_OR,
+ OP_CMP_AND,
+ OP_CALL,
+ OP_CONSTRUCT,
+ OP_MAX
+ };
+
+ enum FlowOperation {
+ FLOW_OP_IF,
+ FLOW_OP_RETURN,
+ //FLOW_OP_FOR,
+ //FLOW_OP_WHILE,
+ //FLOW_OP_DO,
+ //FLOW_OP_BREAK,
+ //FLOW_OP_CONTINUE,
+
+ };
+
+ struct Node {
+
+ enum Type {
+ TYPE_PROGRAM,
+ TYPE_FUNCTION,
+ TYPE_BLOCK,
+ TYPE_VARIABLE,
+ TYPE_CONSTANT,
+ TYPE_OPERATOR,
+ TYPE_CONTROL_FLOW,
+ TYPE_MEMBER
+ };
+
+ Node * parent;
+ Type type;
+
+ virtual DataType get_datatype() const { return TYPE_VOID; }
+
+ virtual ~Node() {}
+ };
+
+
+ struct OperatorNode : public Node {
+
+ DataType return_cache;
+ Operator op;
+ Vector<Node*> arguments;
+ virtual DataType get_datatype() const { return return_cache; }
+
+ OperatorNode() { type=TYPE_OPERATOR; return_cache=TYPE_VOID; }
+ };
+
+ struct VariableNode : public Node {
+ bool uniform;
+ DataType datatype_cache;
+ StringName name;
+ virtual DataType get_datatype() const { return datatype_cache; }
+
+ VariableNode() { type=TYPE_VARIABLE; datatype_cache=TYPE_VOID; uniform=false; }
+ };
+
+ struct ConstantNode : public Node {
+
+ DataType datatype;
+ Variant value;
+ virtual DataType get_datatype() const { return datatype; }
+
+ ConstantNode() { type=TYPE_CONSTANT; }
+ };
+
+ struct BlockNode : public Node {
+
+ Map<StringName,DataType> variables;
+ List<Node*> statements;
+ BlockNode() { type=TYPE_BLOCK; }
+ };
+
+ struct ControlFlowNode : public Node {
+
+ FlowOperation flow_op;
+ Vector<Node*> statements;
+ ControlFlowNode() { type=TYPE_CONTROL_FLOW; flow_op=FLOW_OP_IF;}
+ };
+
+ struct MemberNode : public Node {
+
+ DataType basetype;
+ DataType datatype;
+ StringName name;
+ Node* owner;
+ virtual DataType get_datatype() const { return datatype; }
+ MemberNode() { type=TYPE_MEMBER; }
+ };
+
+
+ struct FunctionNode : public Node {
+
+
+ struct Argument {
+
+ StringName name;
+ DataType type;
+ };
+
+ StringName name;
+ DataType return_type;
+ Vector<Argument> arguments;
+ BlockNode *body;
+
+ FunctionNode() { type=TYPE_FUNCTION; }
+
+ };
+
+ struct Uniform {
+
+ int order;
+ DataType type;
+ Variant default_value;
+ };
+
+ struct ProgramNode : public Node {
+
+ struct Function {
+ StringName name;
+ FunctionNode*function;
+ };
+
+
+ Map<StringName,DataType> builtin_variables;
+ Map<StringName,Uniform> uniforms;
+
+ Vector<Function> functions;
+ BlockNode *body;
+
+ ProgramNode() { type=TYPE_PROGRAM; }
+ };
+
+ typedef void (*CompileFunc)(void*,ProgramNode*);
+
+ struct VarInfo {
+
+ StringName name;
+ DataType type;
+ };
+
+private:
+
+
+
+ enum TokenType {
+
+ TK_EMPTY,
+ TK_INDENTIFIER,
+ TK_TRUE,
+ TK_FALSE,
+ TK_REAL_CONSTANT,
+ TK_TYPE_VOID,
+ TK_TYPE_BOOL,
+ TK_TYPE_FLOAT,
+ TK_TYPE_VEC2,
+ TK_TYPE_VEC3,
+ TK_TYPE_VEC4,
+ TK_TYPE_MAT3,
+ TK_TYPE_MAT4,
+ TK_TYPE_TEXTURE,
+ TK_TYPE_CUBEMAP,
+ TK_TYPE_COLOR,
+ TK_OP_EQUAL,
+ TK_OP_NOT_EQUAL,
+ TK_OP_LESS,
+ TK_OP_LESS_EQUAL,
+ TK_OP_GREATER,
+ TK_OP_GREATER_EQUAL,
+ TK_OP_AND,
+ TK_OP_OR,
+ TK_OP_NOT,
+ TK_OP_ADD,
+ TK_OP_SUB,
+ TK_OP_MUL,
+ TK_OP_DIV,
+ TK_OP_NEG,
+ TK_OP_ASSIGN,
+ TK_OP_ASSIGN_ADD,
+ TK_OP_ASSIGN_SUB,
+ TK_OP_ASSIGN_MUL,
+ TK_OP_ASSIGN_DIV,
+ TK_CF_IF,
+ TK_CF_ELSE,
+ TK_CF_RETURN,
+ TK_BRACKET_OPEN,
+ TK_BRACKET_CLOSE,
+ TK_CURLY_BRACKET_OPEN,
+ TK_CURLY_BRACKET_CLOSE,
+ TK_PARENTHESIS_OPEN,
+ TK_PARENTHESIS_CLOSE,
+ TK_COMMA,
+ TK_SEMICOLON,
+ TK_PERIOD,
+ TK_UNIFORM,
+ TK_ERROR,
+ TK_MAX
+ };
+
+ static const char * token_names[TK_MAX];
+
+ struct Token {
+
+ TokenType type;
+ StringName text;
+ uint16_t line,col;
+
+ Token(TokenType p_type=TK_EMPTY,const String& p_text=String()) { type=p_type; text=p_text; line=0; col=0; }
+ };
+
+
+
+ static Token read_token(const CharType* p_text,int p_len,int &r_line,int &r_chars);
+ static Error tokenize(const String& p_text,Vector<Token> *p_tokens,String *r_error,int *r_err_line,int *r_err_column);
+
+
+
+
+ class Parser {
+
+ Vector<Token> tokens;
+ int pos;
+ String error;
+ public:
+
+
+ void set_error(const String& p_error) { error=p_error; }
+ void get_error(String *r_error, int *r_line, int *r_column) {
+
+ *r_error=error;
+ *r_line=get_token(pos).line;
+ *r_column=get_token(pos).col;
+ }
+
+
+
+ Token get_token(int ofs=0) const { int idx=pos+ofs; if (idx<0 || idx>=tokens.size()) return Token(TK_ERROR); return tokens[idx]; }
+ TokenType get_token_type(int ofs=0) const { int idx=pos+ofs; if (idx<0 || idx>=tokens.size()) return TK_ERROR; return tokens[idx].type; }
+ void advance(int p_amount=1) { pos+=p_amount; }
+ bool is_at_end() const { return pos>=tokens.size(); }
+
+ ProgramNode *program;
+ template<class T>
+ T* create_node(Node *p_parent) { T*n=memnew( T ); nodegc.push_back(n); n->parent=p_parent; return n; }
+ List<Node*> nodegc;
+
+ Parser(const Vector<Token>& p_tokens) { tokens=p_tokens; pos=0;}
+ };
+
+ struct IntrinsicFuncDef {
+
+ enum { MAX_ARGS=5 };
+ const char* name;
+ DataType rettype;
+ const DataType args[MAX_ARGS];
+
+ };
+
+ static const IntrinsicFuncDef intrinsic_func_defs[];
+
+ struct OperatorDef {
+
+ enum { MAX_ARGS=2 };
+ Operator op;
+ DataType rettype;
+ const DataType args[MAX_ARGS];
+ };
+
+ static const OperatorDef operator_defs[];
+
+ struct BuiltinsDef {
+
+ const char* name;
+ DataType type;
+ };
+
+ static const BuiltinsDef vertex_builtins_defs[];
+ static const BuiltinsDef fragment_builtins_defs[];
+ static const BuiltinsDef postprocess_fragment_builtins_defs[];
+
+ static DataType get_token_datatype(TokenType p_type);
+ static String get_datatype_name(DataType p_type);
+ static bool is_token_datatype(TokenType p_type);
+ static bool is_token_nonvoid_datatype(TokenType p_type);
+
+ static bool test_existing_identifier(Node *p_node,const StringName p_identifier,bool p_func=true,bool p_var=true,bool p_builtin=true);
+
+ static bool parser_is_at_function(Parser& parser);
+ static DataType compute_node_type(Node *p_node);
+
+ static Node* validate_function_call(Parser&parser, OperatorNode *p_func);
+ static Node* validate_operator(Parser& parser,OperatorNode *p_func);
+ static bool is_token_operator(TokenType p_type);
+ static Operator get_token_operator(TokenType p_type);
+
+ static Error parse_expression(Parser& parser,Node *p_parent,Node **r_expr);
+
+ static Error parse_variable_declaration(Parser& parser,BlockNode *p_block);
+ static Error parse_function(Parser& parser,BlockNode *p_block);
+ static Error parse_flow_if(Parser& parser,Node *p_parent,Node **r_statement);
+ static Error parse_flow_return(Parser& parser,Node *p_parent,Node **r_statement);
+ static Error parse_statement(Parser& parser,Node *p_parent,Node **r_statement);
+ static Error parse_block(Parser& parser,BlockNode *p_block);
+
+
+ static Error parse(const Vector<Token> &p_tokens,ShaderType p_type,CompileFunc p_compile_func,void *p_userdata,String *r_error,int *r_err_line,int *r_err_column);
+
+;
+public:
+
+ static void get_keyword_list(ShaderType p_type,List<String> *p_keywords);
+
+ static Error compile(const String& p_code,ShaderType p_type, CompileFunc p_compile_func,void *p_userdata,String *r_error,int *r_err_line,int *r_err_column);
+ static String lex_debug(const String& p_code);
+
+};
+
+
+#endif // SHADER_LANGUAGE_H
diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp
new file mode 100644
index 0000000000..42b8d484b5
--- /dev/null
+++ b/servers/visual/visual_server_raster.cpp
@@ -0,0 +1,5717 @@
+/*************************************************************************/
+/* visual_server_raster.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "visual_server_raster.h"
+#include "os/os.h"
+#include "globals.h"
+#include "default_mouse_cursor.xpm"
+#include "sort.h"
+// careful, these may run in different threads than the visual server
+
+BalloonAllocator<> *VisualServerRaster::OctreeAllocator::allocator=NULL;
+
+#define VS_CHANGED\
+ changes++;\
+
+// print_line(__FUNCTION__);
+
+
+
+
+RID VisualServerRaster::texture_create() {
+
+ return rasterizer->texture_create();
+}
+
+
+void VisualServerRaster::texture_allocate(RID p_texture, int p_width, int p_height,Image::Format p_format,uint32_t p_flags) {
+
+ rasterizer->texture_allocate(p_texture,p_width,p_height,p_format,p_flags);
+}
+
+void VisualServerRaster::texture_set_flags(RID p_texture,uint32_t p_flags) {
+
+ VS_CHANGED;
+ rasterizer->texture_set_flags(p_texture,p_flags);
+}
+
+
+void VisualServerRaster::texture_set_data(RID p_texture,const Image& p_image,CubeMapSide p_cube_side) {
+
+ VS_CHANGED;
+ rasterizer->texture_set_data(p_texture,p_image,p_cube_side);
+
+
+}
+
+Image VisualServerRaster::texture_get_data(RID p_texture,CubeMapSide p_cube_side) const {
+
+
+ return rasterizer->texture_get_data(p_texture,p_cube_side);
+}
+
+
+uint32_t VisualServerRaster::texture_get_flags(RID p_texture) const {
+
+ return rasterizer->texture_get_flags(p_texture);
+
+}
+Image::Format VisualServerRaster::texture_get_format(RID p_texture) const {
+
+ return rasterizer->texture_get_format(p_texture);
+
+}
+uint32_t VisualServerRaster::texture_get_width(RID p_texture) const {
+
+ return rasterizer->texture_get_width(p_texture);
+}
+
+uint32_t VisualServerRaster::texture_get_height(RID p_texture) const {
+
+ return rasterizer->texture_get_height(p_texture);
+}
+
+void VisualServerRaster::texture_set_size_override(RID p_texture,int p_width, int p_height) {
+
+ rasterizer->texture_set_size_override(p_texture,p_width,p_height);
+}
+
+bool VisualServerRaster::texture_can_stream(RID p_texture) const {
+
+ return false;
+}
+
+void VisualServerRaster::texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const {
+
+ rasterizer->texture_set_reload_hook(p_texture,p_owner,p_function);
+}
+
+/* SHADER API */
+
+RID VisualServerRaster::shader_create(ShaderMode p_mode) {
+
+ return rasterizer->shader_create(p_mode);
+}
+
+void VisualServerRaster::shader_set_mode(RID p_shader,ShaderMode p_mode){
+
+ VS_CHANGED;
+ rasterizer->shader_set_mode(p_shader,p_mode);
+}
+VisualServer::ShaderMode VisualServerRaster::shader_get_mode(RID p_shader) const{
+
+ return rasterizer->shader_get_mode(p_shader);
+}
+
+
+void VisualServerRaster::shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs,int p_fragment_ofs) {
+
+ VS_CHANGED;
+ rasterizer->shader_set_code(p_shader,p_vertex,p_fragment,p_vertex_ofs,p_fragment_ofs);
+}
+
+String VisualServerRaster::shader_get_vertex_code(RID p_shader) const{
+
+ return rasterizer->shader_get_vertex_code(p_shader);
+}
+
+String VisualServerRaster::shader_get_fragment_code(RID p_shader) const{
+
+ return rasterizer->shader_get_fragment_code(p_shader);
+}
+
+void VisualServerRaster::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const {
+
+ return rasterizer->shader_get_param_list(p_shader,p_param_list);
+}
+
+
+
+/* Material */
+
+RID VisualServerRaster::material_create() {
+
+ return rasterizer->material_create();
+}
+
+void VisualServerRaster::material_set_shader(RID p_material, RID p_shader) {
+
+ VS_CHANGED;
+ rasterizer->material_set_shader(p_material, p_shader );
+}
+
+RID VisualServerRaster::material_get_shader(RID p_material) const {
+
+ return rasterizer->material_get_shader(p_material);
+}
+
+void VisualServerRaster::material_set_param(RID p_material, const StringName& p_param, const Variant& p_value) {
+
+ VS_CHANGED;
+ rasterizer->material_set_param(p_material, p_param,p_value );
+}
+
+Variant VisualServerRaster::material_get_param(RID p_material, const StringName& p_param) const {
+
+ return rasterizer->material_get_param(p_material,p_param);
+}
+
+void VisualServerRaster::material_set_flag(RID p_material, MaterialFlag p_flag,bool p_enabled) {
+ VS_CHANGED;
+ rasterizer->material_set_flag(p_material,p_flag,p_enabled);
+}
+
+void VisualServerRaster::material_set_hint(RID p_material, MaterialHint p_hint,bool p_enabled) {
+
+ VS_CHANGED;
+ rasterizer->material_set_hint(p_material,p_hint,p_enabled);
+}
+
+bool VisualServerRaster::material_get_hint(RID p_material,MaterialHint p_hint) const {
+
+ return rasterizer->material_get_hint(p_material,p_hint);
+
+}
+
+void VisualServerRaster::material_set_shade_model(RID p_material, MaterialShadeModel p_model) {
+ VS_CHANGED;
+ rasterizer->material_set_shade_model(p_material,p_model);
+}
+
+VisualServer::MaterialShadeModel VisualServerRaster::material_get_shade_model(RID p_material) const {
+
+ return rasterizer->material_get_shade_model(p_material);
+
+}
+
+
+bool VisualServerRaster::material_get_flag(RID p_material,MaterialFlag p_flag) const {
+
+ return rasterizer->material_get_flag(p_material,p_flag);
+}
+
+void VisualServerRaster::material_set_blend_mode(RID p_material,MaterialBlendMode p_mode) {
+ VS_CHANGED;
+ rasterizer->material_set_blend_mode(p_material,p_mode);
+}
+VS::MaterialBlendMode VisualServerRaster::material_get_blend_mode(RID p_material) const {
+
+ return rasterizer->material_get_blend_mode(p_material);
+}
+
+void VisualServerRaster::material_set_line_width(RID p_material,float p_line_width) {
+ VS_CHANGED;
+ rasterizer->material_set_line_width(p_material,p_line_width);
+}
+float VisualServerRaster::material_get_line_width(RID p_material) const {
+
+ return rasterizer->material_get_line_width(p_material);
+}
+
+/* FIXED MATERIAL */
+
+RID VisualServerRaster::fixed_material_create() {
+
+ return rasterizer->fixed_material_create();
+}
+
+void VisualServerRaster::fixed_material_set_flag(RID p_material, FixedMaterialFlags p_flag, bool p_enabled) {
+
+ rasterizer->fixed_material_set_flag(p_material,p_flag,p_enabled);
+}
+
+bool VisualServerRaster::fixed_material_get_flag(RID p_material, FixedMaterialFlags p_flag) const {
+
+ return rasterizer->fixed_material_get_flag(p_material,p_flag);
+}
+
+void VisualServerRaster::fixed_material_set_param(RID p_material, FixedMaterialParam p_parameter, const Variant& p_value) {
+ VS_CHANGED;
+ rasterizer->fixed_material_set_parameter(p_material,p_parameter,p_value);
+}
+
+Variant VisualServerRaster::fixed_material_get_param(RID p_material,FixedMaterialParam p_parameter) const {
+
+ return rasterizer->fixed_material_get_parameter(p_material,p_parameter);
+}
+
+
+void VisualServerRaster::fixed_material_set_texture(RID p_material,FixedMaterialParam p_parameter, RID p_texture) {
+ VS_CHANGED;
+ rasterizer->fixed_material_set_texture(p_material,p_parameter,p_texture);
+}
+
+RID VisualServerRaster::fixed_material_get_texture(RID p_material,FixedMaterialParam p_parameter) const {
+
+ return rasterizer->fixed_material_get_texture(p_material,p_parameter);
+}
+
+
+void VisualServerRaster::fixed_material_set_detail_blend_mode(RID p_material,MaterialBlendMode p_mode) {
+ VS_CHANGED;
+ rasterizer->fixed_material_set_detail_blend_mode(p_material,p_mode);
+}
+
+VS::MaterialBlendMode VisualServerRaster::fixed_material_get_detail_blend_mode(RID p_material) const {
+
+ return rasterizer->fixed_material_get_detail_blend_mode(p_material);
+}
+
+
+
+
+void VisualServerRaster::fixed_material_set_texcoord_mode(RID p_material,FixedMaterialParam p_parameter, FixedMaterialTexCoordMode p_mode) {
+ VS_CHANGED;
+ rasterizer->fixed_material_set_texcoord_mode(p_material,p_parameter,p_mode);
+}
+
+VS::FixedMaterialTexCoordMode VisualServerRaster::fixed_material_get_texcoord_mode(RID p_material,FixedMaterialParam p_parameter) const {
+
+ return rasterizer->fixed_material_get_texcoord_mode(p_material,p_parameter);
+}
+
+void VisualServerRaster::fixed_material_set_point_size(RID p_material,float p_size) {
+ VS_CHANGED
+
+ rasterizer->fixed_material_set_point_size(p_material,p_size);
+}
+
+float VisualServerRaster::fixed_material_get_point_size(RID p_material) const{
+
+ return rasterizer->fixed_material_get_point_size(p_material);
+}
+
+
+void VisualServerRaster::fixed_material_set_uv_transform(RID p_material,const Transform& p_transform) {
+ VS_CHANGED;
+ rasterizer->fixed_material_set_uv_transform(p_material,p_transform);
+}
+
+Transform VisualServerRaster::fixed_material_get_uv_transform(RID p_material) const {
+
+ return rasterizer->fixed_material_get_uv_transform(p_material);
+}
+
+
+
+/* MESH API */
+
+RID VisualServerRaster::mesh_create() {
+
+ return rasterizer->mesh_create();
+}
+
+void VisualServerRaster::mesh_set_morph_target_count(RID p_mesh,int p_amount) {
+
+ rasterizer->mesh_set_morph_target_count(p_mesh,p_amount);
+ int amount = rasterizer->mesh_get_morph_target_count(p_mesh);
+
+
+ Map< RID, Set<RID> >::Element * E = instance_dependency_map.find( p_mesh );
+
+ if (!E)
+ return;
+
+
+ Set<RID>::Element *I = E->get().front();
+
+ while(I) {
+
+ Instance *ins = instance_owner.get( I->get() );
+ ins->data.morph_values.resize(amount);
+ I = I->next();
+ }
+}
+
+int VisualServerRaster::mesh_get_morph_target_count(RID p_mesh) const {
+
+ return rasterizer->mesh_get_morph_target_count(p_mesh);
+}
+
+void VisualServerRaster::mesh_set_morph_target_mode(RID p_mesh,MorphTargetMode p_mode) {
+
+ rasterizer->mesh_set_morph_target_mode(p_mesh,p_mode);
+}
+
+VisualServer::MorphTargetMode VisualServerRaster::mesh_get_morph_target_mode(RID p_mesh) const{
+
+ return rasterizer->mesh_get_morph_target_mode(p_mesh);
+}
+
+void VisualServerRaster::mesh_add_custom_surface(RID p_mesh,const Variant& p_dat) {
+
+
+}
+
+
+void VisualServerRaster::mesh_add_surface(RID p_mesh,PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes,bool p_alpha_sort) {
+
+ VS_CHANGED;
+ _dependency_queue_update(p_mesh,true);
+ rasterizer->mesh_add_surface(p_mesh,p_primitive,p_arrays,p_blend_shapes,p_alpha_sort);
+
+}
+
+Array VisualServerRaster::mesh_get_surface_arrays(RID p_mesh,int p_surface) const {
+
+ return rasterizer->mesh_get_surface_arrays(p_mesh,p_surface);
+}
+Array VisualServerRaster::mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const {
+
+ return rasterizer->mesh_get_surface_morph_arrays(p_mesh,p_surface);
+}
+
+
+
+
+void VisualServerRaster::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned){
+ VS_CHANGED;
+ rasterizer->mesh_surface_set_material(p_mesh,p_surface,p_material,p_owned);
+}
+
+RID VisualServerRaster::mesh_surface_get_material(RID p_mesh,int p_surface) const {
+
+ return rasterizer->mesh_surface_get_material(p_mesh,p_surface);
+
+}
+
+
+int VisualServerRaster::mesh_surface_get_array_len(RID p_mesh, int p_surface) const{
+
+ return rasterizer->mesh_surface_get_array_len(p_mesh,p_surface);
+}
+
+int VisualServerRaster::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const{
+
+
+ return rasterizer->mesh_surface_get_array_index_len(p_mesh,p_surface);
+}
+
+uint32_t VisualServerRaster::mesh_surface_get_format(RID p_mesh, int p_surface) const{
+
+ return rasterizer->mesh_surface_get_format(p_mesh,p_surface);
+}
+
+VisualServer::PrimitiveType VisualServerRaster::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const{
+
+ return rasterizer->mesh_surface_get_primitive_type(p_mesh,p_surface);
+}
+
+
+void VisualServerRaster::mesh_remove_surface(RID p_mesh,int p_surface){
+
+ rasterizer->mesh_remove_surface(p_mesh,p_surface);
+}
+
+int VisualServerRaster::mesh_get_surface_count(RID p_mesh) const{
+
+ return rasterizer->mesh_get_surface_count(p_mesh);
+
+}
+
+/* MULTIMESH */
+
+RID VisualServerRaster::multimesh_create() {
+
+ return rasterizer->multimesh_create();
+}
+
+void VisualServerRaster::multimesh_set_instance_count(RID p_multimesh,int p_count) {
+ VS_CHANGED;
+ rasterizer->multimesh_set_instance_count(p_multimesh,p_count);
+}
+
+int VisualServerRaster::multimesh_get_instance_count(RID p_multimesh) const {
+
+ return rasterizer->multimesh_get_instance_count(p_multimesh);
+}
+
+void VisualServerRaster::multimesh_set_mesh(RID p_multimesh,RID p_mesh) {
+ VS_CHANGED;
+ rasterizer->multimesh_set_mesh(p_multimesh,p_mesh);
+
+}
+void VisualServerRaster::multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb) {
+ VS_CHANGED;
+ rasterizer->multimesh_set_aabb(p_multimesh,p_aabb);
+ _dependency_queue_update(p_multimesh,true);
+
+}
+
+void VisualServerRaster::multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform) {
+ VS_CHANGED;
+ rasterizer->multimesh_instance_set_transform(p_multimesh,p_index,p_transform);
+
+}
+void VisualServerRaster::multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color) {
+ VS_CHANGED;
+ rasterizer->multimesh_instance_set_color(p_multimesh,p_index,p_color);
+
+}
+RID VisualServerRaster::multimesh_get_mesh(RID p_multimesh) const {
+
+ return rasterizer->multimesh_get_mesh(p_multimesh);
+}
+AABB VisualServerRaster::multimesh_get_aabb(RID p_multimesh,const AABB& p_aabb) const {
+
+
+ return rasterizer->multimesh_get_aabb(p_multimesh);
+}
+
+Transform VisualServerRaster::multimesh_instance_get_transform(RID p_multimesh,int p_index) const {
+
+ return rasterizer->multimesh_instance_get_transform(p_multimesh,p_index);
+}
+Color VisualServerRaster::multimesh_instance_get_color(RID p_multimesh,int p_index) const {
+
+ return rasterizer->multimesh_instance_get_color(p_multimesh,p_index);
+}
+
+void VisualServerRaster::multimesh_set_visible_instances(RID p_multimesh,int p_visible) {
+
+ rasterizer->multimesh_set_visible_instances(p_multimesh,p_visible);
+
+}
+
+int VisualServerRaster::multimesh_get_visible_instances(RID p_multimesh) const {
+
+ return rasterizer->multimesh_get_visible_instances(p_multimesh);
+}
+
+
+
+
+/* PARTICLES API */
+
+RID VisualServerRaster::particles_create() {
+
+ return rasterizer->particles_create();
+}
+
+void VisualServerRaster::particles_set_amount(RID p_particles, int p_amount) {
+ VS_CHANGED;
+ rasterizer->particles_set_amount(p_particles,p_amount);
+}
+
+int VisualServerRaster::particles_get_amount(RID p_particles) const {
+
+ return rasterizer->particles_get_amount(p_particles);
+}
+
+void VisualServerRaster::particles_set_emitting(RID p_particles, bool p_emitting) {
+ VS_CHANGED;
+ rasterizer->particles_set_emitting(p_particles,p_emitting);
+}
+bool VisualServerRaster::particles_is_emitting(RID p_particles) const {
+
+ return rasterizer->particles_is_emitting(p_particles);
+}
+
+void VisualServerRaster::particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility) {
+ VS_CHANGED;
+ rasterizer->particles_set_visibility_aabb(p_particles, p_visibility);
+}
+AABB VisualServerRaster::particles_get_visibility_aabb(RID p_particles) const {
+
+ return rasterizer->particles_get_visibility_aabb(p_particles);
+}
+
+void VisualServerRaster::particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents) {
+ VS_CHANGED;
+ rasterizer->particles_set_emission_half_extents(p_particles,p_half_extents);
+}
+Vector3 VisualServerRaster::particles_get_emission_half_extents(RID p_particles) const {
+
+ return rasterizer->particles_get_emission_half_extents(p_particles);
+}
+
+void VisualServerRaster::particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity) {
+ VS_CHANGED;
+ rasterizer->particles_set_emission_base_velocity(p_particles,p_base_velocity);
+}
+Vector3 VisualServerRaster::particles_get_emission_base_velocity(RID p_particles) const {
+
+ return rasterizer->particles_get_emission_base_velocity(p_particles);
+}
+
+void VisualServerRaster::particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points) {
+
+ VS_CHANGED;
+ rasterizer->particles_set_emission_points(p_particles,p_points);
+}
+
+DVector<Vector3> VisualServerRaster::particles_get_emission_points(RID p_particles) const {
+
+ return rasterizer->particles_get_emission_points(p_particles);
+}
+
+void VisualServerRaster::particles_set_gravity_normal(RID p_particles, const Vector3& p_normal) {
+ VS_CHANGED;
+ rasterizer->particles_set_gravity_normal(p_particles,p_normal);
+}
+Vector3 VisualServerRaster::particles_get_gravity_normal(RID p_particles) const {
+
+ return rasterizer->particles_get_gravity_normal(p_particles);
+}
+
+void VisualServerRaster::particles_set_variable(RID p_particles, ParticleVariable p_variable,float p_value) {
+ VS_CHANGED;
+ rasterizer->particles_set_variable(p_particles,p_variable,p_value);
+}
+float VisualServerRaster::particles_get_variable(RID p_particles, ParticleVariable p_variable) const {
+
+ return rasterizer->particles_get_variable(p_particles,p_variable);
+}
+
+void VisualServerRaster::particles_set_randomness(RID p_particles, ParticleVariable p_variable,float p_randomness) {
+ VS_CHANGED;
+ rasterizer->particles_set_randomness(p_particles,p_variable,p_randomness);
+}
+float VisualServerRaster::particles_get_randomness(RID p_particles, ParticleVariable p_variable) const {
+
+ return rasterizer->particles_get_randomness(p_particles,p_variable);
+}
+
+void VisualServerRaster::particles_set_color_phases(RID p_particles, int p_phases) {
+ VS_CHANGED;
+ rasterizer->particles_set_color_phases(p_particles,p_phases);
+}
+int VisualServerRaster::particles_get_color_phases(RID p_particles) const {
+
+ return rasterizer->particles_get_color_phases(p_particles);
+}
+
+void VisualServerRaster::particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos) {
+ VS_CHANGED;
+ rasterizer->particles_set_color_phase_pos(p_particles,p_phase,p_pos);
+}
+float VisualServerRaster::particles_get_color_phase_pos(RID p_particles, int p_phase) const {
+
+ return rasterizer->particles_get_color_phase_pos(p_particles,p_phase);
+}
+
+void VisualServerRaster::particles_set_attractors(RID p_particles, int p_attractors) {
+ VS_CHANGED;
+ rasterizer->particles_set_attractors(p_particles,p_attractors);
+}
+int VisualServerRaster::particles_get_attractors(RID p_particles) const {
+
+ return rasterizer->particles_get_attractors(p_particles);
+}
+
+void VisualServerRaster::particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos) {
+ VS_CHANGED;
+ rasterizer->particles_set_attractor_pos(p_particles,p_attractor,p_pos);
+}
+Vector3 VisualServerRaster::particles_get_attractor_pos(RID p_particles,int p_attractor) const {
+
+ return rasterizer->particles_get_attractor_pos(p_particles,p_attractor);
+}
+
+void VisualServerRaster::particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force) {
+ VS_CHANGED;
+ rasterizer->particles_set_attractor_strength(p_particles,p_attractor,p_force);
+}
+
+float VisualServerRaster::particles_get_attractor_strength(RID p_particles,int p_attractor) const {
+
+ return rasterizer->particles_get_attractor_strength(p_particles,p_attractor);
+}
+
+void VisualServerRaster::particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color) {
+ VS_CHANGED;
+ rasterizer->particles_set_color_phase_color(p_particles,p_phase,p_color);
+}
+Color VisualServerRaster::particles_get_color_phase_color(RID p_particles, int p_phase) const {
+
+ return rasterizer->particles_get_color_phase_color(p_particles,p_phase);
+}
+
+void VisualServerRaster::particles_set_material(RID p_particles, RID p_material,bool p_owned) {
+ VS_CHANGED;
+ rasterizer->particles_set_material(p_particles,p_material,p_owned);
+}
+RID VisualServerRaster::particles_get_material(RID p_particles) const {
+
+ return rasterizer->particles_get_material(p_particles);
+}
+
+
+void VisualServerRaster::particles_set_height_from_velocity(RID p_particles, bool p_enable) {
+
+ VS_CHANGED;
+ rasterizer->particles_set_height_from_velocity(p_particles,p_enable);
+
+}
+
+bool VisualServerRaster::particles_has_height_from_velocity(RID p_particles) const {
+
+ return rasterizer->particles_has_height_from_velocity(p_particles);
+}
+
+void VisualServerRaster::particles_set_use_local_coordinates(RID p_particles, bool p_enable) {
+
+ rasterizer->particles_set_use_local_coordinates(p_particles,p_enable);
+}
+
+bool VisualServerRaster::particles_is_using_local_coordinates(RID p_particles) const {
+
+ return rasterizer->particles_is_using_local_coordinates(p_particles);
+}
+
+
+/* Light API */
+
+RID VisualServerRaster::light_create(LightType p_type) {
+
+ return rasterizer->light_create(p_type);
+}
+VisualServer::LightType VisualServerRaster::light_get_type(RID p_light) const {
+
+ return rasterizer->light_get_type(p_light);
+}
+
+void VisualServerRaster::light_set_color(RID p_light,LightColor p_type, const Color& p_color) {
+ VS_CHANGED;
+ rasterizer->light_set_color(p_light,p_type,p_color);
+
+}
+Color VisualServerRaster::light_get_color(RID p_light,LightColor p_type) const {
+
+ return rasterizer->light_get_color(p_light,p_type);
+
+}
+
+
+void VisualServerRaster::light_set_shadow(RID p_light,bool p_enabled) {
+ VS_CHANGED;
+ rasterizer->light_set_shadow(p_light,p_enabled);
+}
+
+bool VisualServerRaster::light_has_shadow(RID p_light) const {
+
+ return rasterizer->light_has_shadow(p_light);
+}
+
+
+
+void VisualServerRaster::light_set_volumetric(RID p_light,bool p_enabled) {
+ VS_CHANGED;
+ rasterizer->light_set_volumetric(p_light,p_enabled);
+}
+
+bool VisualServerRaster::light_is_volumetric(RID p_light) const {
+
+ return rasterizer->light_is_volumetric(p_light);
+}
+
+void VisualServerRaster::light_set_projector(RID p_light,RID p_texture) {
+ VS_CHANGED;
+ rasterizer->light_set_projector(p_light,p_texture);
+}
+
+RID VisualServerRaster::light_get_projector(RID p_light) const {
+
+ return rasterizer->light_get_projector(p_light);
+}
+
+void VisualServerRaster::light_set_param(RID p_light, LightParam p_var, float p_value) {
+ VS_CHANGED;
+ rasterizer->light_set_var(p_light,p_var,p_value);
+ _dependency_queue_update(p_light,true);
+
+}
+
+float VisualServerRaster::light_get_param(RID p_light, LightParam p_var) const {
+
+
+ return rasterizer->light_get_var(p_light,p_var);
+}
+
+void VisualServerRaster::light_set_operator(RID p_light,LightOp p_op) {
+ VS_CHANGED;
+ rasterizer->light_set_operator(p_light,p_op);
+}
+
+VisualServerRaster::LightOp VisualServerRaster::light_get_operator(RID p_light) const {
+
+ return rasterizer->light_get_operator(p_light);
+}
+
+void VisualServerRaster::light_omni_set_shadow_mode(RID p_light,LightOmniShadowMode p_mode) {
+ VS_CHANGED;
+ rasterizer->light_omni_set_shadow_mode(p_light,p_mode);
+}
+
+VisualServerRaster::LightOmniShadowMode VisualServerRaster::light_omni_get_shadow_mode(RID p_light) const {
+
+ return rasterizer->light_omni_get_shadow_mode(p_light);
+}
+
+void VisualServerRaster::light_directional_set_shadow_mode(RID p_light,LightDirectionalShadowMode p_mode){
+ VS_CHANGED;
+ rasterizer->light_directional_set_shadow_mode(p_light,p_mode);
+}
+
+VS::LightDirectionalShadowMode VisualServerRaster::light_directional_get_shadow_mode(RID p_light) const{
+
+ return rasterizer->light_directional_get_shadow_mode(p_light);
+}
+
+void VisualServerRaster::light_directional_set_shadow_param(RID p_light,LightDirectionalShadowParam p_param, float p_value) {
+ VS_CHANGED;
+ rasterizer->light_directional_set_shadow_param(p_light,p_param,p_value);
+}
+
+float VisualServerRaster::light_directional_get_shadow_param(RID p_light,LightDirectionalShadowParam p_param) const {
+
+ return rasterizer->light_directional_get_shadow_param(p_light,p_param);
+}
+
+
+RID VisualServerRaster::skeleton_create() {
+
+ return rasterizer->skeleton_create();
+}
+
+void VisualServerRaster::skeleton_resize(RID p_skeleton,int p_bones) {
+ VS_CHANGED;
+ rasterizer->skeleton_resize(p_skeleton,p_bones);
+}
+
+int VisualServerRaster::skeleton_get_bone_count(RID p_skeleton) const {
+
+ return rasterizer->skeleton_get_bone_count(p_skeleton);
+}
+
+void VisualServerRaster::skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform) {
+ VS_CHANGED;
+ return rasterizer->skeleton_bone_set_transform(p_skeleton,p_bone,p_transform);
+
+}
+
+Transform VisualServerRaster::skeleton_bone_get_transform(RID p_skeleton,int p_bone) {
+
+
+ return rasterizer->skeleton_bone_get_transform(p_skeleton,p_bone);
+
+}
+
+
+/* VISIBILITY API */
+
+/* ROOM API */
+
+RID VisualServerRaster::room_create() {
+
+ Room *room = memnew( Room );
+ ERR_FAIL_COND_V(!room,RID());
+ return room_owner.make_rid( room );
+
+}
+
+void VisualServerRaster::room_set_bounds(RID p_room, const BSP_Tree& p_bounds) {
+ VS_CHANGED;
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND(!room);
+ room->bounds=p_bounds;
+
+}
+
+BSP_Tree VisualServerRaster::room_get_bounds(RID p_room) const {
+
+ Room *room = room_owner.get(p_room);
+ ERR_FAIL_COND_V(!room, BSP_Tree());
+ return room->bounds;
+
+}
+
+/* PORTAL API */
+
+RID VisualServerRaster::portal_create() {
+ VS_CHANGED;
+ Portal *portal = memnew( Portal );
+ ERR_FAIL_COND_V(!portal,RID());
+ return portal_owner.make_rid( portal );
+
+
+}
+
+
+void VisualServerRaster::portal_set_shape(RID p_portal, const Vector<Point2>& p_shape) {
+ VS_CHANGED;
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND(!portal);
+ portal->shape=p_shape;
+
+ portal->bounds=Rect2();
+
+ for(int i=0;i<p_shape.size();i++) {
+
+ if (i==0)
+ portal->bounds.pos=p_shape[i];
+ else
+ portal->bounds.expand_to(p_shape[i]);
+ }
+
+ _dependency_queue_update(p_portal,true);
+}
+
+
+Vector<Point2> VisualServerRaster::portal_get_shape(RID p_portal) const {
+
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND_V(!portal, Vector<Point2>());
+ return portal->shape;
+
+
+}
+
+void VisualServerRaster::portal_set_enabled(RID p_portal, bool p_enabled) {
+ VS_CHANGED;
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND(!portal);
+ portal->enabled=p_enabled;
+
+}
+
+bool VisualServerRaster::portal_is_enabled(RID p_portal) const {
+
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND_V(!portal, false);
+ return portal->enabled;
+
+}
+void VisualServerRaster::portal_set_disable_distance(RID p_portal, float p_distance) {
+ VS_CHANGED;
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND(!portal);
+ portal->disable_distance=p_distance;
+
+}
+float VisualServerRaster::portal_get_disable_distance(RID p_portal) const {
+
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND_V(!portal, -1);
+ return portal->disable_distance;
+
+}
+void VisualServerRaster::portal_set_disabled_color(RID p_portal, const Color& p_color) {
+ VS_CHANGED;
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND(!portal);
+ portal->disable_color=p_color;
+
+}
+Color VisualServerRaster::portal_get_disabled_color(RID p_portal) const {
+
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND_V(!portal, Color());
+ return portal->disable_color;
+}
+
+void VisualServerRaster::portal_set_connect_range(RID p_portal, float p_range) {
+ VS_CHANGED;
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND(!portal);
+ portal->connect_range=p_range;
+ _dependency_queue_update(p_portal,true);
+}
+
+float VisualServerRaster::portal_get_connect_range(RID p_portal) const {
+
+ Portal *portal = portal_owner.get(p_portal);
+ ERR_FAIL_COND_V(!portal,0);
+ return portal->connect_range;
+}
+
+
+/* CAMERA API */
+
+RID VisualServerRaster::camera_create() {
+
+ Camera * camera = memnew( Camera );
+ return camera_owner.make_rid( camera );
+
+}
+
+void VisualServerRaster::camera_set_perspective(RID p_camera,float p_fovy_degrees, float p_z_near, float p_z_far) {
+ VS_CHANGED;
+ Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND(!camera);
+ camera->type=Camera::PERSPECTIVE;
+ camera->fov=p_fovy_degrees;
+ camera->znear=p_z_near;
+ camera->zfar=p_z_far;
+
+}
+
+void VisualServerRaster::camera_set_orthogonal(RID p_camera,float p_size, float p_z_near, float p_z_far) {
+ VS_CHANGED;
+ Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND(!camera);
+ camera->type=Camera::ORTHOGONAL;
+ camera->size=p_size;
+ camera->znear=p_z_near;
+ camera->zfar=p_z_far;
+}
+
+void VisualServerRaster::camera_set_transform(RID p_camera,const Transform& p_transform) {
+ VS_CHANGED;
+ Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND(!camera);
+ camera->transform=p_transform;
+
+
+}
+
+void VisualServerRaster::camera_set_visible_layers(RID p_camera,uint32_t p_layers) {
+
+ VS_CHANGED;
+ Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND(!camera);
+
+ camera->visible_layers=p_layers;
+
+}
+
+uint32_t VisualServerRaster::camera_get_visible_layers(RID p_camera) const{
+
+ const Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND_V(!camera,0);
+
+ return camera->visible_layers;
+}
+
+void VisualServerRaster::camera_set_environment(RID p_camera,RID p_env) {
+
+ Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND(!camera);
+ camera->env=p_env;
+
+}
+
+RID VisualServerRaster::camera_get_environment(RID p_camera) const {
+
+ const Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND_V(!camera,RID());
+ return camera->env;
+
+}
+
+void VisualServerRaster::camera_set_use_vertical_aspect(RID p_camera,bool p_enable) {
+
+ Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND(!camera);
+ camera->vaspect=p_enable;
+
+}
+bool VisualServerRaster::camera_is_using_vertical_aspect(RID p_camera,bool p_enable) const{
+
+ const Camera *camera = camera_owner.get( p_camera );
+ ERR_FAIL_COND_V(!camera,false);
+ return camera->vaspect;
+
+}
+
+
+/* VIEWPORT API */
+
+
+RID VisualServerRaster::viewport_create() {
+
+ Viewport *viewport = memnew( Viewport );
+ RID rid = viewport_owner.make_rid( viewport );
+ ERR_FAIL_COND_V( !rid.is_valid(), rid );
+
+ viewport->self=rid;
+ viewport->hide_scenario=false;
+ viewport->hide_canvas=false;
+ viewport->viewport_data=rasterizer->viewport_data_create();
+
+ return rid;
+}
+
+void VisualServerRaster::viewport_attach_to_screen(RID p_viewport,int p_screen) {
+
+ VS_CHANGED;
+ Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+
+ screen_viewports[p_viewport]=p_screen;
+}
+
+void VisualServerRaster::viewport_detach(RID p_viewport) {
+
+ VS_CHANGED;
+ Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ ERR_FAIL_COND(!screen_viewports.has(p_viewport));
+ screen_viewports.erase(p_viewport);
+
+}
+
+void VisualServerRaster::viewport_set_as_render_target(RID p_viewport,bool p_enable) {
+
+ VS_CHANGED;
+ Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ if (viewport->render_target.is_valid()==p_enable)
+ return;
+ if (!p_enable) {
+
+ rasterizer->free(viewport->render_target);
+ viewport->render_target=RID();
+ viewport->render_target_texture=RID();
+ if (viewport->update_list.in_list())
+ viewport_update_list.remove(&viewport->update_list);
+
+ } else {
+
+ viewport->render_target=rasterizer->render_target_create();
+ rasterizer->render_target_set_size(viewport->render_target,viewport->rect.width,viewport->rect.height);
+ viewport->render_target_texture=rasterizer->render_target_get_texture(viewport->render_target);
+ if (viewport->render_target_update_mode!=RENDER_TARGET_UPDATE_DISABLED)
+ viewport_update_list.add(&viewport->update_list);
+ }
+
+}
+
+void VisualServerRaster::viewport_set_render_target_update_mode(RID p_viewport,RenderTargetUpdateMode p_mode){
+
+ VS_CHANGED;
+ Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ if (viewport->update_list.in_list())
+ viewport_update_list.remove(&viewport->update_list);
+
+ viewport->render_target_update_mode=p_mode;
+
+ if (viewport->render_target_update_mode!=RENDER_TARGET_UPDATE_DISABLED)
+ viewport_update_list.add(&viewport->update_list);
+
+}
+VisualServer::RenderTargetUpdateMode VisualServerRaster::viewport_get_render_target_update_mode(RID p_viewport) const{
+
+ const Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport,RENDER_TARGET_UPDATE_DISABLED);
+
+ return viewport->render_target_update_mode;
+}
+RID VisualServerRaster::viewport_get_render_target_texture(RID p_viewport) const{
+
+ Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport,RID());
+
+ return viewport->render_target_texture;
+
+}
+
+void VisualServerRaster::viewport_queue_screen_capture(RID p_viewport) {
+
+ VS_CHANGED;
+ Viewport *viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+ viewport->queue_capture=true;
+
+}
+
+Image VisualServerRaster::viewport_get_screen_capture(RID p_viewport) const {
+
+ Viewport *viewport = (Viewport*)viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport,Image());
+
+ Image ret = viewport->capture;
+ viewport->capture=Image();
+ return ret;
+}
+
+void VisualServerRaster::viewport_set_rect(RID p_viewport,const ViewportRect& p_rect) {
+ VS_CHANGED;
+ Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+
+ ERR_FAIL_COND(!viewport);
+
+ viewport->rect=p_rect;
+}
+
+
+VisualServer::ViewportRect VisualServerRaster::viewport_get_rect(RID p_viewport) const {
+
+ const Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport, ViewportRect());
+
+ return viewport->rect;
+}
+
+void VisualServerRaster::viewport_set_hide_scenario(RID p_viewport,bool p_hide) {
+
+ VS_CHANGED;
+
+ Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ viewport->hide_scenario=p_hide;
+
+
+}
+
+void VisualServerRaster::viewport_set_hide_canvas(RID p_viewport,bool p_hide) {
+
+ VS_CHANGED;
+
+ Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ viewport->hide_canvas=p_hide;
+
+
+}
+
+void VisualServerRaster::viewport_attach_camera(RID p_viewport,RID p_camera) {
+ VS_CHANGED;
+
+ Viewport *viewport=NULL;
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+
+
+
+ if (p_camera.is_valid()) {
+
+ ERR_FAIL_COND(!camera_owner.owns(p_camera));
+ // a camera
+ viewport->camera=p_camera;
+ } else {
+ viewport->camera=RID();
+ }
+
+}
+
+void VisualServerRaster::viewport_set_scenario(RID p_viewport,RID p_scenario) {
+
+ VS_CHANGED;
+
+ Viewport *viewport=NULL;
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ if (p_scenario.is_valid()) {
+
+ ERR_FAIL_COND(!scenario_owner.owns(p_scenario));
+ // a camera
+ viewport->scenario=p_scenario;
+ } else {
+ viewport->scenario=RID();
+ }
+
+}
+
+RID VisualServerRaster::viewport_get_attached_camera(RID p_viewport) const {
+
+ const Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport, RID());
+
+ return viewport->camera;
+}
+
+void VisualServerRaster::viewport_attach_canvas(RID p_viewport,RID p_canvas) {
+ VS_CHANGED;
+ Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ Canvas *canvas = canvas_owner.get( p_canvas );
+ ERR_FAIL_COND(!canvas);
+
+ ERR_EXPLAIN("Canvas already attached.");
+ ERR_FAIL_COND(viewport->canvas_map.has(p_canvas));
+
+
+ Viewport::CanvasData cd;
+ cd.canvas=canvas;
+ cd.layer=0;
+
+ viewport->canvas_map[p_canvas]=cd;
+ canvas->viewports.insert(p_viewport);
+
+}
+
+
+void VisualServerRaster::viewport_set_canvas_transform(RID p_viewport,RID p_canvas,const Matrix32& p_transform) {
+
+ VS_CHANGED;
+ Viewport *viewport=NULL;
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ Map<RID,Viewport::CanvasData>::Element *E=viewport->canvas_map.find(p_canvas);
+ if (!E) {
+ ERR_EXPLAIN("Viewport does not contain the canvas");
+ ERR_FAIL_COND(!E);
+ }
+
+ E->get().transform=p_transform;
+
+}
+
+Matrix32 VisualServerRaster::viewport_get_canvas_transform(RID p_viewport,RID p_canvas) const {
+
+ Viewport *viewport=NULL;
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport,Matrix32());
+
+ Map<RID,Viewport::CanvasData>::Element *E=viewport->canvas_map.find(p_canvas);
+ if (!E) {
+ ERR_EXPLAIN("Viewport does not contain the canvas");
+ ERR_FAIL_COND_V(!E,Matrix32());
+ }
+
+
+ return E->get().transform;
+}
+
+
+void VisualServerRaster::viewport_set_global_canvas_transform(RID p_viewport,const Matrix32& p_transform) {
+
+ VS_CHANGED
+ Viewport *viewport=NULL;
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ viewport->global_transform=p_transform;
+
+}
+
+Matrix32 VisualServerRaster::viewport_get_global_canvas_transform(RID p_viewport) const{
+
+ Viewport *viewport=NULL;
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport,Matrix32());
+ return viewport->global_transform;
+}
+
+void VisualServerRaster::viewport_remove_canvas(RID p_viewport,RID p_canvas) {
+
+ VS_CHANGED;
+ Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ Canvas *canvas = canvas_owner.get( p_canvas );
+ ERR_FAIL_COND(!canvas);
+
+
+ Map<RID,Viewport::CanvasData>::Element *E=viewport->canvas_map.find(p_canvas);
+ if (!E) {
+ ERR_EXPLAIN("Viewport does not contain the canvas");
+ ERR_FAIL_COND(!E);
+ }
+
+
+ canvas->viewports.erase(p_viewport);
+ viewport->canvas_map.erase(E);
+
+}
+
+
+void VisualServerRaster::viewport_set_canvas_layer(RID p_viewport,RID p_canvas,int p_layer) {
+
+ VS_CHANGED;
+ Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ Map<RID,Viewport::CanvasData>::Element *E=viewport->canvas_map.find(p_canvas);
+ if (!E) {
+ ERR_EXPLAIN("Viewport does not contain the canvas");
+ ERR_FAIL_COND(!E);
+ }
+
+ E->get().layer=p_layer;
+
+}
+
+void VisualServerRaster::viewport_set_transparent_background(RID p_viewport,bool p_enabled) {
+
+ VS_CHANGED;
+ Viewport *viewport=viewport_owner.get( p_viewport );
+ ERR_FAIL_COND(!viewport);
+
+ viewport->transparent_bg=p_enabled;
+}
+
+bool VisualServerRaster::viewport_has_transparent_background(RID p_viewport) const {
+
+ Viewport *viewport=viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport, false);
+
+ return viewport->transparent_bg;
+}
+
+
+RID VisualServerRaster::viewport_get_scenario(RID p_viewport) const {
+
+ const Viewport *viewport=NULL;
+
+ viewport = viewport_owner.get( p_viewport );
+ ERR_FAIL_COND_V(!viewport, RID());
+
+ return viewport->scenario;
+}
+
+
+RID VisualServerRaster::environment_create() {
+
+ return rasterizer->environment_create();
+}
+
+void VisualServerRaster::environment_set_background(RID p_env,EnvironmentBG p_bg){
+
+ rasterizer->environment_set_background(p_env,p_bg);
+}
+VisualServer::EnvironmentBG VisualServerRaster::environment_get_background(RID p_env) const{
+
+ return rasterizer->environment_get_background(p_env);
+}
+
+void VisualServerRaster::environment_set_background_param(RID p_env,EnvironmentBGParam p_param, const Variant& p_value){
+
+
+ rasterizer->environment_set_background_param(p_env,p_param,p_value);
+}
+Variant VisualServerRaster::environment_get_background_param(RID p_env,EnvironmentBGParam p_param) const{
+
+ return rasterizer->environment_get_background_param(p_env,p_param);
+}
+
+void VisualServerRaster::environment_set_enable_fx(RID p_env,EnvironmentFx p_effect,bool p_enabled){
+
+ rasterizer->environment_set_enable_fx(p_env,p_effect,p_enabled);
+}
+bool VisualServerRaster::environment_is_fx_enabled(RID p_env,EnvironmentFx p_effect) const{
+
+ return rasterizer->environment_is_fx_enabled(p_env,p_effect);
+}
+
+
+void VisualServerRaster::environment_fx_set_param(RID p_env,EnvironmentFxParam p_param,const Variant& p_value){
+
+ rasterizer->environment_fx_set_param(p_env,p_param,p_value);
+}
+Variant VisualServerRaster::environment_fx_get_param(RID p_env,EnvironmentFxParam p_param) const {
+
+ return environment_fx_get_param(p_env,p_param);
+}
+
+
+
+/* SCENARIO API */
+
+void VisualServerRaster::_dependency_queue_update(RID p_rid,bool p_update_aabb) {
+
+ Map< RID, Set<RID> >::Element * E = instance_dependency_map.find( p_rid );
+
+ if (!E)
+ return;
+
+
+ Set<RID>::Element *I = E->get().front();
+
+ while(I) {
+
+ Instance *ins = instance_owner.get( I->get() );
+ _instance_queue_update( ins , p_update_aabb );
+
+ I = I->next();
+ }
+
+}
+
+void VisualServerRaster::_instance_queue_update(Instance *p_instance,bool p_update_aabb) {
+
+ if (p_update_aabb)
+ p_instance->update_aabb=true;
+
+ if (p_instance->update)
+ return;
+ p_instance->update_next=instance_update_list;
+ instance_update_list=p_instance;
+ p_instance->update=true;
+
+}
+
+RID VisualServerRaster::scenario_create() {
+
+ Scenario *scenario = memnew( Scenario );
+ ERR_FAIL_COND_V(!scenario,RID());
+ RID scenario_rid = scenario_owner.make_rid( scenario );
+ scenario->self=scenario_rid;
+ scenario->octree.set_pair_callback(instance_pair,this);
+ scenario->octree.set_unpair_callback(instance_unpair,this);
+
+ return scenario_rid;
+}
+
+void VisualServerRaster::scenario_set_debug(RID p_scenario,ScenarioDebugMode p_debug_mode) {
+ VS_CHANGED;
+
+ Scenario *scenario = scenario_owner.get(p_scenario);
+ ERR_FAIL_COND(!scenario);
+ scenario->debug=p_debug_mode;
+}
+
+void VisualServerRaster::scenario_set_environment(RID p_scenario, RID p_environment) {
+
+ VS_CHANGED;
+
+ Scenario *scenario = scenario_owner.get(p_scenario);
+ ERR_FAIL_COND(!scenario);
+ scenario->environment=p_environment;
+
+}
+
+RID VisualServerRaster::scenario_get_environment(RID p_scenario, RID p_environment) const{
+
+ const Scenario *scenario = scenario_owner.get(p_scenario);
+ ERR_FAIL_COND_V(!scenario,RID());
+ return scenario->environment;
+
+}
+
+
+/* INSTANCING API */
+
+
+RID VisualServerRaster::instance_create() {
+
+ Instance *instance = memnew( Instance );
+ ERR_FAIL_COND_V(!instance,RID());
+
+ RID instance_rid = instance_owner.make_rid(instance);
+ instance->self=instance_rid;
+ instance->base_type=INSTANCE_NONE;
+ instance->scenario=NULL;
+
+ return instance_rid;
+}
+
+void VisualServerRaster::instance_set_base(RID p_instance, RID p_base) {
+
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ if (instance->base_type!=INSTANCE_NONE) {
+ //free anything related to that base
+
+ Map< RID, Set<RID> >::Element * E = instance_dependency_map.find( instance->base_rid );
+
+ if (E) {
+ // wtf, no E?
+ E->get().erase( instance->self );
+
+ } else {
+
+ ERR_PRINT("no base E? Bug?");
+ }
+
+ if ( instance->room ) {
+
+ instance_set_room(p_instance,RID());
+ /*
+ if((1<<instance->base_type)&INSTANCE_GEOMETRY_MASK)
+ instance->room->room_info->owned_geometry_instances.erase(instance->RE);
+ else if (instance->base_type==INSTANCE_PORTAL) {
+ print_line("freeing portal, is it there? "+itos(instance->room->room_info->owned_portal_instances.(instance->RE)));
+ instance->room->room_info->owned_portal_instances.erase(instance->RE);
+ } else if (instance->base_type==INSTANCE_ROOM)
+ instance->room->room_info->owned_room_instances.erase(instance->RE);
+ else if (instance->base_type==INSTANCE_LIGHT)
+ instance->room->room_info->owned_light_instances.erase(instance->RE);
+
+ instance->RE=NULL;*/
+ }
+
+
+
+
+
+ if (instance->light_info) {
+
+ if (instance->scenario && instance->light_info->D)
+ instance->scenario->directional_lights.erase( instance->light_info->D );
+ rasterizer->free(instance->light_info->instance);
+ memdelete(instance->light_info);
+ instance->light_info=NULL;
+ }
+
+
+ if (instance->portal_info) {
+
+ _portal_disconnect(instance,true);
+ memdelete(instance->portal_info);
+ instance->portal_info=NULL;
+
+ }
+
+ if (instance->scenario && instance->octree_id) {
+ instance->scenario->octree.erase( instance->octree_id );
+ instance->octree_id=0;
+ }
+
+
+ if (instance->room_info) {
+
+ for(List<Instance*>::Element *E=instance->room_info->owned_geometry_instances.front();E;E=E->next()) {
+
+ Instance *owned = E->get();
+ owned->room=NULL;
+ owned->RE=NULL;
+ }
+ for(List<Instance*>::Element *E=instance->room_info->owned_portal_instances.front();E;E=E->next()) {
+
+ _portal_disconnect(E->get(),true);
+ Instance *owned = E->get();
+ owned->room=NULL;
+ owned->RE=NULL;
+ }
+
+ for(List<Instance*>::Element *E=instance->room_info->owned_room_instances.front();E;E=E->next()) {
+
+ Instance *owned = E->get();
+ owned->room=NULL;
+ owned->RE=NULL;
+ }
+
+ if (instance->room_info->disconnected_child_portals.size()) {
+ ERR_PRINT("BUG: Disconnected portals remain!");
+ }
+ memdelete(instance->room_info);
+ instance->room_info=NULL;
+
+ }
+
+ if (instance->particles_info) {
+
+ rasterizer->free( instance->particles_info->instance );
+ memdelete(instance->particles_info);
+ instance->particles_info=NULL;
+
+ }
+
+ instance->data.morph_values.clear();
+
+ }
+
+
+ instance->base_type=INSTANCE_NONE;
+ instance->base_rid=RID();
+
+
+ if (p_base.is_valid()) {
+
+ if (rasterizer->is_mesh(p_base)) {
+ instance->base_type=INSTANCE_MESH;
+ instance->data.morph_values.resize( rasterizer->mesh_get_morph_target_count(p_base));
+ } else if (rasterizer->is_multimesh(p_base)) {
+ instance->base_type=INSTANCE_MULTIMESH;
+ } else if (rasterizer->is_particles(p_base)) {
+ instance->base_type=INSTANCE_PARTICLES;
+ instance->particles_info=memnew( Instance::ParticlesInfo );
+ instance->particles_info->instance = rasterizer->particles_instance_create( p_base );
+ } else if (rasterizer->is_light(p_base)) {
+
+ instance->base_type=INSTANCE_LIGHT;
+ instance->light_info = memnew( Instance::LightInfo );
+ instance->light_info->instance = rasterizer->light_instance_create(p_base);
+ if (instance->scenario && rasterizer->light_get_type(p_base)==LIGHT_DIRECTIONAL) {
+
+ instance->light_info->D = instance->scenario->directional_lights.push_back(instance->self);
+ }
+
+ } else if (room_owner.owns(p_base)) {
+ instance->base_type=INSTANCE_ROOM;
+ instance->room_info = memnew( Instance::RoomInfo );
+ instance->room_info->room=room_owner.get(p_base);
+ } else if (portal_owner.owns(p_base)) {
+
+ instance->base_type=INSTANCE_PORTAL;
+ instance->portal_info = memnew(Instance::PortalInfo);
+ instance->portal_info->portal=portal_owner.get(p_base);
+ } else {
+ ERR_EXPLAIN("Invalid base RID for instance!")
+ ERR_FAIL();
+ }
+
+ instance_dependency_map[ p_base ].insert( instance->self );
+
+ instance->base_rid=p_base;
+
+ if (instance->scenario)
+ _instance_queue_update(instance,true);
+ }
+
+}
+
+RID VisualServerRaster::instance_get_base(RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, RID() );
+ return instance->base_rid;
+
+}
+
+void VisualServerRaster::instance_set_scenario(RID p_instance, RID p_scenario) {
+
+ VS_CHANGED;
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ if (instance->scenario) {
+
+ Map< RID, Set<RID> >::Element *E = instance_dependency_map.find( instance->scenario->self );
+
+ if (E) {
+ // wtf, no E?
+ E->get().erase( instance->self );
+
+ } else {
+
+ ERR_PRINT("no scenario E? Bug?");
+ }
+
+ if (instance->light_info) {
+
+ if (instance->light_info->D)
+ instance->scenario->directional_lights.erase( instance->light_info->D );
+ }
+
+ if (instance->portal_info) {
+
+ _portal_disconnect(instance,true);
+ }
+
+ if (instance->octree_id) {
+ instance->scenario->octree.erase( instance->octree_id );
+ instance->octree_id=0;
+ }
+
+ instance->scenario=NULL;
+ }
+
+
+ if (p_scenario.is_valid()) {
+ Scenario *scenario = scenario_owner.get( p_scenario );
+ ERR_FAIL_COND(!scenario);
+
+ instance->scenario=scenario;
+
+ instance_dependency_map[ p_scenario ].insert( instance->self );
+ instance->scenario=scenario;
+
+ if (instance->base_type==INSTANCE_LIGHT && rasterizer->light_get_type(instance->base_rid)==LIGHT_DIRECTIONAL) {
+
+ instance->light_info->D = instance->scenario->directional_lights.push_back(instance->self);
+ }
+
+ _instance_queue_update(instance,true);
+ }
+
+}
+RID VisualServerRaster::instance_get_scenario(RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, RID() );
+ if (instance->scenario)
+ return instance->scenario->self;
+ else
+ return RID();
+
+
+}
+
+
+void VisualServerRaster::instance_set_layer_mask(RID p_instance, uint32_t p_mask) {
+
+ VS_CHANGED;
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ instance->layer_mask=p_mask;
+
+}
+
+uint32_t VisualServerRaster::instance_get_layer_mask(RID p_instance) const{
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, 0 );
+
+ return instance->layer_mask;
+}
+
+
+AABB VisualServerRaster::instance_get_base_aabb(RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, AABB() );
+ return instance->aabb;
+
+}
+
+void VisualServerRaster::instance_attach_object_instance_ID(RID p_instance,uint32_t p_ID) {
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ instance->object_ID=p_ID;
+}
+uint32_t VisualServerRaster::instance_get_object_instance_ID(RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, 0 );
+ return instance->object_ID;
+
+}
+
+void VisualServerRaster::instance_attach_skeleton(RID p_instance,RID p_skeleton) {
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+ instance->data.skeleton=p_skeleton;
+
+}
+
+RID VisualServerRaster::instance_get_skeleton(RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, RID() );
+ return instance->data.skeleton;
+
+}
+
+void VisualServerRaster::instance_set_morph_target_weight(RID p_instance,int p_shape, float p_weight) {
+
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+ ERR_FAIL_INDEX( p_shape, instance->data.morph_values.size() );
+ instance->data.morph_values[p_shape]=p_weight;
+}
+
+float VisualServerRaster::instance_get_morph_target_weight(RID p_instance,int p_shape) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, 0 );
+ ERR_FAIL_INDEX_V( p_shape, instance->data.morph_values.size(), 0 );
+ return instance->data.morph_values[p_shape];
+}
+
+void VisualServerRaster::instance_set_transform(RID p_instance, const Transform& p_transform) {
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ if (p_transform==instance->data.transform) // must improve somehow
+ return;
+
+ instance->data.transform=p_transform;
+ if (instance->base_type==INSTANCE_LIGHT)
+ instance->data.transform.orthonormalize();
+ _instance_queue_update(instance);
+
+}
+
+Transform VisualServerRaster::instance_get_transform(RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, Transform() );
+
+ return instance->data.transform;
+
+}
+
+void VisualServerRaster::instance_set_exterior( RID p_instance, bool p_enabled ) {
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ ERR_EXPLAIN("Portals can't be assigned to be exterior");
+
+ ERR_FAIL_COND( instance->base_type == INSTANCE_PORTAL );
+ if (instance->exterior==p_enabled)
+ return;
+ instance->exterior=p_enabled;
+ _instance_queue_update( instance );
+
+
+}
+
+bool VisualServerRaster::instance_is_exterior( RID p_instance) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, false );
+
+ return instance->exterior;
+}
+
+
+void VisualServerRaster::instance_set_room( RID p_instance, RID p_room ) {
+ VS_CHANGED;
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ if (instance->room && instance->RE) {
+
+ //instance already havs a room, remove it from there
+
+ if ( (1<<instance->base_type) & INSTANCE_GEOMETRY_MASK ) {
+
+ instance->room->room_info->owned_geometry_instances.erase(instance->RE);
+
+ if (!p_room.is_valid() && instance->octree_id) {
+ //remove from the octree, so it's re-added with different flags
+ instance->scenario->octree.erase( instance->octree_id );
+ instance->octree_id=0;
+ _instance_queue_update( instance,true );
+ }
+
+
+ } else if ( instance->base_type==INSTANCE_ROOM ) {
+
+ instance->room->room_info->owned_room_instances.erase(instance->RE);
+
+ for(List<Instance*>::Element *E=instance->room_info->owned_portal_instances.front();E;E=E->next()) {
+ _portal_disconnect(E->get());
+ _instance_queue_update( E->get(),false );
+ }
+
+ } else if ( instance->base_type==INSTANCE_PORTAL ) {
+
+ _portal_disconnect(instance,true);
+ bool ss = instance->room->room_info->owned_portal_instances.erase(instance->RE);
+
+ } else if ( instance->base_type==INSTANCE_LIGHT ) {
+
+ instance->room->room_info->owned_light_instances.erase(instance->RE);
+ } else {
+
+ ERR_FAIL();
+
+ }
+
+ instance->RE=NULL;
+ instance->room=NULL;
+
+
+ } else {
+
+ if (p_room.is_valid() && instance->octree_id) {
+ //remove from the octree, so it's re-added with different flags
+ instance->scenario->octree.erase( instance->octree_id );
+ instance->octree_id=0;
+ _instance_queue_update( instance,true );
+ }
+
+ }
+
+ if (!p_room.is_valid())
+ return; // just clearning the room
+
+ Instance *room = instance_owner.get( p_room );
+
+ ERR_FAIL_COND( !room );
+ ERR_FAIL_COND( room->base_type!=INSTANCE_ROOM );
+
+
+ if (instance->base_type==INSTANCE_ROOM) {
+
+ //perform cycle test
+
+ Instance *parent = instance;
+
+ while(parent) {
+
+ ERR_EXPLAIN("Cycle in room assignment");
+ ERR_FAIL_COND( parent == room );
+ parent=parent->room;
+ }
+ }
+
+ if ( (1<<instance->base_type) & INSTANCE_GEOMETRY_MASK ) {
+
+ instance->RE = room->room_info->owned_geometry_instances.push_back(instance);
+ } else if ( instance->base_type==INSTANCE_ROOM ) {
+
+ instance->RE = room->room_info->owned_room_instances.push_back(instance);
+ for(List<Instance*>::Element *E=instance->room_info->owned_portal_instances.front();E;E=E->next())
+ _instance_queue_update( E->get(),false );
+
+
+ } else if ( instance->base_type==INSTANCE_PORTAL ) {
+
+ instance->RE = room->room_info->owned_portal_instances.push_back(instance);
+ } else if ( instance->base_type==INSTANCE_LIGHT ) {
+
+ instance->RE = room->room_info->owned_light_instances.push_back(instance);
+ } else {
+
+ ERR_FAIL();
+
+ }
+
+ instance->room=room;
+}
+
+RID VisualServerRaster::instance_get_room( RID p_instance ) const {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, RID() );
+
+ if (instance->room)
+ return instance->room->self;
+ else
+ return RID();
+}
+
+void VisualServerRaster::instance_set_extra_visibility_margin( RID p_instance, real_t p_margin ) {
+
+ VS_CHANGED;
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ instance->extra_margin=p_margin;
+}
+real_t VisualServerRaster::instance_get_extra_visibility_margin( RID p_instance ) const{
+
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, 0 );
+
+ return instance->extra_margin;
+}
+
+
+Vector<RID> VisualServerRaster::instances_cull_aabb(const AABB& p_aabb, RID p_scenario) const {
+
+
+ Vector<RID> instances;
+ Scenario *scenario=scenario_owner.get(p_scenario);
+ ERR_FAIL_COND_V(!scenario,instances);
+
+ const_cast<VisualServerRaster*>(this)->_update_instances(); // check dirty instances before culling
+
+ int culled=0;
+ Instance *cull[1024];
+ culled=scenario->octree.cull_AABB(p_aabb,cull,1024);
+
+ for (int i=0;i<culled;i++) {
+
+ Instance *instance=cull[i];
+ ERR_CONTINUE(!instance);
+ instances.push_back(instance->self);
+ }
+
+ return instances;
+}
+Vector<RID> VisualServerRaster::instances_cull_ray(const Vector3& p_from, const Vector3& p_to, RID p_scenario) const{
+
+ Vector<RID> instances;
+ Scenario *scenario=scenario_owner.get(p_scenario);
+ ERR_FAIL_COND_V(!scenario,instances);
+ const_cast<VisualServerRaster*>(this)->_update_instances(); // check dirty instances before culling
+
+ int culled=0;
+ Instance *cull[1024];
+ culled=scenario->octree.cull_segment(p_from,p_to*10000,cull,1024);
+
+
+ for (int i=0;i<culled;i++) {
+
+ Instance *instance=cull[i];
+ ERR_CONTINUE(!instance);
+ instances.push_back(instance->self);
+ }
+
+ return instances;
+
+}
+Vector<RID> VisualServerRaster::instances_cull_convex(const Vector<Plane>& p_convex, RID p_scenario) const{
+
+ Vector<RID> instances;
+ Scenario *scenario=scenario_owner.get(p_scenario);
+ ERR_FAIL_COND_V(!scenario,instances);
+ const_cast<VisualServerRaster*>(this)->_update_instances(); // check dirty instances before culling
+
+ int culled=0;
+ Instance *cull[1024];
+
+
+ culled=scenario->octree.cull_convex(p_convex,cull,1024);
+
+ for (int i=0;i<culled;i++) {
+
+ Instance *instance=cull[i];
+ ERR_CONTINUE(!instance);
+ instances.push_back(instance->self);
+ }
+
+ return instances;
+
+}
+
+void VisualServerRaster::instance_geometry_set_flag(RID p_instance,InstanceFlags p_flags,bool p_enabled) {
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+// ERR_FAIL_COND( ! ( (1<<instance->base_type) & INSTANCE_GEOMETRY_MASK) );
+
+ switch(p_flags) {
+
+ case INSTANCE_FLAG_VISIBLE: {
+
+ instance->visible=p_enabled;
+
+ } break;
+ case INSTANCE_FLAG_BILLBOARD: {
+
+ instance->data.billboard=p_enabled;
+
+ } break;
+ case INSTANCE_FLAG_BILLBOARD_FIX_Y: {
+
+ instance->data.billboard_y=p_enabled;
+
+ } break;
+ case INSTANCE_FLAG_CAST_SHADOW: {
+ instance->cast_shadows=p_enabled;
+
+ } break;
+ case INSTANCE_FLAG_RECEIVE_SHADOWS: {
+
+ instance->receive_shadows=p_enabled;
+
+ } break;
+ case INSTANCE_FLAG_DEPH_SCALE: {
+
+ instance->data.depth_scale=p_enabled;
+
+ } break;
+ case INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS: {
+
+ instance->visible_in_all_rooms=p_enabled;
+
+ } break;
+
+ }
+
+}
+
+bool VisualServerRaster::instance_geometry_get_flag(RID p_instance,InstanceFlags p_flags) const{
+
+ const Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, false );
+// ERR_FAIL_COND_V( ! ( (1<<instance->base_type) & INSTANCE_GEOMETRY_MASK), false );
+
+ switch(p_flags) {
+
+ case INSTANCE_FLAG_VISIBLE: {
+
+ return instance->visible;
+
+ } break;
+ case INSTANCE_FLAG_BILLBOARD: {
+
+ return instance->data.billboard;
+
+ } break;
+ case INSTANCE_FLAG_BILLBOARD_FIX_Y: {
+
+ return instance->data.billboard_y;
+
+ } break;
+ case INSTANCE_FLAG_CAST_SHADOW: {
+ return instance->cast_shadows;
+
+ } break;
+ case INSTANCE_FLAG_RECEIVE_SHADOWS: {
+
+ return instance->receive_shadows;
+
+ } break;
+ case INSTANCE_FLAG_DEPH_SCALE: {
+
+ return instance->data.depth_scale;
+
+ } break;
+ case INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS: {
+
+ return instance->visible_in_all_rooms;
+
+ } break;
+
+ }
+
+ return false;
+}
+
+
+void VisualServerRaster::instance_geometry_set_material_override(RID p_instance, RID p_material) {
+
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+ instance->data.material_override=p_material;
+
+}
+
+RID VisualServerRaster::instance_geometry_get_material_override(RID p_instance) const{
+
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance, RID() );
+ return instance->data.material_override;
+
+}
+
+void VisualServerRaster::instance_geometry_set_draw_range(RID p_instance,float p_min,float p_max){
+
+ VS_CHANGED;
+ Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND( !instance );
+
+ instance->draw_range_begin=p_min;
+ instance->draw_range_end=p_max;
+
+}
+
+float VisualServerRaster::instance_geometry_get_draw_range_min(RID p_instance) const{
+
+ const Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance,0 );
+
+ return instance->draw_range_begin;
+
+
+}
+
+float VisualServerRaster::instance_geometry_get_draw_range_max(RID p_instance) const{
+
+ const Instance *instance = instance_owner.get( p_instance );
+ ERR_FAIL_COND_V( !instance,0 );
+
+ return instance->draw_range_end;
+
+
+}
+
+void VisualServerRaster::_update_instance(Instance *p_instance) {
+
+ p_instance->version++;
+
+ if (p_instance->base_type == INSTANCE_LIGHT) {
+
+ rasterizer->light_instance_set_transform( p_instance->light_info->instance, p_instance->data.transform );
+
+ }
+
+ if (p_instance->aabb.has_no_surface())
+ return;
+
+
+ if (p_instance->base_type == INSTANCE_PARTICLES) {
+
+ rasterizer->particles_instance_set_transform( p_instance->particles_info->instance, p_instance->data.transform );
+ }
+
+
+ if (p_instance->base_type&INSTANCE_GEOMETRY_MASK) {
+
+ //make sure lights are updated
+ InstanceSet::Element *E=p_instance->lights.front();
+ while(E) {
+
+ E->get()->version++;
+ E=E->next();
+ }
+
+ }
+
+ if (p_instance->base_type == INSTANCE_ROOM) {
+
+ p_instance->room_info->affine_inverse=p_instance->data.transform.affine_inverse();
+ }
+
+ p_instance->data.mirror = p_instance->data.transform.basis.determinant() < 0.0;
+
+ AABB new_aabb;
+
+ if (p_instance->base_type==INSTANCE_PORTAL) {
+
+ //portals need to be transformed in a special way, so they don't become too wide if they have scale..
+ Transform portal_xform = p_instance->data.transform;
+ portal_xform.basis.set_axis(2,portal_xform.basis.get_axis(2).normalized());
+
+ p_instance->portal_info->plane_cache=Plane( p_instance->data.transform.origin, portal_xform.basis.get_axis(2));
+ int point_count=p_instance->portal_info->portal->shape.size();
+ p_instance->portal_info->transformed_point_cache.resize(point_count);
+
+ AABB portal_aabb;
+
+ for(int i=0;i<point_count;i++) {
+
+ Point2 src = p_instance->portal_info->portal->shape[i];
+ Vector3 point = portal_xform.xform(Vector3(src.x,src.y,0));
+ p_instance->portal_info->transformed_point_cache[i]=point;
+ if (i==0)
+ portal_aabb.pos=point;
+ else
+ portal_aabb.expand_to(point);
+ }
+
+ portal_aabb.grow_by(p_instance->portal_info->portal->connect_range);
+
+ new_aabb = portal_aabb;
+
+ } else {
+
+ new_aabb = p_instance->data.transform.xform(p_instance->aabb);
+ }
+
+
+ for(InstanceSet::Element *E=p_instance->lights.front();E;E=E->next()) {
+ Instance *light = E->get();
+ light->version++;
+ }
+
+
+ p_instance->transformed_aabb=new_aabb;
+
+ if (!p_instance->scenario) {
+
+
+ return;
+ }
+
+
+
+ if (p_instance->octree_id==0) {
+
+ uint32_t base_type = 1<<p_instance->base_type;
+ uint32_t pairable_mask=0;
+ bool pairable=false;
+
+ if (p_instance->base_type == INSTANCE_LIGHT) {
+
+ pairable_mask=INSTANCE_GEOMETRY_MASK;
+ pairable=true;
+ }
+
+ if (p_instance->base_type == INSTANCE_PORTAL) {
+
+ pairable_mask=(1<<INSTANCE_PORTAL);
+ pairable=true;
+ }
+
+ if (!p_instance->room && (1<<p_instance->base_type)&INSTANCE_GEOMETRY_MASK) {
+
+ base_type|=INSTANCE_ROOMLESS_MASK;
+ }
+
+ if (p_instance->base_type == INSTANCE_ROOM) {
+
+ pairable_mask=INSTANCE_ROOMLESS_MASK;
+ pairable=true;
+ }
+
+
+ // not inside octree
+ p_instance->octree_id = p_instance->scenario->octree.create(p_instance,new_aabb,0,pairable,base_type,pairable_mask);
+
+ } else {
+
+ // if (new_aabb==p_instance->data.transformed_aabb)
+ // return;
+
+ p_instance->scenario->octree.move(p_instance->octree_id,new_aabb);
+ }
+
+ if (p_instance->base_type==INSTANCE_PORTAL) {
+
+ _portal_attempt_connect(p_instance);
+ }
+
+ if (!p_instance->room && (1<<p_instance->base_type)&INSTANCE_GEOMETRY_MASK) {
+
+ _instance_validate_autorooms(p_instance);
+ }
+
+ if (p_instance->base_type == INSTANCE_ROOM) {
+
+ for(Set<Instance*>::Element *E=p_instance->room_info->owned_autoroom_geometry.front();E;E=E->next())
+ _instance_validate_autorooms(E->get());
+ }
+
+
+}
+
+void VisualServerRaster::_update_instance_aabb(Instance *p_instance) {
+
+ AABB new_aabb;
+
+ ERR_FAIL_COND(p_instance->base_type!=INSTANCE_NONE && !p_instance->base_rid.is_valid());
+
+ switch(p_instance->base_type) {
+ case VisualServer::INSTANCE_NONE: {
+
+ // do nothing
+ } break;
+ case VisualServer::INSTANCE_MESH: {
+
+ new_aabb = rasterizer->mesh_get_aabb(p_instance->base_rid);
+
+ } break;
+ case VisualServer::INSTANCE_MULTIMESH: {
+
+ new_aabb = rasterizer->multimesh_get_aabb(p_instance->base_rid);
+
+ } break;
+ case VisualServer::INSTANCE_PARTICLES: {
+
+ new_aabb = rasterizer->particles_get_aabb(p_instance->base_rid);
+
+
+ } break;
+ case VisualServer::INSTANCE_LIGHT: {
+
+ new_aabb = rasterizer->light_get_aabb(p_instance->base_rid);
+
+ } break;
+ case VisualServer::INSTANCE_ROOM: {
+
+ Room *room = room_owner.get( p_instance->base_rid );
+ ERR_FAIL_COND(!room);
+ new_aabb=room->bounds.get_aabb();
+
+ } break;
+ case VisualServer::INSTANCE_PORTAL: {
+
+ Portal *portal = portal_owner.get( p_instance->base_rid );
+ ERR_FAIL_COND(!portal);
+ for (int i=0;i<portal->shape.size();i++) {
+
+ Vector3 point( portal->shape[i].x, portal->shape[i].y, 0 );
+ if (i==0) {
+
+ new_aabb.pos=point;
+ new_aabb.size.z=0.01; // make it not flat for octree
+ } else {
+
+ new_aabb.expand_to(point);
+ }
+ }
+
+ } break;
+ default: {}
+ }
+
+ if (p_instance->extra_margin)
+ new_aabb.grow_by(p_instance->extra_margin);
+
+ p_instance->aabb=new_aabb;
+
+}
+
+void VisualServerRaster::_update_instances() {
+
+ while(instance_update_list) {
+
+ Instance *instance=instance_update_list;
+
+ instance_update_list=instance_update_list->update_next;
+
+ if (instance->update_aabb)
+ _update_instance_aabb(instance);
+
+ _update_instance(instance);
+
+ instance->update=false;
+ instance->update_aabb=false;
+ instance->update_next=0;
+ }
+}
+
+/****** CANVAS *********/
+RID VisualServerRaster::canvas_create() {
+
+ Canvas * canvas = memnew( Canvas );
+ ERR_FAIL_COND_V(!canvas,RID());
+ RID rid = canvas_owner.make_rid( canvas );
+
+ return rid;
+}
+
+void VisualServerRaster::canvas_set_item_mirroring(RID p_canvas,RID p_item,const Point2& p_mirroring) {
+
+ Canvas * canvas = canvas_owner.get(p_canvas);
+ ERR_FAIL_COND(!canvas);
+ CanvasItem *canvas_item = canvas_item_owner.get(p_item);
+ ERR_FAIL_COND(!canvas_item);
+
+ int idx = canvas->find_item(canvas_item);
+ ERR_FAIL_COND(idx==-1);
+ canvas->child_items[idx].mirror=p_mirroring;
+
+}
+
+Point2 VisualServerRaster::canvas_get_item_mirroring(RID p_canvas,RID p_item) const {
+
+ Canvas * canvas = canvas_owner.get(p_canvas);
+ ERR_FAIL_COND_V(!canvas,Point2());
+ CanvasItem *canvas_item = memnew( CanvasItem );
+ ERR_FAIL_COND_V(!canvas_item,Point2());
+
+ int idx = canvas->find_item(canvas_item);
+ ERR_FAIL_COND_V(idx==-1,Point2());
+ return canvas->child_items[idx].mirror;
+}
+
+
+RID VisualServerRaster::canvas_item_create() {
+
+ CanvasItem *canvas_item = memnew( CanvasItem );
+ ERR_FAIL_COND_V(!canvas_item,RID());
+
+ return canvas_item_owner.make_rid( canvas_item );
+}
+
+void VisualServerRaster::canvas_item_set_parent(RID p_item,RID p_parent) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ if (canvas_item->parent.is_valid()) {
+
+ if (canvas_owner.owns(canvas_item->parent)) {
+
+ Canvas *canvas = canvas_owner.get(canvas_item->parent);
+ canvas->erase_item(canvas_item);
+ } else if (canvas_item_owner.owns(canvas_item->parent)) {
+
+ CanvasItem *item_owner = canvas_item_owner.get(canvas_item->parent);
+ item_owner->child_items.erase(canvas_item);
+ }
+
+ canvas_item->parent=RID();
+ }
+
+
+ if (p_parent.is_valid()) {
+ if (canvas_owner.owns(p_parent)) {
+
+ Canvas *canvas = canvas_owner.get(p_parent);
+ Canvas::ChildItem ci;
+ ci.item=canvas_item;
+ canvas->child_items.push_back(ci);
+ } else if (canvas_item_owner.owns(p_parent)) {
+
+ CanvasItem *item_owner = canvas_item_owner.get(p_parent);
+ item_owner->child_items.push_back(canvas_item);
+
+ } else {
+
+ ERR_EXPLAIN("Invalid parent");
+ ERR_FAIL();
+ }
+
+
+ }
+
+ canvas_item->parent=p_parent;
+
+
+}
+
+RID VisualServerRaster::canvas_item_get_parent(RID p_canvas_item) const {
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_canvas_item );
+ ERR_FAIL_COND_V(!canvas_item,RID());
+
+ return canvas_item->parent;
+}
+
+void VisualServerRaster::canvas_item_set_visible(RID p_item,bool p_visible) {
+
+ VS_CHANGED;
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ canvas_item->visible=p_visible;
+}
+
+
+bool VisualServerRaster::canvas_item_is_visible(RID p_item) const {
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND_V(!canvas_item,RID());
+
+ return canvas_item->visible;
+
+}
+
+void VisualServerRaster::canvas_item_set_blend_mode(RID p_canvas_item,MaterialBlendMode p_blend) {
+
+ VS_CHANGED;
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_canvas_item );
+ if (!canvas_item) {
+ printf("!canvas_item\n");
+ };
+ ERR_FAIL_COND(!canvas_item);
+
+ if (canvas_item->blend_mode==p_blend)
+ return;
+ VS_CHANGED;
+
+ canvas_item->blend_mode=p_blend;
+
+}
+
+void VisualServerRaster::canvas_item_attach_viewport(RID p_canvas_item, RID p_viewport) {
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_canvas_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ VS_CHANGED;
+
+ canvas_item->viewport=p_viewport;
+
+}
+
+
+/*
+void VisualServerRaster::canvas_item_set_rect(RID p_item, const Rect2& p_rect) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ canvas_item->rect=p_rect;
+}*/
+
+void VisualServerRaster::canvas_item_set_clip(RID p_item, bool p_clip) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ canvas_item->clip=p_clip;
+}
+
+const Rect2& VisualServerRaster::CanvasItem::get_rect() const {
+
+ if (custom_rect || !rect_dirty)
+ return rect;
+
+ //must update rect
+ int s=commands.size();
+ if (s==0) {
+
+ rect=Rect2();
+ rect_dirty=false;
+ return rect;
+ }
+
+ Matrix32 xf;
+ bool found_xform=false;
+ bool first=true;
+
+ const CanvasItem::Command * const *cmd = &commands[0];
+
+
+ for (int i=0;i<s;i++) {
+
+ const CanvasItem::Command *c=cmd[i];
+ Rect2 r;
+
+ switch(c->type) {
+ case CanvasItem::Command::TYPE_LINE: {
+
+ const CanvasItem::CommandLine* line = static_cast< const CanvasItem::CommandLine*>(c);
+ r.pos=line->from;
+ r.expand_to(line->to);
+ } break;
+ case CanvasItem::Command::TYPE_RECT: {
+
+ const CanvasItem::CommandRect* crect = static_cast< const CanvasItem::CommandRect*>(c);
+ r=crect->rect;
+
+ } break;
+ case CanvasItem::Command::TYPE_STYLE: {
+
+ const CanvasItem::CommandStyle* style = static_cast< const CanvasItem::CommandStyle*>(c);
+ r=style->rect;
+ } break;
+ case CanvasItem::Command::TYPE_PRIMITIVE: {
+
+ const CanvasItem::CommandPrimitive* primitive = static_cast< const CanvasItem::CommandPrimitive*>(c);
+ r.pos=primitive->points[0];
+ for(int i=1;i<primitive->points.size();i++) {
+
+ r.expand_to(primitive->points[i]);
+
+ }
+ } break;
+ case CanvasItem::Command::TYPE_POLYGON: {
+
+ const CanvasItem::CommandPolygon* polygon = static_cast< const CanvasItem::CommandPolygon*>(c);
+ int l = polygon->points.size();
+ const Point2*pp=&polygon->points[0];
+ r.pos=pp[0];
+ for(int i=1;i<l;i++) {
+
+ r.expand_to(pp[i]);
+
+ }
+ } break;
+
+ case CanvasItem::Command::TYPE_POLYGON_PTR: {
+
+ const CanvasItem::CommandPolygonPtr* polygon = static_cast< const CanvasItem::CommandPolygonPtr*>(c);
+ int l = polygon->count;
+ if (polygon->indices != NULL) {
+
+ r.pos=polygon->points[polygon->indices[0]];
+ for (int i=1; i<polygon->count; i++) {
+
+ r.expand_to(polygon->points[polygon->indices[i]]);
+ };
+ } else {
+ r.pos=polygon->points[0];
+ for (int i=1; i<polygon->count; i++) {
+
+ r.expand_to(polygon->points[i]);
+ };
+ };
+ } break;
+ case CanvasItem::Command::TYPE_CIRCLE: {
+
+ const CanvasItem::CommandCircle* circle = static_cast< const CanvasItem::CommandCircle*>(c);
+ r.pos=Point2(-circle->radius,-circle->radius)+circle->pos;
+ r.size=Point2(circle->radius*2.0,circle->radius*2.0);
+ } break;
+ case CanvasItem::Command::TYPE_TRANSFORM: {
+
+ const CanvasItem::CommandTransform* transform = static_cast<const CanvasItem::CommandTransform*>(c);
+ xf=transform->xform;
+ found_xform=true;
+ continue;
+ } break;
+ case CanvasItem::Command::TYPE_BLEND_MODE: {
+
+ } break;
+ case CanvasItem::Command::TYPE_CLIP_IGNORE: {
+
+ } break;
+ }
+
+ if (found_xform) {
+ r = xf.xform(r);
+ found_xform=false;
+ }
+
+
+ if (first) {
+ rect=r;
+ first=false;
+ } else
+ rect=rect.merge(r);
+ }
+
+ rect_dirty=false;
+ return rect;
+}
+
+void VisualServerRaster::canvas_item_set_transform(RID p_item, const Matrix32& p_transform) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ canvas_item->xform=p_transform;
+
+}
+
+
+void VisualServerRaster::canvas_item_set_custom_rect(RID p_item, bool p_custom_rect,const Rect2& p_rect) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ canvas_item->custom_rect=p_custom_rect;
+ if (p_custom_rect)
+ canvas_item->rect=p_rect;
+
+}
+
+void VisualServerRaster::canvas_item_set_opacity(RID p_item, float p_opacity) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+ canvas_item->opacity=p_opacity;
+
+}
+float VisualServerRaster::canvas_item_get_opacity(RID p_item, float p_opacity) const {
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND_V(!canvas_item,-1);
+ return canvas_item->opacity;
+
+}
+
+void VisualServerRaster::canvas_item_set_on_top(RID p_item, bool p_on_top) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+ canvas_item->ontop=p_on_top;
+
+}
+
+bool VisualServerRaster::canvas_item_is_on_top(RID p_item) const{
+ const CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND_V(!canvas_item,false);
+ return canvas_item->ontop;
+
+}
+
+
+void VisualServerRaster::canvas_item_set_self_opacity(RID p_item, float p_self_opacity) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+ canvas_item->self_opacity=p_self_opacity;
+
+}
+float VisualServerRaster::canvas_item_get_self_opacity(RID p_item, float p_self_opacity) const {
+
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND_V(!canvas_item,-1);
+ return canvas_item->self_opacity;
+
+}
+
+
+void VisualServerRaster::canvas_item_add_line(RID p_item, const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandLine * line = memnew( CanvasItem::CommandLine );
+ ERR_FAIL_COND(!line);
+ line->color=p_color;
+ line->from=p_from;
+ line->to=p_to;
+ line->width=p_width;
+ canvas_item->rect_dirty=true;
+
+
+ canvas_item->commands.push_back(line);
+}
+
+void VisualServerRaster::canvas_item_add_rect(RID p_item, const Rect2& p_rect, const Color& p_color) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandRect * rect = memnew( CanvasItem::CommandRect );
+ ERR_FAIL_COND(!rect);
+ rect->modulate=p_color;
+ rect->rect=p_rect;
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(rect);
+}
+
+void VisualServerRaster::canvas_item_add_circle(RID p_item, const Point2& p_pos, float p_radius,const Color& p_color) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandCircle * circle = memnew( CanvasItem::CommandCircle );
+ ERR_FAIL_COND(!circle);
+ circle->color=p_color;
+ circle->pos=p_pos;
+ circle->radius=p_radius;
+
+ canvas_item->commands.push_back(circle);
+
+}
+
+void VisualServerRaster::canvas_item_add_texture_rect(RID p_item, const Rect2& p_rect, RID p_texture,bool p_tile,const Color& p_modulate) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandRect * rect = memnew( CanvasItem::CommandRect );
+ ERR_FAIL_COND(!rect);
+ rect->modulate=p_modulate;
+ rect->rect=p_rect;
+ rect->flags=0;
+ if (p_tile)
+ rect->flags|=Rasterizer::CANVAS_RECT_TILE;
+
+ if (p_rect.size.x<0) {
+
+ rect->flags|=Rasterizer::CANVAS_RECT_FLIP_H;
+ rect->rect.size.x = -rect->rect.size.x;
+ }
+ if (p_rect.size.y<0) {
+
+ rect->flags|=Rasterizer::CANVAS_RECT_FLIP_V;
+ rect->rect.size.y = -rect->rect.size.y;
+ }
+ rect->texture=p_texture;
+ canvas_item->rect_dirty=true;
+ canvas_item->commands.push_back(rect);
+}
+
+void VisualServerRaster::canvas_item_add_texture_rect_region(RID p_item, const Rect2& p_rect, RID p_texture,const Rect2& p_src_rect,const Color& p_modulate) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandRect * rect = memnew( CanvasItem::CommandRect );
+ ERR_FAIL_COND(!rect);
+ rect->modulate=p_modulate;
+ rect->rect=p_rect;
+ rect->texture=p_texture;
+ rect->source=p_src_rect;
+ rect->flags=Rasterizer::CANVAS_RECT_REGION;
+
+ if (p_rect.size.x<0) {
+
+ rect->flags|=Rasterizer::CANVAS_RECT_FLIP_H;
+ rect->rect.size.x = -rect->rect.size.x;
+ }
+ if (p_rect.size.y<0) {
+
+ rect->flags|=Rasterizer::CANVAS_RECT_FLIP_V;
+ rect->rect.size.y = -rect->rect.size.y;
+ }
+
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(rect);
+
+}
+void VisualServerRaster::canvas_item_add_style_box(RID p_item, const Rect2& p_rect, RID p_texture,const Vector2& p_topleft, const Vector2& p_bottomright, bool p_draw_center,const Color& p_modulate) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandStyle * style = memnew( CanvasItem::CommandStyle );
+ ERR_FAIL_COND(!style);
+ style->texture=p_texture;
+ style->rect=p_rect;
+ style->draw_center=p_draw_center;
+ style->color=p_modulate;
+ style->margin[MARGIN_LEFT]=p_topleft.x;
+ style->margin[MARGIN_TOP]=p_topleft.y;
+ style->margin[MARGIN_RIGHT]=p_bottomright.x;
+ style->margin[MARGIN_BOTTOM]=p_bottomright.y;
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(style);
+}
+void VisualServerRaster::canvas_item_add_primitive(RID p_item,const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandPrimitive * prim = memnew( CanvasItem::CommandPrimitive );
+ ERR_FAIL_COND(!prim);
+ prim->texture=p_texture;
+ prim->points=p_points;
+ prim->uvs=p_uvs;
+ prim->colors=p_colors;
+ prim->width=p_width;
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(prim);
+}
+
+void VisualServerRaster::canvas_item_add_polygon(RID p_item, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+#ifdef DEBUG_ENABLED
+ int pointcount = p_points.size();
+ ERR_FAIL_COND(pointcount<3);
+ int color_size=p_colors.size();
+ int uv_size=p_uvs.size();
+ ERR_FAIL_COND(color_size!=0 && color_size!=1 && color_size!=pointcount);
+ ERR_FAIL_COND(uv_size!=0 && (uv_size!=pointcount || !p_texture.is_valid()));
+#endif
+ Vector<int> indices = Geometry::triangulate_polygon(p_points);
+
+ if (indices.empty()) {
+
+ ERR_EXPLAIN("Bad Polygon!");
+ ERR_FAIL_V();
+ }
+
+ CanvasItem::CommandPolygon * polygon = memnew( CanvasItem::CommandPolygon );
+ ERR_FAIL_COND(!polygon);
+ polygon->texture=p_texture;
+ polygon->points=p_points;
+ polygon->uvs=p_uvs;
+ polygon->colors=p_colors;
+ polygon->indices=indices;
+ polygon->count=indices.size();
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(polygon);
+
+}
+
+void VisualServerRaster::canvas_item_add_triangle_array_ptr(RID p_item, int p_count, const int* p_indices, const Point2* p_points, const Color* p_colors,const Point2* p_uvs, RID p_texture) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ ERR_FAIL_COND(p_count <= 0);
+
+ ERR_FAIL_COND(p_points == NULL);
+
+ CanvasItem::CommandPolygonPtr * polygon = memnew( CanvasItem::CommandPolygonPtr );
+ ERR_FAIL_COND(!polygon);
+ polygon->texture=p_texture;
+ polygon->points=p_points;
+ polygon->uvs=p_uvs;
+ polygon->colors=p_colors;
+ polygon->indices=p_indices;
+ polygon->count = p_count * 3;
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(polygon);
+};
+
+void VisualServerRaster::canvas_item_add_triangle_array(RID p_item, const Vector<int>& p_indices, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture, int p_count) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ int ps = p_points.size();
+ ERR_FAIL_COND(!p_colors.empty() && p_colors.size()!=ps);
+ ERR_FAIL_COND(!p_uvs.empty() && p_uvs.size()!=ps);
+
+ Vector<int> indices = p_indices;
+
+ int count = p_count * 3;
+
+ if (indices.empty()) {
+
+ ERR_FAIL_COND( ps % 3 != 0 );
+ if (p_count == -1)
+ count = ps;
+ } else {
+
+ ERR_FAIL_COND( indices.size() % 3 != 0 );
+ if (p_count == -1)
+ count = indices.size();
+ }
+
+ CanvasItem::CommandPolygon * polygon = memnew( CanvasItem::CommandPolygon );
+ ERR_FAIL_COND(!polygon);
+ polygon->texture=p_texture;
+ polygon->points=p_points;
+ polygon->uvs=p_uvs;
+ polygon->colors=p_colors;
+ polygon->indices=indices;
+ polygon->count = count;
+ canvas_item->rect_dirty=true;
+
+ canvas_item->commands.push_back(polygon);
+}
+
+
+void VisualServerRaster::canvas_item_add_set_transform(RID p_item,const Matrix32& p_transform) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandTransform * tr = memnew( CanvasItem::CommandTransform );
+ ERR_FAIL_COND(!tr);
+ tr->xform=p_transform;
+
+ canvas_item->commands.push_back(tr);
+
+}
+
+void VisualServerRaster::canvas_item_add_set_blend_mode(RID p_item, MaterialBlendMode p_blend) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandBlendMode * bm = memnew( CanvasItem::CommandBlendMode );
+ ERR_FAIL_COND(!bm);
+ bm->blend_mode = p_blend;
+
+ canvas_item->commands.push_back(bm);
+};
+
+void VisualServerRaster::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) {
+
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ CanvasItem::CommandClipIgnore * ci = memnew( CanvasItem::CommandClipIgnore);
+ ERR_FAIL_COND(!ci);
+ ci->ignore=p_ignore;
+
+ canvas_item->commands.push_back(ci);
+
+}
+
+void VisualServerRaster::canvas_item_clear(RID p_item) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+
+ canvas_item->clear();
+
+}
+
+void VisualServerRaster::canvas_item_raise(RID p_item) {
+ VS_CHANGED;
+ CanvasItem *canvas_item = canvas_item_owner.get( p_item );
+ ERR_FAIL_COND(!canvas_item);
+
+ if (canvas_item->parent.is_valid()) {
+
+ if (canvas_owner.owns(canvas_item->parent)) {
+
+ Canvas *canvas = canvas_owner.get(canvas_item->parent);
+ int idx = canvas->find_item(canvas_item);
+ ERR_FAIL_COND(idx<0);
+ Canvas::ChildItem ci = canvas->child_items[idx];
+ canvas->child_items.remove(idx);
+ canvas->child_items.push_back(ci);
+
+ } else if (canvas_item_owner.owns(canvas_item->parent)) {
+
+ CanvasItem *item_owner = canvas_item_owner.get(canvas_item->parent);
+ int idx = item_owner->child_items.find(canvas_item);
+ ERR_FAIL_COND(idx<0);
+ item_owner->child_items.remove(idx);
+ item_owner->child_items.push_back(canvas_item);
+
+ }
+ }
+
+}
+
+/******** CANVAS *********/
+
+
+void VisualServerRaster::cursor_set_rotation(float p_rotation, int p_cursor) {
+ VS_CHANGED;
+ ERR_FAIL_INDEX(p_cursor, MAX_CURSORS);
+
+ cursors[p_cursor].rot = p_rotation;
+};
+
+void VisualServerRaster::cursor_set_texture(RID p_texture, const Point2 &p_center_offset, int p_cursor) {
+ VS_CHANGED;
+ ERR_FAIL_INDEX(p_cursor, MAX_CURSORS);
+
+ cursors[p_cursor].texture = p_texture;
+ cursors[p_cursor].center = p_center_offset;
+};
+
+void VisualServerRaster::cursor_set_visible(bool p_visible, int p_cursor) {
+ VS_CHANGED;
+ ERR_FAIL_INDEX(p_cursor, MAX_CURSORS);
+
+ cursors[p_cursor].visible = p_visible;
+};
+
+void VisualServerRaster::cursor_set_pos(const Point2& p_pos, int p_cursor) {
+
+ ERR_FAIL_INDEX(p_cursor, MAX_CURSORS);
+ if (cursors[p_cursor].pos==p_pos)
+ return;
+ VS_CHANGED;
+ cursors[p_cursor].pos = p_pos;
+};
+
+
+void VisualServerRaster::black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom) {
+
+ black_margin[MARGIN_LEFT]=p_left;
+ black_margin[MARGIN_TOP]=p_top;
+ black_margin[MARGIN_RIGHT]=p_right;
+ black_margin[MARGIN_BOTTOM]=p_bottom;
+}
+
+
+void VisualServerRaster::_free_attached_instances(RID p_rid,bool p_free_scenario) {
+
+ Map< RID, Set<RID> >::Element * E = instance_dependency_map.find( p_rid );
+
+ if (E) {
+ // has instances
+ while( E->get().size() ) {
+ // erase all attached instances
+ if (p_free_scenario)
+ instance_set_scenario( E->get().front()->get(), RID() );
+ else
+ instance_set_base( E->get().front()->get(), RID() );
+
+ }
+ }
+
+ instance_dependency_map.erase(p_rid);
+
+}
+
+void VisualServerRaster::custom_shade_model_set_shader(int p_model, RID p_shader) {
+
+ VS_CHANGED;
+// rasterizer->custom_shade_model_set_shader(p_model,p_shader);
+}
+
+RID VisualServerRaster::custom_shade_model_get_shader(int p_model) const {
+
+ //return rasterizer->custom_shade_model_get_shader(p_model);
+ return RID();
+
+}
+void VisualServerRaster::custom_shade_model_set_name(int p_model, const String& p_name) {
+
+ //rasterizer->custom_shade_model_set_name(p_model,p_name);
+
+}
+String VisualServerRaster::custom_shade_model_get_name(int p_model) const {
+
+ //return rasterizer->custom_shade_model_get_name(p_model);
+ return "";
+}
+void VisualServerRaster::custom_shade_model_set_param_info(int p_model, const List<PropertyInfo>& p_info) {
+
+ VS_CHANGED;
+ //rasterizer->custom_shade_model_set_param_info(p_model,p_info);
+}
+void VisualServerRaster::custom_shade_model_get_param_info(int p_model, List<PropertyInfo>* p_info) const {
+
+ //rasterizer->custom_shade_model_get_param_info(p_model,p_info);
+}
+
+void VisualServerRaster::free( RID p_rid ) {
+
+ VS_CHANGED;
+
+ if (rasterizer->is_texture(p_rid) || rasterizer->is_material(p_rid) || rasterizer->is_skeleton(p_rid) || rasterizer->is_shader(p_rid)) {
+
+ rasterizer->free(p_rid);
+
+ } else if (rasterizer->is_mesh(p_rid) || rasterizer->is_multimesh(p_rid) || rasterizer->is_light(p_rid) || rasterizer->is_particles(p_rid) ) {
+ //delete the resource
+
+ _free_attached_instances(p_rid);
+ rasterizer->free(p_rid);
+ } else if (room_owner.owns(p_rid)) {
+
+ _free_attached_instances(p_rid);
+ Room *room = room_owner.get(p_rid);
+ ERR_FAIL_COND(!room);
+ room_owner.free(p_rid);
+ memdelete(room);
+
+
+ } else if (portal_owner.owns(p_rid)) {
+
+ _free_attached_instances(p_rid);
+
+ Portal *portal = portal_owner.get(p_rid);
+ ERR_FAIL_COND(!portal);
+ portal_owner.free(p_rid);
+ memdelete(portal);
+
+ } else if (camera_owner.owns(p_rid)) {
+ // delete te camera
+
+ Camera *camera = camera_owner.get(p_rid);
+ ERR_FAIL_COND(!camera);
+
+ camera_owner.free( p_rid );
+ memdelete(camera);
+
+ } else if (viewport_owner.owns(p_rid)) {
+ // delete the viewport
+
+ Viewport *viewport = viewport_owner.get( p_rid );
+ ERR_FAIL_COND(!viewport);
+
+// Viewport *parent=NULL;
+
+ rasterizer->free(viewport->viewport_data);
+ if (viewport->render_target.is_valid()) {
+
+ rasterizer->free(viewport->render_target);
+ }
+
+ if (viewport->update_list.in_list())
+ viewport_update_list.remove(&viewport->update_list);
+ if (screen_viewports.has(p_rid))
+ screen_viewports.erase(p_rid);
+
+ while(viewport->canvas_map.size()) {
+
+ Canvas *c = viewport->canvas_map.front()->get().canvas;
+ c->viewports.erase(p_rid);
+
+ viewport->canvas_map.erase(viewport->canvas_map.front());
+ }
+
+
+ viewport_owner.free(p_rid);
+ memdelete(viewport);
+
+ } else if (instance_owner.owns(p_rid)) {
+ // delete the instance
+
+ _update_instances(); // be sure
+
+ Instance *instance = instance_owner.get(p_rid);
+ ERR_FAIL_COND(!instance);
+
+ instance_set_room(p_rid,RID());
+ instance_set_scenario(p_rid,RID());
+ instance_set_base(p_rid,RID());
+
+ instance_owner.free(p_rid);
+ memdelete(instance);
+
+ } else if (canvas_owner.owns(p_rid)) {
+
+ Canvas *canvas = canvas_owner.get(p_rid);
+ ERR_FAIL_COND(!canvas);
+
+ while(canvas->viewports.size()) {
+
+ Viewport *vp = viewport_owner.get(canvas->viewports.front()->get());
+ ERR_FAIL_COND(!vp);
+
+ Map<RID,Viewport::CanvasData>::Element *E=vp->canvas_map.find(p_rid);
+ ERR_FAIL_COND(!E);
+ vp->canvas_map.erase(p_rid);
+
+ canvas->viewports.erase( canvas->viewports.front() );
+ }
+
+ for (int i=0;i<canvas->child_items.size();i++) {
+
+ canvas->child_items[i].item->parent=RID();
+ }
+
+ canvas_owner.free( p_rid );
+
+ memdelete( canvas );
+
+ } else if (canvas_item_owner.owns(p_rid)) {
+
+ CanvasItem *canvas_item = canvas_item_owner.get(p_rid);
+ ERR_FAIL_COND(!canvas_item);
+
+ if (canvas_item->parent.is_valid()) {
+
+ if (canvas_owner.owns(canvas_item->parent)) {
+
+ Canvas *canvas = canvas_owner.get(canvas_item->parent);
+ canvas->erase_item(canvas_item);
+ } else if (canvas_item_owner.owns(canvas_item->parent)) {
+
+ CanvasItem *item_owner = canvas_item_owner.get(canvas_item->parent);
+ item_owner->child_items.erase(canvas_item);
+
+ }
+ }
+
+ for (int i=0;i<canvas_item->child_items.size();i++) {
+
+ canvas_item->child_items[i]->parent=RID();
+ }
+
+ canvas_item_owner.free( p_rid );
+
+ memdelete( canvas_item );
+ } else if (scenario_owner.owns(p_rid)) {
+
+ Scenario *scenario=scenario_owner.get(p_rid);
+ ERR_FAIL_COND(!scenario);
+
+ _update_instances(); // be sure
+ _free_attached_instances(p_rid,true);
+
+ //rasterizer->free( scenario->environment );
+ scenario_owner.free(p_rid);
+ memdelete(scenario);
+
+ } else {
+
+ ERR_FAIL();
+ }
+
+}
+
+
+
+void VisualServerRaster::_instance_draw(Instance *p_instance) {
+
+ if (p_instance->light_cache_dirty) {
+ int l=0;
+ //add positional lights
+ InstanceSet::Element *LE=p_instance->lights.front();
+ p_instance->data.light_instances.resize(p_instance->lights.size());
+ while(LE) {
+
+ p_instance->data.light_instances[l++]=LE->get()->light_info->instance;
+ LE=LE->next();
+ }
+ p_instance->light_cache_dirty=false;
+ }
+
+
+ switch(p_instance->base_type) {
+
+ case INSTANCE_MESH: {
+ const float *morphs = NULL;
+ if (!p_instance->data.morph_values.empty()) {
+ morphs=&p_instance->data.morph_values[0];
+ }
+
+ rasterizer->add_mesh(p_instance->base_rid, &p_instance->data);
+ } break;
+ case INSTANCE_MULTIMESH: {
+ rasterizer->add_multimesh(p_instance->base_rid, &p_instance->data);
+ } break;
+ case INSTANCE_PARTICLES: {
+ rasterizer->add_particles(p_instance->particles_info->instance, &p_instance->data);
+ } break;
+ default: {};
+ }
+}
+
+
+Vector<Vector3> VisualServerRaster::_camera_generate_endpoints(Instance *p_light,Camera *p_camera,float p_range_min, float p_range_max) {
+
+ // setup a camera matrix for that range!
+ CameraMatrix camera_matrix;
+
+ switch(p_camera->type) {
+
+ case Camera::ORTHOGONAL: {
+
+ camera_matrix.set_orthogonal(p_camera->size,viewport_rect.width / (float)viewport_rect.height,p_range_min,p_range_max,p_camera->vaspect);
+ } break;
+ case Camera::PERSPECTIVE: {
+
+ camera_matrix.set_perspective(
+ p_camera->fov,
+ viewport_rect.width / (float)viewport_rect.height,
+ p_range_min,
+ p_range_max,
+ p_camera->vaspect
+ );
+
+ } break;
+ }
+
+ //obtain the frustum endpoints
+
+ Vector<Vector3> endpoints;
+ endpoints.resize(8);
+ bool res = camera_matrix.get_endpoints(p_camera->transform,&endpoints[0]);
+ ERR_FAIL_COND_V(!res,Vector<Vector3>());
+
+ return endpoints;
+}
+
+Vector<Plane> VisualServerRaster::_camera_generate_orthogonal_planes(Instance *p_light,Camera *p_camera,float p_range_min, float p_range_max) {
+
+ Vector<Vector3> endpoints=_camera_generate_endpoints(p_light,p_camera,p_range_min,p_range_max); // frustum plane endpoints
+ ERR_FAIL_COND_V(endpoints.empty(),Vector<Plane>());
+
+ // obtain the light frustm ranges (given endpoints)
+
+ Vector3 x_vec=p_light->data.transform.basis.get_axis( Vector3::AXIS_X ).normalized();
+ Vector3 y_vec=p_light->data.transform.basis.get_axis( Vector3::AXIS_Y ).normalized();
+ Vector3 z_vec=p_light->data.transform.basis.get_axis( Vector3::AXIS_Z ).normalized();
+
+ float x_min,x_max;
+ float y_min,y_max;
+ float z_min,z_max;
+
+ for(int j=0;j<8;j++) {
+
+ float d_x=x_vec.dot(endpoints[j]);
+ float d_y=y_vec.dot(endpoints[j]);
+ float d_z=z_vec.dot(endpoints[j]);
+
+ if (j==0 || d_x<x_min)
+ x_min=d_x;
+ if (j==0 || d_x>x_max)
+ x_max=d_x;
+
+ if (j==0 || d_y<y_min)
+ y_min=d_y;
+ if (j==0 || d_y>y_max)
+ y_max=d_y;
+
+ if (j==0 || d_z<z_min)
+ z_min=d_z;
+ if (j==0 || d_z>z_max)
+ z_max=d_z;
+
+
+ }
+ //now that we now all ranges, we can proceed to make the light frustum planes, for culling octree
+
+ Vector<Plane> light_frustum_planes;
+ light_frustum_planes.resize(6);
+
+ //right/left
+ light_frustum_planes[0]=Plane( x_vec, x_max );
+ light_frustum_planes[1]=Plane( -x_vec, -x_min );
+ //top/bottom
+ light_frustum_planes[2]=Plane( y_vec, y_max );
+ light_frustum_planes[3]=Plane( -y_vec, -y_min );
+ //near/far
+ light_frustum_planes[4]=Plane( z_vec, z_max+1e6 );
+ light_frustum_planes[5]=Plane( -z_vec, -z_min ); // z_min is ok, since casters further than far-light plane are not needed
+
+ //TODO@ add more actual frustum planes to minimize get
+
+ return light_frustum_planes;
+
+}
+void VisualServerRaster::_light_instance_update_pssm_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range) {
+
+ int splits = rasterizer->light_instance_get_shadow_passes( p_light->light_info->instance );
+
+ float split_weight=rasterizer->light_directional_get_shadow_param(p_light->base_rid,LIGHT_DIRECTIONAL_SHADOW_PARAM_PSSM_SPLIT_WEIGHT);
+
+
+ float distances[5];
+ float texsize=rasterizer->light_instance_get_shadow_size( p_light->light_info->instance );
+
+// float cull_min=p_cull_range.min;
+ //float cull_max=p_cull_range.max;
+
+
+ float cull_min=p_camera->znear;
+ float cull_max=p_camera->zfar;
+ float max_dist = rasterizer->light_directional_get_shadow_param(p_light->base_rid,VS::LIGHT_DIRECTIONAL_SHADOW_PARAM_MAX_DISTANCE);
+ if (max_dist>0.0)
+ cull_max=MIN(cull_max,max_dist);
+
+ for(int i = 0; i < splits; i++) {
+ float idm = i / (float)splits;
+ float lg = cull_min * Math::pow(cull_max/cull_min, idm);
+ float uniform = cull_min + (cull_max - cull_min) * idm;
+ distances[i] = lg * split_weight + uniform * (1.0 - split_weight);
+
+ }
+
+ distances[0]=cull_min;
+ distances[splits]=cull_max;
+
+ for (int i=0;i<splits;i++) {
+
+ // setup a camera matrix for that range!
+ CameraMatrix camera_matrix;
+
+ switch(p_camera->type) {
+
+ case Camera::ORTHOGONAL: {
+
+ camera_matrix.set_orthogonal(
+ p_camera->size,
+ viewport_rect.width / (float)viewport_rect.height,
+ distances[i],
+ distances[i+1],
+ p_camera->vaspect
+
+ );
+ } break;
+ case Camera::PERSPECTIVE: {
+
+
+ camera_matrix.set_perspective(
+ p_camera->fov,
+ viewport_rect.width / (float)viewport_rect.height,
+ distances[i],
+ distances[i+1],
+ p_camera->vaspect
+
+ );
+
+ } break;
+ }
+
+ //obtain the frustum endpoints
+
+ Vector3 endpoints[8]; // frustum plane endpoints
+ bool res = camera_matrix.get_endpoints(p_camera->transform,endpoints);
+ ERR_CONTINUE(!res);
+
+ // obtain the light frustm ranges (given endpoints)
+
+ Vector3 x_vec=p_light->data.transform.basis.get_axis( Vector3::AXIS_X ).normalized();
+ Vector3 y_vec=p_light->data.transform.basis.get_axis( Vector3::AXIS_Y ).normalized();
+ Vector3 z_vec=p_light->data.transform.basis.get_axis( Vector3::AXIS_Z ).normalized();
+ //z_vec points agsint the camera, like in default opengl
+
+ float x_min,x_max;
+ float y_min,y_max;
+ float z_min,z_max;
+
+ float x_min_cam,x_max_cam;
+ float y_min_cam,y_max_cam;
+ float z_min_cam,z_max_cam;
+
+
+ //used for culling
+ for(int j=0;j<8;j++) {
+
+ float d_x=x_vec.dot(endpoints[j]);
+ float d_y=y_vec.dot(endpoints[j]);
+ float d_z=z_vec.dot(endpoints[j]);
+
+ if (j==0 || d_x<x_min)
+ x_min=d_x;
+ if (j==0 || d_x>x_max)
+ x_max=d_x;
+
+ if (j==0 || d_y<y_min)
+ y_min=d_y;
+ if (j==0 || d_y>y_max)
+ y_max=d_y;
+
+ if (j==0 || d_z<z_min)
+ z_min=d_z;
+ if (j==0 || d_z>z_max)
+ z_max=d_z;
+
+
+ }
+
+
+
+
+
+ {
+ //camera viewport stuff
+ //this trick here is what stabilizes the shadow (make potential jaggies to not move)
+ //at the cost of some wasted resolution. Still the quality increase is very well worth it
+
+
+ Vector3 center;
+
+ for(int j=0;j<8;j++) {
+
+ center+=endpoints[j];
+ }
+ center/=8.0;
+
+ //center=x_vec*(x_max-x_min)*0.5 + y_vec*(y_max-y_min)*0.5 + z_vec*(z_max-z_min)*0.5;
+
+ float radius=0;
+
+ for(int j=0;j<8;j++) {
+
+ float d = center.distance_to(endpoints[j]);
+ if (d>radius)
+ radius=d;
+ }
+
+
+ radius *= texsize/(texsize-2.0); //add a texel by each side, so stepified texture will always fit
+
+ x_max_cam=x_vec.dot(center)+radius;
+ x_min_cam=x_vec.dot(center)-radius;
+ y_max_cam=y_vec.dot(center)+radius;
+ y_min_cam=y_vec.dot(center)-radius;
+ z_max_cam=z_vec.dot(center)+radius;
+ z_min_cam=z_vec.dot(center)-radius;
+
+ float unit = radius*2.0/texsize;
+
+ x_max_cam=Math::stepify(x_max_cam,unit);
+ x_min_cam=Math::stepify(x_min_cam,unit);
+ y_max_cam=Math::stepify(y_max_cam,unit);
+ y_min_cam=Math::stepify(y_min_cam,unit);
+
+ }
+
+ //now that we now all ranges, we can proceed to make the light frustum planes, for culling octree
+
+ Vector<Plane> light_frustum_planes;
+ light_frustum_planes.resize(6);
+
+ //right/left
+ light_frustum_planes[0]=Plane( x_vec, x_max );
+ light_frustum_planes[1]=Plane( -x_vec, -x_min );
+ //top/bottom
+ light_frustum_planes[2]=Plane( y_vec, y_max );
+ light_frustum_planes[3]=Plane( -y_vec, -y_min );
+ //near/far
+ light_frustum_planes[4]=Plane( z_vec, z_max+1e6 );
+ light_frustum_planes[5]=Plane( -z_vec, -z_min ); // z_min is ok, since casters further than far-light plane are not needed
+
+ int caster_cull_count = p_scenario->octree.cull_convex(light_frustum_planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,INSTANCE_GEOMETRY_MASK);
+
+ // a pre pass will need to be needed to determine the actual z-near to be used
+ for(int j=0;j<caster_cull_count;j++) {
+
+ float min,max;
+ Instance *ins=instance_shadow_cull_result[j];
+ if (!ins->visible || !ins->cast_shadows)
+ continue;
+ ins->transformed_aabb.project_range_in_plane(Plane(z_vec,0),min,max);
+
+ if (max>z_max)
+ z_max=max;
+ }
+
+ {
+ CameraMatrix ortho_camera;
+ real_t half_x = (x_max_cam-x_min_cam) * 0.5;
+ real_t half_y = (y_max_cam-y_min_cam) * 0.5;
+
+
+ ortho_camera.set_orthogonal( -half_x, half_x,-half_y,half_y, 0, (z_max-z_min_cam) );
+
+ Transform ortho_transform;
+ ortho_transform.basis=p_light->data.transform.basis;
+ ortho_transform.origin=x_vec*(x_min_cam+half_x)+y_vec*(y_min_cam+half_y)+z_vec*z_max;
+
+ rasterizer->light_instance_set_shadow_transform(p_light->light_info->instance, i, ortho_camera, ortho_transform,distances[i],distances[i+1] );
+ }
+
+ rasterizer->begin_shadow_map( p_light->light_info->instance, i );
+
+ for (int j=0;j<caster_cull_count;j++) {
+
+ Instance *instance = instance_shadow_cull_result[j];
+ if (!instance->visible || !instance->cast_shadows)
+ continue;
+ _instance_draw(instance);
+ }
+
+ rasterizer->end_shadow_map();
+
+
+ }
+
+
+}
+
+
+CameraMatrix _lispm_look( const Vector3 pos, const Vector3 dir, const Vector3 up) {
+
+ Vector3 dirN;
+ Vector3 upN;
+ Vector3 lftN;
+
+ lftN=dir.cross(up);
+ lftN.normalize();
+
+ upN=lftN.cross(dir);
+ upN.normalize();
+ dirN=dir.normalized();
+
+ CameraMatrix cmout;
+ float *output=&cmout.matrix[0][0];
+ output[ 0] = lftN[0];
+ output[ 1] = upN[0];
+ output[ 2] = -dirN[0];
+ output[ 3] = 0.0;
+
+ output[ 4] = lftN[1];
+ output[ 5] = upN[1];
+ output[ 6] = -dirN[1];
+ output[ 7] = 0.0;
+
+ output[ 8] = lftN[2];
+ output[ 9] = upN[2];
+ output[10] = -dirN[2];
+ output[11] = 0.0;
+
+ output[12] = -lftN.dot(pos);
+ output[13] = -upN.dot(pos);
+ output[14] = dirN.dot(pos);
+ output[15] = 1.0;
+
+ return cmout;
+}
+
+
+
+#if 1
+
+void VisualServerRaster::_light_instance_update_lispsm_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range) {
+
+ Vector3 light_vec = -p_light->data.transform.basis.get_axis(2);
+ Vector3 view_vec = -p_camera->transform.basis.get_axis(2);
+ float viewdot = light_vec.normalized().dot(view_vec.normalized());
+
+
+ float near_dist=1;
+
+ Vector<Plane> light_frustum_planes = _camera_generate_orthogonal_planes(p_light,p_camera,p_cull_range.min,p_cull_range.max);
+ int caster_count = p_scenario->octree.cull_convex(light_frustum_planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,INSTANCE_GEOMETRY_MASK);
+
+ // this could be faster by just getting supports from the AABBs..
+ // but, safer to do as the original implementation explains for now..
+
+ Vector<Vector3> caster_pointcloud;
+ caster_pointcloud.resize(caster_count*8);
+ int caster_pointcloud_size=0;
+
+ {
+
+ //fill pointcloud
+ Vector3* caster_pointcloud_ptr=&caster_pointcloud[0];
+
+ for(int i=0;i<caster_count;i++) {
+
+ Instance *ins = instance_shadow_cull_result[i];
+ if (!ins->visible || !ins->cast_shadows)
+ continue;
+
+ for(int j=0;j<8;j++) {
+
+ Vector3 v = ins->aabb.get_endpoint(j);
+ v = ins->data.transform.xform(v);
+ caster_pointcloud_ptr[caster_pointcloud_size+j]=v;
+ }
+
+ caster_pointcloud_size+=8;
+
+ }
+ }
+
+ // now generate a pointcloud that contains the maximum bound (camera extruded by light)
+
+ Vector<Vector3> camera_pointcloud = _camera_generate_endpoints(p_light,p_camera,p_cull_range.min,p_cull_range.max);
+ int cpcsize=camera_pointcloud.size();
+ camera_pointcloud.resize( cpcsize*2 );
+
+ for(int i=0;i<cpcsize;i++) {
+
+ camera_pointcloud[i+cpcsize]=camera_pointcloud[i]-light_vec*1000;
+ }
+
+
+
+ // Vector<Vector3> frustum_points=_camera_generate_endpoints(p_light,p_camera,p_cull_range.min,p_cull_range.max);
+
+
+ // compute the "light-space" basis, using the algorithm described in the paper
+ // note: since bodyB is defined in eye space, all of these vectors should also be defined in eye space
+
+
+ Vector3 eye = p_camera->transform.origin;
+ Vector3 up = light_vec.cross(view_vec).cross(light_vec).normalized();
+
+
+ CameraMatrix light_space_basis = _lispm_look(eye,light_vec,up);
+
+ AABB light_space_aabb;
+
+
+ { //create an optimal AABB from both the camera pointcloud and the objects pointcloud
+ AABB light_space_pointcloud_aabb;
+ AABB light_space_camera_aabb;
+ //xform pointcloud
+ const Vector3* caster_pointcloud_ptr=&caster_pointcloud[0];
+
+ for(int i=0;i<caster_pointcloud_size;i++) {
+
+ Vector3 p = light_space_basis.xform(caster_pointcloud_ptr[i]);
+ if (i==0) {
+ light_space_pointcloud_aabb.pos=p;
+ } else {
+ light_space_pointcloud_aabb.expand_to(p);
+ }
+ }
+ for(int i=0;i<camera_pointcloud.size();i++) {
+
+ Vector3 p = light_space_basis.xform(camera_pointcloud[i]);
+ if (i==0) {
+ light_space_camera_aabb.pos=p;
+ } else {
+ light_space_camera_aabb.expand_to(p);
+ }
+ }
+
+ light_space_aabb=light_space_pointcloud_aabb.intersection(light_space_camera_aabb);
+ }
+
+ float lvdp = light_vec.dot(view_vec);
+
+ float sin_gamma = Math::sqrt(1.0-lvdp*lvdp);
+ //use the formulas of the paper to get n (and f)
+ float factor = 1.0/sin_gamma;
+ float z_n = factor*near_dist; //often 1
+ float d = Math::abs(light_space_aabb.size.y); //perspective transform depth //light space y extents
+ float z_f = z_n + d*sin_gamma;
+ float n = (z_n+Math::sqrt(z_f*z_n))/sin_gamma;
+ float f = n+d;
+
+ Vector3 pos = eye - up*(n-near_dist);
+
+ CameraMatrix light_space_basis2 = _lispm_look(pos,light_vec,up);
+ //Transform light_space_basis2;
+ //light_space_basis2.set_look_at(pos,light_vec-pos,up);
+ //light_space_basis2.affine_invert();
+
+ //one possibility for a simple perspective transformation matrix
+ //with the two parameters n(near) and f(far) in y direction
+
+ CameraMatrix lisp_matrix;
+ lisp_matrix.matrix[1][1]=(f+n)/(f-n);
+ lisp_matrix.matrix[3][1]=-2*f*n/(f-n);
+ lisp_matrix.matrix[1][3]=1;
+ lisp_matrix.matrix[3][3]=0;
+
+ CameraMatrix projection = lisp_matrix * light_space_basis2;
+ //CameraMatrix projection = light_space_basis2 * lisp_matrix;
+
+
+ AABB proj_space_aabb;
+ float max_d,min_d;
+
+ {
+
+ AABB proj_space_pointcloud_aabb;
+ AABB proj_space_camera_aabb;
+ //xform pointcloud
+ Vector3* caster_pointcloud_ptr=&caster_pointcloud[0];
+ for(int i=0;i<caster_pointcloud_size;i++) {
+
+ Vector3 p = projection.xform(caster_pointcloud_ptr[i]);
+ if (i==0) {
+ proj_space_pointcloud_aabb.pos=p;
+ } else {
+ proj_space_pointcloud_aabb.expand_to(p);
+ }
+ }
+
+ for(int i=0;i<camera_pointcloud.size();i++) {
+
+ Vector3 p = projection.xform(camera_pointcloud[i]);
+ if (i==0) {
+ proj_space_camera_aabb.pos=p;
+ } else {
+ proj_space_camera_aabb.expand_to(p);
+ }
+ }
+
+ //proj_space_aabb=proj_space_pointcloud_aabb.intersection_with(proj_space_camera_aabb);
+ proj_space_aabb=proj_space_pointcloud_aabb;
+ }
+
+ projection.scale_translate_to_fit(proj_space_aabb);
+ projection=projection * lisp_matrix;
+
+ CameraMatrix scale;
+ scale.make_scale(Vector3(1.0,1.0,-1.0)); // transform to left handed
+
+ projection=scale * projection;
+
+ rasterizer->light_instance_set_shadow_transform(p_light->light_info->instance,0, projection , light_space_basis2.inverse() );
+
+ rasterizer->begin_shadow_map( p_light->light_info->instance, 0 );
+
+ for(int i=0;i<caster_count;i++) {
+
+ Instance *instance = instance_shadow_cull_result[i];
+
+ if (!instance->visible || !instance->cast_shadows)
+ continue;
+ _instance_draw(instance);
+ }
+
+ rasterizer->end_shadow_map();
+
+
+}
+
+#else
+
+
+void VisualServerRaster::_light_instance_update_lispsm_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range) {
+
+ /* STEP 1: GENERATE LIGHT TRANSFORM */
+
+
+
+ Vector3 light_vec = -p_light->data.transform.basis.get_axis(2);
+ Vector3 view_vec = -p_camera->transform.basis.get_axis(2);
+ float viewdot = Math::absf(light_vec.dot(view_vec));
+
+ Vector3 up = light_vec.cross(view_vec).cross(light_vec).normalized();
+
+ Transform light_transform;
+ light_transform.set_look_at(Vector3(),light_vec,up);
+
+
+ /* STEP 2: GENERATE WORDLSPACE PLANES AND VECTORS*/
+ float range_min=0.01; //p_cull_range.min
+ float range_max=20;//p_cull_range.max;
+
+ Vector<Vector3> camera_endpoints=_camera_generate_endpoints(p_light,p_camera,range_min,range_max); // frustum plane endpoints
+ ERR_FAIL_COND(camera_endpoints.empty());
+
+ // obtain the light frustm ranges (given endpoints)
+
+
+ Vector3 light_x_vec=light_transform.basis.get_axis( Vector3::AXIS_X ).normalized();
+ Vector3 light_y_vec=light_transform.basis.get_axis( Vector3::AXIS_Y ).normalized();
+ Vector3 light_z_vec=light_transform.basis.get_axis( Vector3::AXIS_Z ).normalized();
+
+ Vector3 light_axis_max;
+ Vector3 light_axis_min;
+
+ for(int j=0;j<8;j++) {
+
+ float d_x=light_x_vec.dot(camera_endpoints[j]);
+ float d_y=light_y_vec.dot(camera_endpoints[j]);
+ float d_z=light_z_vec.dot(camera_endpoints[j]);
+
+ if (j==0 || d_x<light_axis_min.x)
+ light_axis_min.x=d_x;
+ if (j==0 || d_x>light_axis_max.x)
+ light_axis_max.x=d_x;
+
+ if (j==0 || d_y<light_axis_min.y)
+ light_axis_min.y=d_y;
+ if (j==0 || d_y>light_axis_max.y)
+ light_axis_max.y=d_y;
+
+ if (j==0 || d_z<light_axis_min.z)
+ light_axis_min.z=d_z;
+ if (j==0 || d_z>light_axis_max.z)
+ light_axis_max.z=d_z;
+
+
+ }
+
+ //now that we now all ranges, we can proceed to make the light frustum planes, for culling octree
+
+ Vector<Plane> light_cull_planes;
+ light_cull_planes.resize(6);
+
+
+ //right/left
+ light_cull_planes[0]=Plane( light_x_vec, light_axis_max.x );
+ light_cull_planes[1]=Plane( -light_x_vec, -light_axis_min.x );
+ //top/bottom
+ light_cull_planes[2]=Plane( light_y_vec, light_axis_max.y );
+ light_cull_planes[3]=Plane( -light_y_vec, -light_axis_min.y );
+ //near/far
+ light_cull_planes[4]=Plane( light_z_vec, light_axis_max.z+1e6 );
+ light_cull_planes[5]=Plane( -light_z_vec, -light_axis_min.z ); // z_min is ok, since casters further than far-light plane are not needed
+
+
+ /* STEP 3: CULL CASTERS */
+
+ int caster_count = p_scenario->octree.cull_convex(light_cull_planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,INSTANCE_GEOMETRY_MASK);
+
+ /* STEP 4: ADJUST FAR Z PLANE */
+
+ float caster_max_z=1e-1;
+ for(int i=0;i<caster_count;i++) {
+
+ Instance *ins=instance_shadow_cull_result[i];
+ if (!ins->visible || !ins->cast_shadows)
+ continue;
+
+ //@TODO optimize using support mapping
+ for(int j=0;j<8;j++) {
+
+ Vector3 v=ins->data.transform.xform(ins->aabb.get_endpoint(j));
+ float d = light_z_vec.dot(v);
+ if (d>caster_max_z)
+ caster_max_z=d;
+
+ }
+
+ }
+
+ float expand = caster_max_z-light_axis_max.z;
+ if (expand<0)
+ expand=0;
+ light_axis_max.z=MAX(caster_max_z,light_axis_max.z);
+
+ /* STEP 5: CREATE ORTHOGONAL PROJECTION */
+
+ CameraMatrix light_projection;
+
+ real_t half_x = (light_axis_max.x-light_axis_min.x) * 0.5;
+ real_t half_y = (light_axis_max.y-light_axis_min.y) * 0.5;
+ light_projection.set_orthogonal( -half_x, half_x,half_y, -half_y, 0, (light_axis_max.z-light_axis_min.z) );
+ light_transform.origin=light_x_vec*(light_axis_min.x+half_x)+light_y_vec*(light_axis_min.y+half_y)+light_z_vec*light_axis_max.z;
+
+
+ if (/*false &&*/ viewdot<0.96) {
+
+ float lvdp = light_vec.dot(view_vec);
+
+ float near_dist=1.0;
+ float sin_gamma = Math::sqrt(1.0-lvdp*lvdp);
+ //use the formulas of the paper to get n (and f)
+ float factor = 1.0/sin_gamma;
+ float z_n = factor*near_dist; //often 1
+ float d = Math::abs(light_axis_max.y-light_axis_min.y); //perspective transform depth //light space y extents
+ float z_f = z_n + d*sin_gamma;
+ float n = (z_n+Math::sqrt(z_f*z_n))/sin_gamma;
+ float f = n+d;
+
+ CameraMatrix lisp_matrix;
+ lisp_matrix.matrix[1][1]=(f+n)/(f-n);
+ lisp_matrix.matrix[3][1]=-2*f*n/(f-n);
+ lisp_matrix.matrix[1][3]=1;
+ lisp_matrix.matrix[3][3]=0;
+
+ Vector3 pos = p_camera->transform.origin - up*(n-near_dist);
+
+ CameraMatrix world2light = _lispm_look(pos,light_vec,up);
+ CameraMatrix projection = lisp_matrix * world2light;
+
+ AABB projection_bounds;
+ for(int i=0;i<camera_endpoints.size();i++) {
+
+ Vector3 p=camera_endpoints[i];
+ if (i==0)
+ projection_bounds.pos=projection.xform(p);
+ else
+ projection_bounds.expand_to(projection.xform(p));
+
+ projection_bounds.expand_to(projection.xform(p+light_vec*-expand));
+ }
+
+ CameraMatrix scaletrans;
+ scaletrans.scale_translate_to_fit(projection_bounds);
+ projection=scaletrans * lisp_matrix;
+
+ CameraMatrix scale;
+ scale.make_scale(Vector3(1.0,1.0,-1.0)); // transform to left handed
+
+ projection=scale * projection;
+
+
+ rasterizer->light_instance_set_shadow_transform(p_light->light_info->instance,0, projection, world2light.inverse(), viewdot);
+
+ } else {
+ //orthogonal
+ rasterizer->light_instance_set_shadow_transform(p_light->light_info->instance,0, light_projection , light_transform, viewdot);
+ }
+
+ rasterizer->begin_shadow_map( p_light->light_info->instance, 0 );
+
+ for(int i=0;i<caster_count;i++) {
+
+ Instance *instance = instance_shadow_cull_result[i];
+
+ if (!instance->visible || !instance->cast_shadows)
+ continue;
+ _instance_draw(instance);
+ }
+
+ rasterizer->end_shadow_map();
+
+}
+
+#endif
+
+
+void VisualServerRaster::_light_instance_update_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range) {
+
+
+
+ if (!rasterizer->shadow_allocate_near( p_light->light_info->instance ))
+ return; // shadow could not be updated
+
+
+ /* VisualServerRaster supports for many shadow techniques, using the one the rasterizer requests */
+
+ Rasterizer::ShadowType shadow_type = rasterizer->light_instance_get_shadow_type(p_light->light_info->instance);
+
+ switch(shadow_type) {
+
+ case Rasterizer::SHADOW_SIMPLE: {
+ /* SPOT SHADOW */
+
+
+ rasterizer->begin_shadow_map( p_light->light_info->instance, 0 );
+
+ //using this one ensures that raster deferred will have it
+
+ float far = rasterizer->light_get_var( p_light->base_rid, VS::LIGHT_PARAM_RADIUS);
+
+ float angle = rasterizer->light_get_var( p_light->base_rid, VS::LIGHT_PARAM_SPOT_ANGLE );
+
+ CameraMatrix cm;
+ cm.set_perspective( angle*2.0, 1.0, 0.001, far );
+
+ Vector<Plane> planes = cm.get_projection_planes(p_light->data.transform);
+ int cull_count = p_scenario->octree.cull_convex(planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,INSTANCE_GEOMETRY_MASK);
+
+
+ for (int i=0;i<cull_count;i++) {
+
+ Instance *instance = instance_shadow_cull_result[i];
+ if (!instance->visible || !instance->cast_shadows)
+ continue;
+ _instance_draw(instance);
+ }
+
+ rasterizer->end_shadow_map();
+
+ } break;
+ case Rasterizer::SHADOW_DUAL_PARABOLOID: {
+
+ /* OMNI SHADOW */
+
+ int passes = rasterizer->light_instance_get_shadow_passes( p_light->light_info->instance );
+
+ if (passes==2) {
+
+ for(int i=0;i<2;i++) {
+
+ rasterizer->begin_shadow_map( p_light->light_info->instance, i );
+
+
+ //using this one ensures that raster deferred will have it
+
+ float radius = rasterizer->light_get_var( p_light->base_rid, VS::LIGHT_PARAM_RADIUS);
+
+ float z =i==0?-1:1;
+ Vector<Plane> planes;
+ planes.resize(5);
+ planes[0]=p_light->data.transform.xform(Plane(Vector3(0,0,z),radius));
+ planes[1]=p_light->data.transform.xform(Plane(Vector3(1,0,z).normalized(),radius));
+ planes[2]=p_light->data.transform.xform(Plane(Vector3(-1,0,z).normalized(),radius));
+ planes[3]=p_light->data.transform.xform(Plane(Vector3(0,1,z).normalized(),radius));
+ planes[4]=p_light->data.transform.xform(Plane(Vector3(0,-1,z).normalized(),radius));
+
+
+ int cull_count = p_scenario->octree.cull_convex(planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,INSTANCE_GEOMETRY_MASK);
+
+
+ for (int j=0;j<cull_count;j++) {
+
+ Instance *instance = instance_shadow_cull_result[j];
+ if (!instance->visible || !instance->cast_shadows)
+ continue;
+
+ _instance_draw(instance);
+ }
+
+ rasterizer->end_shadow_map();
+ }
+ } else if (passes==1) {
+ //one go
+
+
+
+ }
+
+ } break;
+ case Rasterizer::SHADOW_CUBE: {
+
+ // todo
+ } break;
+ case Rasterizer::SHADOW_ORTHOGONAL: {
+
+ _light_instance_update_pssm_shadow(p_light,p_scenario,p_camera,p_cull_range);
+ } break;
+ case Rasterizer::SHADOW_PSSM: {
+
+ _light_instance_update_pssm_shadow(p_light,p_scenario,p_camera,p_cull_range);
+ } break;
+ case Rasterizer::SHADOW_PSM: {
+
+ _light_instance_update_lispsm_shadow(p_light,p_scenario,p_camera,p_cull_range);
+ // todo
+ } break;
+ default: {}
+ }
+
+}
+
+void VisualServerRaster::_portal_disconnect(Instance *p_portal,bool p_cleanup) {
+
+ if (p_portal->portal_info->connected) {
+
+ //disconnect first
+ p_portal->portal_info->connected->portal_info->connected=NULL;
+ p_portal->portal_info->connected=NULL;
+
+ }
+
+ if (p_portal->room && p_portal->room->room) {
+
+ if (p_cleanup) {
+
+ p_portal->room->room->room_info->disconnected_child_portals.erase(p_portal);
+ //p_portal->room->room->room_info->disconnected_child_portals.erase(p_portal);
+ } else {
+ p_portal->room->room->room_info->disconnected_child_portals.insert(p_portal);
+ }
+ }
+
+}
+
+void VisualServerRaster::_instance_validate_autorooms(Instance *p_geometry) {
+
+ if (p_geometry->auto_rooms.size()==0)
+ return;
+
+ p_geometry->valid_auto_rooms.clear();
+
+ int point_count = aabb_random_points.size();
+ const Vector3 * src_points = &aabb_random_points[0];
+
+ for(Set<Instance*>::Element *E=p_geometry->valid_auto_rooms.front();E;E=E->next()) {
+
+ Instance *room = E->get();
+ Vector3 *dst_points=&transformed_aabb_random_points[0];
+
+ //generate points
+ for(int i=0;i<point_count;i++) {
+
+ dst_points[i] = room->room_info->affine_inverse.xform(p_geometry->data.transform.xform((src_points[i]*p_geometry->transformed_aabb.size)+p_geometry->transformed_aabb.pos));
+ }
+
+ int pass = room->room_info->room->bounds.get_points_inside(dst_points,point_count);
+
+ float ratio = (float)pass / point_count;
+
+ if (ratio>0.5) // should make some constant
+ p_geometry->valid_auto_rooms.insert(room);
+ }
+}
+
+void VisualServerRaster::_portal_attempt_connect(Instance *p_portal) {
+
+
+ _portal_disconnect(p_portal);
+
+ Vector3 A_norm = p_portal->data.transform.basis.get_axis(Vector3::AXIS_Z).normalized();
+ Plane A_plane( p_portal->data.transform.origin, A_norm );
+ float A_surface = p_portal->portal_info->portal->bounds.get_area();
+ if (A_surface==0)
+ return; //wtf
+
+ Instance *found=NULL;
+ Transform affine_inverse = p_portal->data.transform.affine_inverse();
+
+ for(Set<Instance*>::Element *E=p_portal->portal_info->candidate_set.front();E;E=E->next()) {
+
+ Instance *B = E->get();
+
+ if (B->portal_info->connected)
+ continue; // in use
+
+ Vector3 B_norm = B->data.transform.basis.get_axis(Vector3::AXIS_Z).normalized();
+
+ // check that they are in front of another
+ float dot = A_norm.dot(-B_norm);
+
+ if (dot<0.707) // 45 degrees, TODO unharcode this
+ continue;
+
+ // check the max distance to the other portal
+
+ bool valid=true;
+
+ Rect2 local_bounds;
+
+ for(int i=0;i<B->portal_info->portal->shape.size();i++) {
+
+ Point2 point2 = B->portal_info->portal->shape[i];
+
+ Vector3 point = B->data.transform.xform( Vector3( point2.x, point2.y, 0 ) );
+
+ float dist = Math::abs(A_plane.distance_to(point));
+
+ if (
+ dist>p_portal->portal_info->portal->connect_range ||
+ dist>B->portal_info->portal->connect_range ) {
+ valid=false;
+ break;
+ }
+
+
+ Vector3 point_local = affine_inverse.xform(A_plane.project(point));
+ point2 = Point2(point_local.x,point_local.y);
+
+ if (i==0)
+ local_bounds.pos=point2;
+ else
+ local_bounds.expand_to(point2);
+
+
+ }
+
+ if (!valid)
+ continue;
+
+ float B_surface = B->portal_info->portal->bounds.get_area();
+ if (B_surface==0)
+ continue; //wtf
+
+ float clip_area = p_portal->portal_info->portal->bounds.clip(local_bounds).get_area();
+
+
+ //check that most of the area is shared
+
+ if ( (clip_area/A_surface) < 0.5 || (clip_area/B_surface) < 0.5) // TODO change for something else
+ continue;
+
+ found=B;
+ break;
+
+
+
+ }
+
+
+ if (!found) {
+
+ if (p_portal->room && p_portal->room->room) {
+
+ p_portal->room->room->room_info->disconnected_child_portals.insert(p_portal);
+ }
+
+ return;
+ }
+
+ p_portal->portal_info->connected=found;
+ found->portal_info->connected=p_portal;
+
+
+}
+
+void* VisualServerRaster::instance_pair(void *p_self, OctreeElementID, Instance *p_A,int, OctreeElementID, Instance *p_B,int) {
+
+ VisualServerRaster *self = (VisualServerRaster*)p_self;
+ Instance *A = p_A;
+ Instance *B = p_B;
+
+ if (A->base_type==INSTANCE_PORTAL) {
+
+ ERR_FAIL_COND_V( B->base_type!=INSTANCE_PORTAL,NULL );
+
+ A->portal_info->candidate_set.insert(B);
+ B->portal_info->candidate_set.insert(A);
+
+ self->_portal_attempt_connect(A);
+ //attempt to conncet portal A (will go through B anyway)
+ //this is a little hackish, but works fine in practice
+
+ } else if (A->base_type==INSTANCE_ROOM || B->base_type==INSTANCE_ROOM) {
+
+ if (B->base_type==INSTANCE_ROOM)
+ SWAP(A,B);
+
+ ERR_FAIL_COND_V(! ((1<<B->base_type)&INSTANCE_GEOMETRY_MASK ),NULL);
+
+ B->auto_rooms.insert(A);
+ A->room_info->owned_autoroom_geometry.insert(B);
+
+ self->_instance_validate_autorooms(B);
+
+
+ } else {
+
+ if (B->base_type==INSTANCE_LIGHT) {
+
+ SWAP(A,B);
+ } else if (A->base_type!=INSTANCE_LIGHT) {
+ return NULL;
+ }
+
+
+ A->light_info->affected.insert(B);
+ B->lights.insert(A);
+ B->light_cache_dirty=true;
+ }
+
+ return NULL;
+
+}
+void VisualServerRaster::instance_unpair(void *p_self, OctreeElementID, Instance *p_A,int, OctreeElementID, Instance *p_B,int,void*) {
+
+ VisualServerRaster *self = (VisualServerRaster*)p_self;
+ Instance *A = p_A;
+ Instance *B = p_B;
+
+ if (A->base_type==INSTANCE_PORTAL) {
+
+ ERR_FAIL_COND( B->base_type!=INSTANCE_PORTAL );
+
+
+ A->portal_info->candidate_set.erase(B);
+ B->portal_info->candidate_set.erase(A);
+
+ //after disconnecting them, see if they can connect again
+ self->_portal_attempt_connect(A);
+ self->_portal_attempt_connect(B);
+
+ } else if (A->base_type==INSTANCE_ROOM || B->base_type==INSTANCE_ROOM) {
+
+ if (B->base_type==INSTANCE_ROOM)
+ SWAP(A,B);
+
+ ERR_FAIL_COND(! ((1<<B->base_type)&INSTANCE_GEOMETRY_MASK ));
+
+ B->auto_rooms.erase(A);
+ B->valid_auto_rooms.erase(A);
+ A->room_info->owned_autoroom_geometry.erase(B);
+
+ }else {
+
+
+ if (B->base_type==INSTANCE_LIGHT) {
+
+ SWAP(A,B);
+ } else if (A->base_type!=INSTANCE_LIGHT) {
+ return;
+ }
+
+
+ A->light_info->affected.erase(B);
+ B->lights.erase(A);
+ B->light_cache_dirty=true;
+ }
+}
+
+bool VisualServerRaster::_test_portal_cull(Camera *p_camera, Instance *p_from_portal, Instance *p_to_portal) {
+
+
+ int src_point_count=p_from_portal->portal_info->transformed_point_cache.size();
+ int dst_point_count=p_to_portal->portal_info->transformed_point_cache.size();
+
+ if (src_point_count<2 || dst_point_count<2)
+ return false;
+
+ const Vector3 *src_points=&p_from_portal->portal_info->transformed_point_cache[0];
+ const Vector3 *dst_points=&p_to_portal->portal_info->transformed_point_cache[0];
+
+ bool outside=false;
+
+ bool clockwise = !p_from_portal->portal_info->plane_cache.is_point_over(p_camera->transform.origin);
+
+ for(int i=0;i<src_point_count;i++) {
+
+ const Vector3& point_prev = src_points[i?(i-1):(src_point_count-1)];
+ const Vector3& point = src_points[i];
+
+ Plane p = clockwise?Plane(p_camera->transform.origin,point,point_prev):Plane(p_camera->transform.origin,point_prev,point);
+
+ bool all_over=true;
+
+ for(int j=0;j<dst_point_count;j++) {
+
+ if (!p.is_point_over(dst_points[j])) {
+
+ all_over=false;
+ break;
+ }
+
+ }
+
+ if (all_over) {
+ outside=true;
+ break;
+ }
+
+ }
+
+ return !outside;
+
+}
+
+void VisualServerRaster::_cull_portal(Camera *p_camera, Instance *p_portal,Instance *p_from_portal) {
+
+ ERR_FAIL_COND(!p_portal->scenario); //scenario outside
+
+ Instance *portal = p_portal;
+
+ if (!portal->room) {
+
+ return; //portals need all to belong to a room, it may be unconfigured yet
+ } else if (portal->last_render_pass!=render_pass) {
+
+ return; //invalid portal, ignore
+ } else if (portal->portal_info->last_visited_pass==render_pass) {
+
+ return; //portal already visited
+ } else if (portal==p_from_portal) {
+
+ return; // came from this portal, don't even bother testing
+ }
+
+ /* TEST DISABLE DISTANCE */
+
+ float disable_distance = p_portal->portal_info->portal->disable_distance;
+ if (disable_distance) {
+ //has disable distance..
+ float distance = p_camera->transform.origin.distance_to(portal->data.transform.origin);
+ if (disable_distance < distance) {
+
+ return;
+ }
+ }
+
+ /* TEST PORTAL NOT FACING OPTIMIZATION */
+
+
+ if (p_portal->portal_info->connected) {
+ //connected portal means, it must face against the camera to be seen
+ if (p_portal->portal_info->plane_cache.is_point_over(p_camera->transform.origin)) { //portal facing against camera (exterior)
+
+ return;
+ }
+ } else {
+ //disconencted portals (go from room to parent room or exterior) must face towards the canera
+ if (!p_portal->portal_info->plane_cache.is_point_over(p_camera->transform.origin)) { //portal facing against camera (exterior)
+
+ return;
+ }
+ }
+
+ if (p_from_portal && !_test_portal_cull(p_camera, p_from_portal, portal)) {
+ return; // portal not visible (culled)
+ }
+
+ portal->portal_info->last_visited_pass=render_pass;
+
+ if (portal->portal_info->connected) {
+
+ //interior<->interior portal
+ Instance *to_room = portal->portal_info->connected->room;
+ if (!to_room) {
+ return; //wtf.. oh well, connected to a roomless (invalid) portal
+ }
+
+ _cull_room(p_camera, to_room, portal->portal_info->connected);
+
+ } else {
+ //to exterior/to parent roomportal
+
+ Instance *parent_room = portal->room->room;
+
+ _cull_room(p_camera, parent_room, portal);
+ }
+
+}
+
+void VisualServerRaster::_cull_room(Camera *p_camera, Instance *p_room,Instance *p_from_portal) {
+
+ if (p_room==NULL) {
+ //exterior
+ exterior_visited=true;
+
+ for(int i=0;i<exterior_portal_cull_count;i++) {
+
+ _cull_portal(p_camera, exterior_portal_cull_result[i],p_from_portal);
+ }
+
+ } else {
+
+ ERR_FAIL_COND(!p_room->scenario);
+
+ if (p_room->last_render_pass!=render_pass)
+ return; //this room is invalid
+
+ //interior
+ //first of all, validate the room
+ p_room->room_info->last_visited_pass=render_pass;
+ //see about going around portals
+ if (!p_room->room_info->room->occlude_exterior)
+ exterior_visited=true;
+
+ for(List<Instance*>::Element * E=p_room->room_info->owned_portal_instances.front();E;E=E->next()) {
+
+ _cull_portal(p_camera, E->get(),p_from_portal);
+
+ }
+
+ for(Set<Instance*>::Element * E=p_room->room_info->disconnected_child_portals.front();E;E=E->next()) {
+
+ _cull_portal(p_camera, E->get(),p_from_portal);
+
+ }
+
+
+ }
+
+}
+
+void VisualServerRaster::_render_camera(Viewport *p_viewport,Camera *p_camera, Scenario *p_scenario) {
+
+
+ uint64_t t = OS::get_singleton()->get_ticks_usec();
+ render_pass++;
+ uint32_t camera_layer_mask=p_camera->visible_layers;
+
+ /* STEP 1 - SETUP CAMERA */
+ CameraMatrix camera_matrix;
+
+ switch(p_camera->type) {
+ case Camera::ORTHOGONAL: {
+
+ camera_matrix.set_orthogonal(
+ p_camera->size,
+ viewport_rect.width / (float)viewport_rect.height,
+ p_camera->znear,
+ p_camera->zfar,
+ p_camera->vaspect
+
+ );
+ } break;
+ case Camera::PERSPECTIVE: {
+
+ camera_matrix.set_perspective(
+ p_camera->fov,
+ viewport_rect.width / (float)viewport_rect.height,
+ p_camera->znear,
+ p_camera->zfar,
+ p_camera->vaspect
+
+ );
+
+ } break;
+ }
+
+
+ rasterizer->set_camera(p_camera->transform, camera_matrix);
+
+ Vector<Plane> planes = camera_matrix.get_projection_planes(p_camera->transform);
+
+ CullRange cull_range; // cull range is used for PSSM, and having an idea of the rendering depth
+ cull_range.nearp=Plane(p_camera->transform.origin,-p_camera->transform.basis.get_axis(2).normalized());
+ cull_range.z_near=camera_matrix.get_z_near();
+ cull_range.z_far=camera_matrix.get_z_far();
+ cull_range.min=cull_range.z_far;
+ cull_range.max=cull_range.z_near;
+
+ /* STEP 2 - CULL */
+ int cull_count = p_scenario->octree.cull_convex(planes,instance_cull_result,MAX_INSTANCE_CULL);
+ light_cull_count=0;
+
+/* print_line("OT: "+rtos( (OS::get_singleton()->get_ticks_usec()-t)/1000.0));
+ print_line("OTO: "+itos(p_scenario->octree.get_octant_count()));
+// print_line("OTE: "+itos(p_scenario->octree.get_elem_count()));
+ print_line("OTP: "+itos(p_scenario->octree.get_pair_count()));
+*/
+
+ /* STEP 3 - PROCESS PORTALS, VALIDATE ROOMS */
+
+
+ // compute portals
+
+ exterior_visited=false;
+ exterior_portal_cull_count=0;
+
+ if (room_cull_enabled) {
+ for(int i=0;i<cull_count;i++) {
+
+ Instance *ins = instance_cull_result[i];
+ ins->last_render_pass=render_pass;
+
+ if (ins->base_type!=INSTANCE_PORTAL)
+ continue;
+
+ if (ins->room)
+ continue;
+
+ ERR_CONTINUE(exterior_portal_cull_count>=MAX_EXTERIOR_PORTALS);
+ exterior_portal_cull_result[exterior_portal_cull_count++]=ins;
+
+ }
+
+ room_cull_count = p_scenario->octree.cull_point(p_camera->transform.origin,room_cull_result,MAX_ROOM_CULL,NULL,(1<<INSTANCE_ROOM)|(1<<INSTANCE_PORTAL));
+
+
+ Set<Instance*> current_rooms;
+ Set<Instance*> portal_rooms;
+ //add to set
+ for(int i=0;i<room_cull_count;i++) {
+
+ if (room_cull_result[i]->base_type==INSTANCE_ROOM) {
+ current_rooms.insert(room_cull_result[i]);
+ }
+ if (room_cull_result[i]->base_type==INSTANCE_PORTAL) {
+ //assume inside that room if also inside the portal..
+ if (room_cull_result[i]->room) {
+ portal_rooms.insert(room_cull_result[i]->room);
+ }
+
+ SWAP(room_cull_result[i],room_cull_result[room_cull_count-1]);
+ room_cull_count--;
+ i--;
+ }
+ }
+
+ //remove from set if it has a parent room or BSP doesn't contain
+ for(int i=0;i<room_cull_count;i++) {
+ Instance *r = room_cull_result[i];
+
+ //check inside BSP
+ Vector3 room_local_point = r->room_info->affine_inverse.xform( p_camera->transform.origin );
+
+ if (!portal_rooms.has(r) && !r->room_info->room->bounds.point_is_inside(room_local_point)) {
+
+ current_rooms.erase(r);
+ continue;
+ }
+
+ //check parent
+ while (r->room) {// has parent room
+
+ current_rooms.erase(r);
+ r=r->room;
+ }
+
+ }
+
+ if (current_rooms.size()) {
+ //camera is inside a room
+ // go through rooms
+ for(Set<Instance*>::Element *E=current_rooms.front();E;E=E->next()) {
+ _cull_room(p_camera,E->get());
+ }
+
+ } else {
+ //start from exterior
+ _cull_room(p_camera,NULL);
+
+ }
+ }
+
+ /* STEP 4 - REMOVE FURTHER CULLED OBJECTS, ADD LIGHTS */
+
+ for(int i=0;i<cull_count;i++) {
+
+ Instance *ins = instance_cull_result[i];
+
+ bool keep=false;
+
+
+ if ((camera_layer_mask&ins->layer_mask)==0) {
+
+ //failure
+ } else if (ins->base_type==INSTANCE_LIGHT) {
+
+ if (light_cull_count<MAX_LIGHTS_CULLED) {
+ light_cull_result[light_cull_count++]=ins;
+// rasterizer->light_instance_set_active_hint(ins->light_info->instance);
+ {
+ //compute distance to camera using aabb support
+ Vector3 n = ins->data.transform.basis.xform_inv(cull_range.nearp.normal).normalized();
+ Vector3 s = ins->data.transform.xform(ins->aabb.get_support(n));
+ ins->light_info->dtc=cull_range.nearp.distance_to(s);
+ }
+ }
+
+ } else if ((1<<ins->base_type)&INSTANCE_GEOMETRY_MASK && ins->visible) {
+
+
+ bool discarded=false;
+
+ if (ins->draw_range_end>0) {
+
+ float d = cull_range.nearp.distance_to(ins->data.transform.origin);
+ if (d<0)
+ d=0;
+ discarded=(d<ins->draw_range_begin || d>=ins->draw_range_end);
+
+
+ }
+
+ if (!discarded) {
+
+ // test if this geometry should be visible
+
+ if (room_cull_enabled) {
+
+
+ if (ins->visible_in_all_rooms) {
+ keep=true;
+ } else if (ins->room) {
+
+ if (ins->room->room_info->last_visited_pass==render_pass)
+ keep=true;
+ } else if (ins->auto_rooms.size()) {
+
+
+ for(Set<Instance*>::Element *E=ins->auto_rooms.front();E;E=E->next()) {
+
+ if (E->get()->room_info->last_visited_pass==render_pass) {
+ keep=true;
+ break;
+ }
+ }
+ } else if(exterior_visited)
+ keep=true;
+ } else {
+
+ keep=true;
+ }
+ }
+
+
+ if (keep) {
+ // update cull range
+ float min,max;
+ ins->transformed_aabb.project_range_in_plane(cull_range.nearp,min,max);
+
+ if (min<cull_range.min)
+ cull_range.min=min;
+ if (max>cull_range.max)
+ cull_range.max=max;
+ }
+
+ }
+
+ if (!keep) {
+ // remove, no reason to keep
+ cull_count--;
+ SWAP( instance_cull_result[i], instance_cull_result[ cull_count ] );
+ i--;
+ ins->last_render_pass=0; // make invalid
+ } else {
+
+ ins->last_render_pass=render_pass;
+ }
+ }
+
+ if (cull_range.max > cull_range.z_far )
+ cull_range.max=cull_range.z_far;
+ if (cull_range.min < cull_range.z_near )
+ cull_range.min=cull_range.z_near;
+
+ /* STEP 5 - PROCESS LIGHTS */
+
+ rasterizer->shadow_clear_near(); //clear near shadows, will be recreated
+
+ // directional lights
+ {
+ List<RID>::Element *E=p_scenario->directional_lights.front();
+
+
+ while(E) {
+
+ Instance *light = E->get().is_valid()?instance_owner.get(E->get()):NULL;
+
+ if (rasterizer->light_has_shadow(light->base_rid)) {
+ //rasterizer->light_instance_set_active_hint(light->light_info->instance);
+ _light_instance_update_shadow(light,p_scenario,p_camera,cull_range);
+ }
+
+ E=E->next();
+ }
+ }
+
+
+ //discard lights not affecting anything (useful for deferred rendering, shadowmaps, etc)
+
+ for (int i=0;i<light_cull_count;i++) {
+
+ Instance *ins = light_cull_result[i];
+
+ if (light_discard_enabled) {
+
+ //see if the light should be pre discarded because no one is seeing it
+ //this test may seem expensive, but in reality, it shouldn't be
+ //because of early out condition. It will only go through everything
+ //if it's being discarded.
+
+ bool valid=false;
+ InstanceSet::Element *E =ins->light_info->affected.front();
+ while(E) {
+
+ if (E->get()->last_render_pass==render_pass) {
+
+ valid=true; // early out.
+ break;
+ }
+ E=E->next();
+ }
+ if (!valid) {
+
+ light_cull_count--;
+ SWAP( light_cull_result[i], light_cull_result[ light_cull_count ] );
+ i--;
+
+ }
+ }
+
+ }
+
+ {
+ //assign shadows by distance to camera
+ SortArray<Instance*,_InstanceLightsort> sorter;
+ sorter.sort(light_cull_result,light_cull_count);
+ for (int i=0;i<light_cull_count;i++) {
+
+ Instance *ins = light_cull_result[i];
+
+ if (!rasterizer->light_has_shadow(ins->base_rid) || !shadows_enabled)
+ continue;
+
+ /* for far shadows?
+ if (ins->version == ins->light_info->last_version && rasterizer->light_instance_has_far_shadow(ins->light_info->instance))
+ continue; // didn't change
+ */
+
+ _light_instance_update_shadow(ins,p_scenario,p_camera,cull_range);
+ ins->light_info->last_version=ins->version;
+ }
+ }
+
+
+
+ /* STEP 6 - PROCESS GEOMETRY AND DRAW SCENE*/
+
+ RID environment;
+ if (p_camera->env.is_valid()) //camera has more environment priority
+ environment=p_camera->env;
+ else
+ environment=p_scenario->environment;
+
+ rasterizer->begin_scene(p_viewport->viewport_data,environment,p_scenario->debug);
+ rasterizer->set_viewport(viewport_rect);
+
+ // add lights
+
+ {
+ List<RID>::Element *E=p_scenario->directional_lights.front();
+
+
+ for(;E;E=E->next()) {
+ Instance *light = E->get().is_valid()?instance_owner.get(E->get()):NULL;
+
+ ERR_CONTINUE(!light);
+ rasterizer->add_light(light->light_info->instance);
+ light->light_info->last_add_pass=render_pass;
+ }
+
+ for (int i=0;i<light_cull_count;i++) {
+
+ Instance *ins = light_cull_result[i];
+ rasterizer->add_light(ins->light_info->instance);
+ ins->light_info->last_add_pass=render_pass;
+ }
+ }
+ // add geometry
+
+ for(int i=0;i<cull_count;i++) {
+
+ Instance *ins = instance_cull_result[i];
+
+ ERR_CONTINUE(!((1<<ins->base_type)&INSTANCE_GEOMETRY_MASK));
+
+ _instance_draw(ins);
+ }
+
+ rasterizer->end_scene();
+}
+
+void VisualServerRaster::_render_canvas_item(CanvasItem *p_canvas_item,const Matrix32& p_transform,const Rect2& p_clip_rect, float p_opacity) {
+
+ CanvasItem *ci = p_canvas_item;
+
+ if (!ci->visible)
+ return;
+
+ if (p_opacity<0.007)
+ return;
+
+
+ Rect2 rect = ci->get_rect();
+ Matrix32 xform = p_transform * ci->xform;
+ Rect2 global_rect = xform.xform(rect);
+ global_rect.pos+=p_clip_rect.pos;
+
+
+ if (global_rect.intersects(p_clip_rect) && ci->viewport.is_valid() && viewport_owner.owns(ci->viewport)) {
+
+ Viewport *vp = viewport_owner.get(ci->viewport);
+
+ Point2i from = xform.get_origin() + Point2(viewport_rect.x,viewport_rect.y);
+ Point2i size = rect.size;
+ size.x *= xform[0].length();
+ size.y *= xform[1].length();
+
+ _draw_viewport(vp,
+ from.x,
+ from.y,
+ size.x,
+ size.y);
+
+ rasterizer->canvas_begin();
+ }
+
+ int s = ci->commands.size();
+ bool reclip=false;
+
+ float opacity = ci->opacity * p_opacity;
+
+#ifndef ONTOP_DISABLED
+ CanvasItem **child_items = ci->child_items.ptr();
+ int child_item_count=ci->child_items.size();
+ int top_item_count=0;
+ CanvasItem **top_items=(CanvasItem**)alloca(child_item_count*sizeof(CanvasItem*));
+
+ if (ci->clip) {
+ rasterizer->canvas_set_clip(true,global_rect);
+ canvas_clip=global_rect;
+ }
+
+ for(int i=0;i<child_item_count;i++) {
+
+ if (child_items[i]->ontop)
+ top_items[top_item_count++]=child_items[i];
+ else {
+ _render_canvas_item(child_items[i],xform,p_clip_rect,opacity);
+ }
+ }
+#endif
+
+ if (s!=0) {
+
+ //Rect2 rect( ci->rect.pos + p_ofs, ci->rect.size);
+
+ if (p_clip_rect.intersects(global_rect)) {
+
+ rasterizer->canvas_begin_rect(xform);
+ rasterizer->canvas_set_opacity( opacity * ci->self_opacity );
+ rasterizer->canvas_set_blend_mode( ci->blend_mode );
+
+ CanvasItem::Command **commands = &ci->commands[0];
+
+ for (int i=0;i<s;i++) {
+
+ CanvasItem::Command *c=commands[i];
+
+ switch(c->type) {
+ case CanvasItem::Command::TYPE_LINE: {
+
+ CanvasItem::CommandLine* line = static_cast<CanvasItem::CommandLine*>(c);
+ rasterizer->canvas_draw_line(line->from,line->to,line->color,line->width);
+ } break;
+ case CanvasItem::Command::TYPE_RECT: {
+
+ CanvasItem::CommandRect* rect = static_cast<CanvasItem::CommandRect*>(c);
+// rasterizer->canvas_draw_rect(rect->rect,rect->region,rect->source,rect->flags&CanvasItem::CommandRect::FLAG_TILE,rect->flags&CanvasItem::CommandRect::FLAG_FLIP_H,rect->flags&CanvasItem::CommandRect::FLAG_FLIP_V,rect->texture,rect->modulate);
+#if 0
+ int flags=0;
+
+ if (rect->flags&CanvasItem::CommandRect::FLAG_REGION) {
+ flags|=Rasterizer::CANVAS_RECT_REGION;
+ }
+ if (rect->flags&CanvasItem::CommandRect::FLAG_TILE) {
+ flags|=Rasterizer::CANVAS_RECT_TILE;
+ }
+ if (rect->flags&CanvasItem::CommandRect::FLAG_FLIP_H) {
+
+ flags|=Rasterizer::CANVAS_RECT_FLIP_H;
+ }
+ if (rect->flags&CanvasItem::CommandRect::FLAG_FLIP_V) {
+
+ flags|=Rasterizer::CANVAS_RECT_FLIP_V;
+ }
+#else
+
+ int flags=rect->flags;
+#endif
+ rasterizer->canvas_draw_rect(rect->rect,flags,rect->source,rect->texture,rect->modulate);
+
+ } break;
+ case CanvasItem::Command::TYPE_STYLE: {
+
+ CanvasItem::CommandStyle* style = static_cast<CanvasItem::CommandStyle*>(c);
+ rasterizer->canvas_draw_style_box(style->rect,style->texture,style->margin,style->draw_center,style->color);
+
+ } break;
+ case CanvasItem::Command::TYPE_PRIMITIVE: {
+
+ CanvasItem::CommandPrimitive* primitive = static_cast<CanvasItem::CommandPrimitive*>(c);
+ rasterizer->canvas_draw_primitive(primitive->points,primitive->colors,primitive->uvs,primitive->texture,primitive->width);
+ } break;
+ case CanvasItem::Command::TYPE_POLYGON: {
+
+ CanvasItem::CommandPolygon* polygon = static_cast<CanvasItem::CommandPolygon*>(c);
+ rasterizer->canvas_draw_polygon(polygon->count,polygon->indices.ptr(),polygon->points.ptr(),polygon->uvs.ptr(),polygon->colors.ptr(),polygon->texture,polygon->colors.size()==1);
+
+ } break;
+
+ case CanvasItem::Command::TYPE_POLYGON_PTR: {
+
+ CanvasItem::CommandPolygonPtr* polygon = static_cast<CanvasItem::CommandPolygonPtr*>(c);
+ rasterizer->canvas_draw_polygon(polygon->count,polygon->indices,polygon->points,polygon->uvs,polygon->colors,polygon->texture,false);
+ } break;
+ case CanvasItem::Command::TYPE_CIRCLE: {
+
+ CanvasItem::CommandCircle* circle = static_cast<CanvasItem::CommandCircle*>(c);
+ static const int numpoints=32;
+ Vector2 points[numpoints+1];
+ points[numpoints]=circle->pos;
+ int indices[numpoints*3];
+
+ for(int i=0;i<numpoints;i++) {
+
+ points[i]=circle->pos+Vector2( Math::sin(i*Math_PI*2.0/numpoints),Math::cos(i*Math_PI*2.0/numpoints) )*circle->radius;
+ indices[i*3+0]=i;
+ indices[i*3+1]=(i+1)%numpoints;
+ indices[i*3+2]=numpoints;
+ }
+ rasterizer->canvas_draw_polygon(numpoints*3,indices,points,NULL,&circle->color,RID(),true);
+ //rasterizer->canvas_draw_circle(circle->indices.size(),circle->indices.ptr(),circle->points.ptr(),circle->uvs.ptr(),circle->colors.ptr(),circle->texture,circle->colors.size()==1);
+ } break;
+ case CanvasItem::Command::TYPE_TRANSFORM: {
+
+ CanvasItem::CommandTransform* transform = static_cast<CanvasItem::CommandTransform*>(c);
+ rasterizer->canvas_set_transform(transform->xform);
+ } break;
+ case CanvasItem::Command::TYPE_BLEND_MODE: {
+
+ CanvasItem::CommandBlendMode* bm = static_cast<CanvasItem::CommandBlendMode*>(c);
+ rasterizer->canvas_set_blend_mode(bm->blend_mode);
+
+ } break;
+ case CanvasItem::Command::TYPE_CLIP_IGNORE: {
+
+ CanvasItem::CommandClipIgnore* ci = static_cast<CanvasItem::CommandClipIgnore*>(c);
+ if (canvas_clip!=Rect2()) {
+
+ if (ci->ignore!=reclip) {
+ if (ci->ignore) {
+
+ rasterizer->canvas_set_clip(false,Rect2());
+ reclip=true;
+ } else {
+ rasterizer->canvas_set_clip(true,canvas_clip);
+ reclip=false;
+ }
+ }
+ }
+
+
+
+ } break;
+ }
+ }
+ rasterizer->canvas_end_rect();
+ }
+ }
+
+
+ if (reclip) {
+
+ rasterizer->canvas_set_clip(true,canvas_clip);
+ }
+
+#ifndef ONTOP_DISABLED
+
+ for(int i=0;i<top_item_count;i++) {
+
+ _render_canvas_item(top_items[i],xform,p_clip_rect,opacity);
+ }
+
+#else
+ for(int i=0;i<p_canvas_item->child_items.size();i++) {
+
+ _render_canvas_item(p_canvas_item->child_items[i],xform,p_clip_rect,opacity);
+ }
+#endif
+
+
+ if (ci->clip) {
+ rasterizer->canvas_set_clip(false,Rect2());
+ canvas_clip=Rect2();
+ }
+
+}
+
+void VisualServerRaster::_render_canvas(Canvas *p_canvas,const Matrix32 &p_transform) {
+
+ rasterizer->canvas_begin();
+
+ int l = p_canvas->child_items.size();
+
+ for(int i=0;i<l;i++) {
+
+ Canvas::ChildItem& ci=p_canvas->child_items[i];
+ _render_canvas_item(ci.item,p_transform,Rect2(viewport_rect.x,viewport_rect.y,viewport_rect.width,viewport_rect.height),1);
+
+ //mirroring (useful for scrolling backgrounds)
+ if (ci.mirror.x!=0) {
+
+ Matrix32 xform2 = p_transform * Matrix32(0,Vector2(ci.mirror.x,0));
+ _render_canvas_item(ci.item,xform2,Rect2(viewport_rect.x,viewport_rect.y,viewport_rect.width,viewport_rect.height),1);
+ }
+ if (ci.mirror.y!=0) {
+
+ Matrix32 xform2 = p_transform * Matrix32(0,Vector2(0,ci.mirror.y));
+ _render_canvas_item(ci.item,xform2,Rect2(viewport_rect.x,viewport_rect.y,viewport_rect.width,viewport_rect.height),1);
+ }
+ if (ci.mirror.y!=0 && ci.mirror.x!=0) {
+
+ Matrix32 xform2 = p_transform * Matrix32(0,ci.mirror);
+ _render_canvas_item(ci.item,xform2,Rect2(viewport_rect.x,viewport_rect.y,viewport_rect.width,viewport_rect.height),1);
+ }
+
+ }
+
+}
+
+
+void VisualServerRaster::_draw_viewport(Viewport *p_viewport,int p_ofs_x, int p_ofs_y,int p_parent_w,int p_parent_h) {
+
+ ViewportRect desired_rect=p_viewport->rect;
+ ViewportRect old_rect = viewport_rect;
+// bool vpchanged=false;
+ // convert default expanding viewports to actual size
+ //if (desired_rect.x==0 && desired_rect.y==0 && desired_rect.width==0 && desired_rect.height==0) {
+ if (p_parent_w != 0 && p_parent_h != 0) {
+
+ desired_rect.width=p_parent_w;
+ desired_rect.height=p_parent_h;
+ }
+
+ ERR_FAIL_COND(desired_rect.width<=0 || desired_rect.height<=0);
+
+ desired_rect.x+=p_ofs_x;
+ desired_rect.y+=p_ofs_y;
+
+ // if the viewport is different than the actual one, change it
+
+ if ( p_viewport->render_target.is_valid() || viewport_rect.x != desired_rect.x ||
+ viewport_rect.y != desired_rect.y ||
+ viewport_rect.width != desired_rect.width ||
+ viewport_rect.height != desired_rect.height ) {
+
+
+ viewport_rect=desired_rect;
+ rasterizer->set_viewport(viewport_rect);
+
+ }
+
+ /* Camera should always be BEFORE any other 3D */
+
+ if (!p_viewport->hide_scenario && camera_owner.owns(p_viewport->camera) && scenario_owner.owns(p_viewport->scenario)) {
+
+ Camera *camera = camera_owner.get( p_viewport->camera );
+ Scenario *scenario = scenario_owner.get( p_viewport->scenario );
+
+ _update_instances(); // check dirty instances before rendering
+
+ _render_camera(p_viewport, camera,scenario );
+
+ } else if (true /*|| !p_viewport->canvas_list.empty()*/){
+
+ //clear the viewport black because of no camera? i seriously should..
+ rasterizer->clear_viewport(clear_color);
+ }
+
+ if (!p_viewport->hide_canvas) {
+ int i=0;
+
+ Map<Viewport::CanvasKey,Viewport::CanvasData*> canvas_map;
+
+ for (Map<RID,Viewport::CanvasData>::Element *E=p_viewport->canvas_map.front();E;E=E->next()) {
+ canvas_map[ Viewport::CanvasKey( E->key(), E->get().layer) ]=&E->get();
+
+ }
+
+ for (Map<Viewport::CanvasKey,Viewport::CanvasData*>::Element *E=canvas_map.front();E;E=E->next()) {
+
+
+ // print_line("canvas "+itos(i)+" size: "+itos(I->get()->canvas->child_items.size()));
+ //print_line("GT "+p_viewport->global_transform+". CT: "+E->get()->transform);
+ Matrix32 xform = p_viewport->global_transform * E->get()->transform;
+ _render_canvas( E->get()->canvas,xform );
+ i++;
+
+ }
+ }
+
+ //capture
+
+ if (p_viewport->queue_capture) {
+
+ rasterizer->capture_viewport(&p_viewport->capture);
+ }
+
+ //restore
+ if ( viewport_rect.x != old_rect.x ||
+ viewport_rect.y != old_rect.y ||
+ viewport_rect.width != old_rect.width ||
+ viewport_rect.height != old_rect.height ) {
+
+ viewport_rect=old_rect;
+
+ rasterizer->set_viewport(viewport_rect);
+ }
+
+
+}
+
+void VisualServerRaster::_draw_viewports() {
+
+ //draw viewports for render targets
+ List<Viewport*> to_disable;
+ for(SelfList<Viewport> *E=viewport_update_list.first();E;E=E->next()) {
+
+ Viewport *vp = E->self();
+ ERR_CONTINUE(!vp);
+ if (
+ vp->render_target_update_mode==RENDER_TARGET_UPDATE_WHEN_VISIBLE &&
+ !vp->rendered_in_prev_frame &&
+ !vp->queue_capture
+ ) {
+
+ continue;
+ }
+
+ rasterizer->set_render_target(vp->render_target);
+ _draw_viewport(vp,0,0,vp->rect.width,vp->rect.height);
+
+ if ( (vp->queue_capture && vp->render_target_update_mode==RENDER_TARGET_UPDATE_DISABLED) || vp->render_target_update_mode==RENDER_TARGET_UPDATE_ONCE) {
+ //was only enabled for capture
+ to_disable.push_back(vp);
+ vp->render_target_update_mode=RENDER_TARGET_UPDATE_DISABLED;
+ }
+
+ }
+
+ rasterizer->set_render_target(RID());
+
+ while(to_disable.size()) {
+ //disable again because it was only for capture
+ viewport_update_list.remove(&to_disable.front()->get()->update_list);
+ to_disable.pop_front();
+ }
+
+ //draw viewports attached to screen
+
+ for(Map<RID,int>::Element *E=screen_viewports.front();E;E=E->next()) {
+
+ Viewport *vp = viewport_owner.get(E->key());
+ ERR_CONTINUE(!vp);
+
+ int window_w = OS::get_singleton()->get_video_mode(E->get()).width;
+ int window_h = OS::get_singleton()->get_video_mode(E->get()).height;
+
+ _draw_viewport(vp,0,0,window_w,window_h);
+ }
+
+
+ //check when a viewport associated to a render target was drawn
+
+ for(SelfList<Viewport> *E=viewport_update_list.first();E;E=E->next()) {
+
+ Viewport *vp = E->self();
+ ERR_CONTINUE(!vp);
+ if (vp->render_target_update_mode!=RENDER_TARGET_UPDATE_WHEN_VISIBLE)
+ continue;
+ vp->rendered_in_prev_frame=rasterizer->render_target_renedered_in_frame(vp->render_target);
+ }
+
+}
+
+
+
+void VisualServerRaster::_draw_cursors_and_margins() {
+
+ int window_w = OS::get_singleton()->get_video_mode().width;
+ int window_h = OS::get_singleton()->get_video_mode().height;
+
+ ViewportRect desired_rect;
+ desired_rect.x = desired_rect.y = 0;
+ desired_rect.width = window_w;
+ desired_rect.height = window_h;
+
+ if ( viewport_rect.x != desired_rect.x ||
+ viewport_rect.y != desired_rect.y ||
+ viewport_rect.width != desired_rect.width ||
+ viewport_rect.height != desired_rect.height ) {
+
+ viewport_rect=desired_rect;
+
+ rasterizer->set_viewport(viewport_rect);
+ }
+
+ rasterizer->canvas_begin();
+ rasterizer->canvas_begin_rect(Matrix32());
+
+ for (int i=0; i<MAX_CURSORS; i++) {
+
+ if (!cursors[i].visible) {
+
+ continue;
+ };
+
+ RID tex = cursors[i].texture?cursors[i].texture:default_cursor_texture;
+ ERR_CONTINUE( !tex );
+ Point2 size(texture_get_width(tex), texture_get_height(tex));
+ rasterizer->canvas_draw_rect(Rect2(cursors[i].pos, size), 0, Rect2(), tex, Color(1, 1, 1, 1));
+ };
+
+ if (black_margin[MARGIN_LEFT])
+ rasterizer->canvas_draw_rect(Rect2(0,0,black_margin[MARGIN_LEFT],window_h),0,Rect2(0,0,1,1),RID(),Color(0,0,0));
+ if (black_margin[MARGIN_RIGHT])
+ rasterizer->canvas_draw_rect(Rect2(window_w-black_margin[MARGIN_RIGHT],0,black_margin[MARGIN_RIGHT],window_h),0,Rect2(0,0,1,1),RID(),Color(0,0,0));
+ if (black_margin[MARGIN_TOP])
+ rasterizer->canvas_draw_rect(Rect2(0,0,window_w,black_margin[MARGIN_TOP]),0,Rect2(0,0,1,1),RID(),Color(0,0,0));
+ if (black_margin[MARGIN_BOTTOM])
+ rasterizer->canvas_draw_rect(Rect2(0,window_h-black_margin[MARGIN_BOTTOM],window_w,black_margin[MARGIN_BOTTOM]),0,Rect2(0,0,1,1),RID(),Color(0,0,0));
+
+ rasterizer->canvas_end_rect();
+};
+
+void VisualServerRaster::flush() {
+ //do none
+}
+
+void VisualServerRaster::draw() {
+ //if (changes)
+ // print_line("changes: "+itos(changes));
+ changes=0;
+ shadows_enabled=GLOBAL_DEF("render/shadows_enabled",true);
+ room_cull_enabled = GLOBAL_DEF("render/room_cull_enabled",true);
+ light_discard_enabled = GLOBAL_DEF("render/light_discard_enabled",true);
+ rasterizer->begin_frame();
+ _draw_viewports();
+ _draw_cursors_and_margins();
+ rasterizer->end_frame();
+ draw_extra_frame=rasterizer->needs_to_draw_next_frame();
+}
+
+bool VisualServerRaster::has_changed() const {
+
+ return changes>0 || draw_extra_frame;
+}
+
+int VisualServerRaster::get_render_info(RenderInfo p_info) {
+
+ return rasterizer->get_render_info(p_info);
+}
+
+bool VisualServerRaster::has_feature(Features p_feature) const {
+
+ return rasterizer->has_feature(p_feature); // lies for now
+}
+
+void VisualServerRaster::set_default_clear_color(const Color& p_color) {
+
+ clear_color=p_color;
+}
+
+void VisualServerRaster::set_boot_image(const Image& p_image, const Color& p_color) {
+
+ if (p_image.empty())
+ return;
+
+ rasterizer->begin_frame();
+
+ int window_w = OS::get_singleton()->get_video_mode(0).width;
+ int window_h = OS::get_singleton()->get_video_mode(0).height;
+ ViewportRect vr;
+ vr.x=0;
+ vr.y=0;
+ vr.width=OS::get_singleton()->get_video_mode(0).width;
+ vr.height=OS::get_singleton()->get_video_mode(0).height;
+ rasterizer->set_viewport(vr);
+ rasterizer->clear_viewport(p_color);
+ rasterizer->canvas_begin();
+ RID texture = texture_create();
+ texture_allocate(texture,p_image.get_width(),p_image.get_height(),p_image.get_format(),TEXTURE_FLAG_FILTER);
+ texture_set_data(texture,p_image);
+ rasterizer->canvas_begin_rect(Matrix32());
+ Rect2 imgrect(0,0,p_image.get_width(),p_image.get_height());
+ Rect2 screenrect=imgrect;
+ screenrect.pos+=((Size2(vr.width,vr.height)-screenrect.size)/2.0).floor();
+ rasterizer->canvas_draw_rect(screenrect,0,imgrect,texture,Color(1,1,1,0));
+ rasterizer->canvas_draw_rect(screenrect,0,imgrect,texture,Color(1,1,1,1));
+ rasterizer->canvas_end_rect();
+
+ rasterizer->end_frame();
+ rasterizer->flush_frame();
+
+ free(texture); // free since it's only one frame that stays there
+
+}
+
+void VisualServerRaster::init() {
+
+ rasterizer->init();
+
+ shadows_enabled=GLOBAL_DEF("render/shadows_enabled",true);
+ //default_scenario = scenario_create();
+ //default_viewport = viewport_create();
+ for(int i=0;i<4;i++)
+ black_margin[i]=0;
+
+ Image img;
+ img.create(default_mouse_cursor_xpm);
+ //img.convert(Image::FORMAT_RGB);
+ default_cursor_texture = texture_create_from_image(img, 0);
+
+ aabb_random_points.resize( GLOBAL_DEF("render/aabb_random_points",16) );
+ for(int i=0;i<aabb_random_points.size();i++)
+ aabb_random_points[i]=Vector3(Math::random(0,1),Math::random(0,1),Math::random(0,1));
+ transformed_aabb_random_points.resize(aabb_random_points.size());
+ changes=0;
+}
+
+void VisualServerRaster::_clean_up_owner(RID_OwnerBase *p_owner,String p_type) {
+
+ List<RID> rids;
+ p_owner->get_owned_list(&rids);
+
+ int lost=0;
+ for(List<RID>::Element *I=rids.front();I;I=I->next()) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ lost++;
+ }
+ free(I->get());
+ }
+
+ if (lost)
+ print_line("VisualServerRaster: WARNING: Lost "+itos(lost)+" RIDs of type "+p_type);
+
+}
+
+void VisualServerRaster::finish() {
+
+
+ free(default_cursor_texture);
+
+ _clean_up_owner( &room_owner,"Room" );
+ _clean_up_owner( &portal_owner,"Portal" );
+
+ _clean_up_owner( &camera_owner,"Camera" );
+ _clean_up_owner( &viewport_owner,"Viewport" );
+
+ _clean_up_owner( &scenario_owner,"Scenario" );
+ _clean_up_owner( &instance_owner,"Instance" );
+
+ _clean_up_owner( &canvas_owner,"Canvas" );
+ _clean_up_owner( &canvas_item_owner,"CanvasItem" );
+
+ rasterizer->finish();
+ octree_allocator.clear();
+
+ if (instance_dependency_map.size()) {
+ print_line("base resources missing "+itos(instance_dependency_map.size()));
+ }
+ ERR_FAIL_COND( instance_dependency_map.size() );
+}
+
+
+RID VisualServerRaster::get_test_cube() {
+
+ if (test_cube.is_valid())
+ return test_cube;
+
+ test_cube=_make_test_cube();
+ return test_cube;
+
+}
+
+
+
+VisualServerRaster::VisualServerRaster(Rasterizer *p_rasterizer) {
+
+ rasterizer=p_rasterizer;
+ instance_update_list=NULL;
+ render_pass=0;
+ clear_color=Color(0.3,0.3,0.3,1.0);
+ OctreeAllocator::allocator=&octree_allocator;
+ draw_extra_frame=false;
+
+}
+
+
+VisualServerRaster::~VisualServerRaster()
+{
+}
+
+
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
new file mode 100644
index 0000000000..f0b3a2d13c
--- /dev/null
+++ b/servers/visual/visual_server_raster.h
@@ -0,0 +1,1112 @@
+/*************************************************************************/
+/* visual_server_raster.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 VISUAL_SERVER_RASTER_H
+#define VISUAL_SERVER_RASTER_H
+
+
+#include "servers/visual_server.h"
+#include "servers/visual/rasterizer.h"
+#include "balloon_allocator.h"
+#include "octree.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+class VisualServerRaster : public VisualServer {
+
+
+ enum {
+
+ MAX_INSTANCE_CULL=8192,
+ MAX_INSTANCE_LIGHTS=4,
+ LIGHT_CACHE_DIRTY=-1,
+ MAX_LIGHTS_CULLED=256,
+ MAX_ROOM_CULL=32,
+ MAX_EXTERIOR_PORTALS=128,
+ INSTANCE_ROOMLESS_MASK=(1<<20)
+
+
+ };
+
+ struct Room {
+
+ bool occlude_exterior;
+ BSP_Tree bounds;
+ Room() { occlude_exterior=true; }
+ };
+
+
+ BalloonAllocator<> octree_allocator;
+
+ struct OctreeAllocator {
+
+ static BalloonAllocator<> *allocator;
+
+ _FORCE_INLINE_ static void *alloc(size_t p_size) { return allocator->alloc(p_size); }
+ _FORCE_INLINE_ static void free(void *p_ptr) { return allocator->free(p_ptr); }
+
+ };
+
+ struct Portal {
+
+ bool enabled;
+ float disable_distance;
+ Color disable_color;
+ float connect_range;
+ Vector<Point2> shape;
+ Rect2 bounds;
+
+ Portal() { enabled=true; disable_distance=50; disable_color=Color(); connect_range=0.8; }
+ };
+
+
+ struct Camera {
+
+ enum Type {
+ PERSPECTIVE,
+ ORTHOGONAL
+ };
+ Type type;
+ float fov;
+ float znear,zfar;
+ float size;
+ uint32_t visible_layers;
+ bool vaspect;
+ RID env;
+
+ Transform transform;
+
+ Camera() {
+
+ visible_layers=0xFFFFFFFF;
+ fov=60;
+ type=PERSPECTIVE;
+ znear=0.1; zfar=100;
+ size=1.0;
+ vaspect=false;
+
+ }
+ };
+
+
+ struct Instance;
+ typedef Set<Instance*,Comparator<Instance*>,OctreeAllocator> InstanceSet;
+ struct Scenario;
+
+ struct Instance {
+
+ enum {
+
+ MAX_LIGHTS=4
+ };
+
+ RID self;
+ OctreeElementID octree_id;
+ Scenario *scenario;
+ bool update;
+ bool update_aabb;
+ Instance *update_next;
+ InstanceType base_type;
+
+ RID base_rid;
+
+ AABB aabb;
+ AABB transformed_aabb;
+ uint32_t object_ID;
+ bool visible;
+ bool cast_shadows;
+ bool receive_shadows;
+ bool visible_in_all_rooms;
+ uint32_t layer_mask;
+ float draw_range_begin;
+ float draw_range_end;
+ float extra_margin;
+
+
+ Rasterizer::InstanceData data;
+
+
+ Set<Instance*> auto_rooms;
+ Set<Instance*> valid_auto_rooms;
+ Instance *room;
+ List<Instance*>::Element *RE;
+ bool exterior;
+
+ uint64_t last_render_pass;
+ uint64_t last_frame_pass;
+
+ uint64_t version; // changes to this, and changes to base increase version
+
+ InstanceSet lights;
+ bool light_cache_dirty;
+
+ struct RoomInfo {
+
+ Transform affine_inverse;
+ Room *room;
+ List<Instance*> owned_geometry_instances;
+ List<Instance*> owned_portal_instances;
+ List<Instance*> owned_room_instances;
+ List<Instance*> owned_light_instances; //not used, but just for the sake of it
+ Set<Instance*> disconnected_child_portals;
+ Set<Instance*> owned_autoroom_geometry;
+ uint64_t last_visited_pass;
+ RoomInfo() { last_visited_pass=0; }
+
+ };
+
+ struct PortalInfo {
+
+ Portal *portal;
+ Set<Instance*> candidate_set;
+ Instance *connected;
+ uint64_t last_visited_pass;
+
+ Plane plane_cache;
+ Vector<Vector3> transformed_point_cache;
+
+
+ PortalInfo() { connected=NULL; last_visited_pass=0;}
+ };
+
+ struct LightInfo {
+
+ RID instance;
+ int light_set_index;
+ uint64_t last_version;
+ uint64_t last_add_pass;
+ List<RID>::Element *D; // directional light in scenario
+ InstanceSet affected;
+ float dtc; //distance to camera, used for sorting
+
+
+ LightInfo() {
+
+ D=NULL;
+ light_set_index=-1;
+ last_add_pass=0;
+ }
+ };
+
+ struct ParticlesInfo {
+
+ RID instance;
+ };
+
+
+ RoomInfo *room_info;
+ LightInfo *light_info;
+ ParticlesInfo *particles_info;
+ PortalInfo * portal_info;
+
+
+ Instance() {
+ octree_id=0;
+ update_next=0;
+ object_ID=0;
+ last_render_pass=0;
+ last_frame_pass=0;
+ light_info=0;
+ particles_info=0;
+ update_next=NULL;
+ update=false;
+ visible=true;
+ cast_shadows=true;
+ receive_shadows=true;
+ data.depth_scale=false;
+ data.billboard=false;
+ data.billboard_y=false;
+ version=1;
+ room_info=NULL;
+ room=NULL;
+ RE=NULL;
+ portal_info=NULL;
+ exterior=false;
+ layer_mask=1;
+ draw_range_begin=0;
+ draw_range_end=0;
+ extra_margin=0;
+ visible_in_all_rooms=false;
+
+ light_cache_dirty=true;
+
+ }
+
+ ~Instance() {
+
+ if (light_info)
+ memdelete(light_info);
+ if (particles_info)
+ memdelete(particles_info);
+ if (room_info)
+ memdelete(room_info);
+ if (portal_info)
+ memdelete(portal_info);
+ };
+ };
+
+ struct _InstanceLightsort {
+
+ bool operator()(const Instance* p_A, const Instance* p_B) const { return p_A->light_info->dtc < p_B->light_info->dtc; }
+ };
+
+ struct Scenario {
+
+
+ ScenarioDebugMode debug;
+ RID self;
+ // well wtf, balloon allocator is slower?
+ typedef ::Octree<Instance,true> Octree;
+
+ Octree octree;
+
+ List<RID> directional_lights;
+ RID environment;
+
+ Instance *dirty_instances;
+
+ Scenario() { dirty_instances=NULL; debug=SCENARIO_DEBUG_DISABLED; }
+ };
+
+
+
+
+
+ struct CanvasItem {
+
+ struct Command {
+
+ enum Type {
+
+ TYPE_LINE,
+ TYPE_RECT,
+ TYPE_STYLE,
+ TYPE_PRIMITIVE,
+ TYPE_POLYGON,
+ TYPE_POLYGON_PTR,
+ TYPE_CIRCLE,
+ TYPE_TRANSFORM,
+ TYPE_BLEND_MODE,
+ TYPE_CLIP_IGNORE,
+ };
+
+ Type type;
+ };
+
+ struct CommandLine : public Command {
+
+ Point2 from,to;
+ Color color;
+ float width;
+ CommandLine() { type = TYPE_LINE; }
+ };
+
+ struct CommandRect : public Command {
+
+ Rect2 rect;
+ RID texture;
+ Color modulate;
+ Rect2 source;
+ uint8_t flags;
+
+ CommandRect() { flags=0; type = TYPE_RECT; }
+ };
+
+ struct CommandStyle : public Command {
+
+ Rect2 rect;
+ RID texture;
+ float margin[4];
+ float draw_center;
+ Color color;
+ CommandStyle() { draw_center=true; type = TYPE_STYLE; }
+ };
+
+ struct CommandPrimitive : public Command {
+
+ Vector<Point2> points;
+ Vector<Point2> uvs;
+ Vector<Color> colors;
+ RID texture;
+ float width;
+
+ CommandPrimitive() { type = TYPE_PRIMITIVE; width=1;}
+ };
+
+ struct CommandPolygon : public Command {
+
+ Vector<int> indices;
+ Vector<Point2> points;
+ Vector<Point2> uvs;
+ Vector<Color> colors;
+ RID texture;
+ int count;
+
+ CommandPolygon() { type = TYPE_POLYGON; count = 0; }
+ };
+
+ struct CommandPolygonPtr : public Command {
+
+ const int* indices;
+ const Point2* points;
+ const Point2* uvs;
+ const Color* colors;
+ RID texture;
+ int count;
+
+ CommandPolygonPtr() { type = TYPE_POLYGON_PTR; count = 0; }
+ };
+
+ struct CommandCircle : public Command {
+
+ Point2 pos;
+ float radius;
+ Color color;
+ CommandCircle() { type = TYPE_CIRCLE; }
+ };
+
+ struct CommandTransform : public Command {
+
+ Matrix32 xform;
+ CommandTransform() { type = TYPE_TRANSFORM; }
+ };
+
+ struct CommandBlendMode : public Command {
+
+ MaterialBlendMode blend_mode;
+ CommandBlendMode() { type = TYPE_BLEND_MODE; blend_mode = MATERIAL_BLEND_MODE_MIX; };
+ };
+ struct CommandClipIgnore : public Command {
+
+ bool ignore;
+ CommandClipIgnore() { type = TYPE_CLIP_IGNORE; ignore=false; };
+ };
+
+ RID parent; // canvas it belongs to
+ List<CanvasItem*>::Element *E;
+ Matrix32 xform;
+ bool clip;
+ bool visible;
+ bool ontop;
+ float opacity;
+ float self_opacity;
+ MaterialBlendMode blend_mode;
+ RID viewport;
+
+ mutable bool custom_rect;
+ mutable bool rect_dirty;
+ mutable Rect2 rect;
+
+ Vector<Command*> commands;
+ Vector<CanvasItem*> child_items;
+
+ const Rect2& get_rect() const;
+ void clear() { for (int i=0;i<commands.size();i++) memdelete( commands[i] ); commands.clear(); clip=false; rect_dirty=true;};
+ CanvasItem() { clip=false; E=NULL; opacity=1; self_opacity=1; blend_mode=MATERIAL_BLEND_MODE_MIX; visible=true; rect_dirty=true; custom_rect=false; ontop=true; }
+ ~CanvasItem() { clear(); }
+ };
+
+
+
+ struct Canvas {
+
+ Set<RID> viewports;
+ struct ChildItem {
+
+ Point2 mirror;
+ CanvasItem *item;
+ };
+
+
+ Vector<ChildItem> child_items;
+
+ int find_item(CanvasItem *p_item) {
+ for(int i=0;i<child_items.size();i++) {
+ if (child_items[i].item==p_item)
+ return i;
+ }
+ return -1;
+ }
+ void erase_item(CanvasItem *p_item) {
+ int idx=find_item(p_item);
+ if (idx>=0)
+ child_items.remove(idx);
+ }
+
+ Canvas() { }
+
+ };
+
+
+
+ struct Viewport {
+
+ RID self;
+ RID parent;
+
+ VisualServer::ViewportRect rect;
+ RID camera;
+ RID scenario;
+ RID viewport_data;
+
+ RenderTargetUpdateMode render_target_update_mode;
+ RID render_target;
+ RID render_target_texture;
+
+ bool hide_scenario;
+ bool hide_canvas;
+ bool transparent_bg;
+
+ bool queue_capture;
+ Image capture;
+
+ bool rendered_in_prev_frame;
+
+ struct CanvasKey {
+
+ int layer;
+ RID canvas;
+ bool operator<(const CanvasKey& p_canvas) const { if (layer==p_canvas.layer) return canvas < p_canvas.canvas; return layer<p_canvas.layer; }
+ CanvasKey() { layer=0; }
+ CanvasKey(const RID& p_canvas, int p_layer) { canvas=p_canvas; layer=p_layer; }
+ };
+
+ struct CanvasData {
+
+ Canvas *canvas;
+ Matrix32 transform;
+ int layer;
+ };
+
+ Matrix32 global_transform;
+
+ Map<RID,CanvasData> canvas_map;
+
+ SelfList<Viewport> update_list;
+
+ Viewport() : update_list(this) { transparent_bg=false; render_target_update_mode=RENDER_TARGET_UPDATE_WHEN_VISIBLE; queue_capture=false; rendered_in_prev_frame=false;}
+ };
+
+ SelfList<Viewport>::List viewport_update_list;
+
+ Map<RID,int> screen_viewports;
+
+ struct CullRange {
+
+ Plane nearp;
+ float min,max;
+ float z_near,z_far;
+
+ void add_aabb(const AABB& p_aabb) {
+
+
+ }
+ };
+
+ struct Cursor {
+
+ Point2 pos;
+ float rot;
+ RID texture;
+ Point2 center;
+ bool visible;
+ Cursor() {
+
+ rot = 0;
+ visible = false;
+ };
+ };
+
+ Rect2 canvas_clip;
+ Color clear_color;
+ Cursor cursors[MAX_CURSORS];
+ RID default_cursor_texture;
+
+ static void* instance_pair(void *p_self, OctreeElementID,Instance *p_A,int, OctreeElementID,Instance *p_B,int);
+ static void instance_unpair(void *p_self, OctreeElementID,Instance *p_A,int, OctreeElementID,Instance *p_B,int,void*);
+
+ Instance *instance_cull_result[MAX_INSTANCE_CULL];
+ Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
+ Instance *light_cull_result[MAX_LIGHTS_CULLED];
+ int light_cull_count;
+
+ Instance *exterior_portal_cull_result[MAX_EXTERIOR_PORTALS];
+ int exterior_portal_cull_count;
+ bool exterior_visited;
+
+ Instance *room_cull_result[MAX_ROOM_CULL];
+ int room_cull_count;
+ bool room_cull_enabled;
+ bool light_discard_enabled;
+ bool shadows_enabled;
+ int black_margin[4];
+
+ Vector<Vector3> aabb_random_points;
+ Vector<Vector3> transformed_aabb_random_points;
+
+ void _instance_validate_autorooms(Instance *p_geometry);
+
+ void _portal_disconnect(Instance *p_portal,bool p_cleanup=false);
+ void _portal_attempt_connect(Instance *p_portal);
+ void _dependency_queue_update(RID p_rid,bool p_update_aabb=false);
+ void _instance_queue_update(Instance *p_instance,bool p_update_aabb=false);
+ void _update_instances();
+ void _update_instance_aabb(Instance *p_instance);
+ void _update_instance(Instance *p_instance);
+ void _free_attached_instances(RID p_rid,bool p_free_scenario=false);
+ void _clean_up_owner(RID_OwnerBase *p_owner,String p_type);
+
+ Instance *instance_update_list;
+
+ //RID default_scenario;
+ //RID default_viewport;
+
+ RID test_cube;
+
+
+ mutable RID_Owner<Room> room_owner;
+ mutable RID_Owner<Portal> portal_owner;
+
+ mutable RID_Owner<Camera> camera_owner;
+ mutable RID_Owner<Viewport> viewport_owner;
+
+ mutable RID_Owner<Scenario> scenario_owner;
+ mutable RID_Owner<Instance> instance_owner;
+
+ mutable RID_Owner<Canvas> canvas_owner;
+ mutable RID_Owner<CanvasItem> canvas_item_owner;
+
+ Map< RID, Set<RID> > instance_dependency_map;
+
+
+ ViewportRect viewport_rect;
+ _FORCE_INLINE_ void _instance_draw(Instance *p_instance);
+
+ bool _test_portal_cull(Camera *p_camera, Instance *p_portal_from, Instance *p_portal_to);
+ void _cull_portal(Camera *p_camera, Instance *p_portal,Instance *p_from_portal);
+ void _cull_room(Camera *p_camera, Instance *p_room,Instance *p_from_portal=NULL);
+ void _render_camera(Viewport *p_viewport,Camera *p_camera, Scenario *p_scenario);
+ void _render_canvas_item(CanvasItem *p_canvas_item,const Matrix32& p_transform,const Rect2& p_clip_rect,float p_opacity);
+ void _render_canvas(Canvas *p_canvas,const Matrix32 &p_transform);
+ Vector<Vector3> _camera_generate_endpoints(Instance *p_light,Camera *p_camera,float p_range_min, float p_range_max);
+ Vector<Plane> _camera_generate_orthogonal_planes(Instance *p_light,Camera *p_camera,float p_range_min, float p_range_max);
+
+ void _light_instance_update_lispsm_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range);
+ void _light_instance_update_pssm_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range);
+
+ void _light_instance_update_shadow(Instance *p_light,Scenario *p_scenario,Camera *p_camera,const CullRange& p_cull_range);
+
+ uint64_t render_pass;
+ int changes;
+ bool draw_extra_frame;
+
+ void _draw_viewport(Viewport *p_viewport,int p_ofs_x, int p_ofs_y,int p_parent_w,int p_parent_h);
+ void _draw_viewports();
+ void _draw_cursors_and_margins();
+
+
+ Rasterizer *rasterizer;
+public:
+
+ virtual RID texture_create();
+ virtual void texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags=TEXTURE_FLAGS_DEFAULT);
+ virtual void texture_set_data(RID p_texture,const Image& p_image,CubeMapSide p_cube_side=CUBEMAP_LEFT);
+ virtual Image texture_get_data(RID p_texture,CubeMapSide p_cube_side=CUBEMAP_LEFT) const;
+ virtual void texture_set_flags(RID p_texture,uint32_t p_flags) ;
+ virtual uint32_t texture_get_flags(RID p_texture) const;
+ virtual Image::Format texture_get_format(RID p_texture) const;
+ virtual uint32_t texture_get_width(RID p_texture) const;
+ virtual uint32_t texture_get_height(RID p_texture) const;
+ virtual void texture_set_size_override(RID p_texture,int p_width, int p_height);
+ virtual bool texture_can_stream(RID p_texture) const;
+ virtual void texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const;
+
+
+ /* SHADER API */
+
+ virtual RID shader_create(ShaderMode p_mode=SHADER_MATERIAL);
+
+ virtual void shader_set_mode(RID p_shader,ShaderMode p_mode);
+ virtual ShaderMode shader_get_mode(RID p_shader) const;
+
+ virtual void shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs=0,int p_fragment_ofs=0);
+ virtual String shader_get_vertex_code(RID p_shader) const;
+ virtual String shader_get_fragment_code(RID p_shader) const;
+
+ virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const;
+
+ /* COMMON MATERIAL API */
+
+ virtual RID material_create();
+
+ virtual void material_set_shader(RID p_shader_material, RID p_shader);
+ virtual RID material_get_shader(RID p_shader_material) const;
+
+ virtual void material_set_param(RID p_material, const StringName& p_param, const Variant& p_value);
+ virtual Variant material_get_param(RID p_material, const StringName& p_param) const;
+
+ virtual void material_set_flag(RID p_material, MaterialFlag p_flag,bool p_enabled);
+ virtual bool material_get_flag(RID p_material,MaterialFlag p_flag) const;
+
+ virtual void material_set_hint(RID p_material, MaterialHint p_hint,bool p_enabled);
+ virtual bool material_get_hint(RID p_material,MaterialHint p_hint) const;
+
+ virtual void material_set_shade_model(RID p_material, MaterialShadeModel p_model);
+ virtual MaterialShadeModel material_get_shade_model(RID p_material) const;
+
+ virtual void material_set_blend_mode(RID p_material,MaterialBlendMode p_mode);
+ virtual MaterialBlendMode material_get_blend_mode(RID p_material) const;
+
+ virtual void material_set_line_width(RID p_material,float p_line_width);
+ virtual float material_get_line_width(RID p_material) const;
+
+ /* FIXED MATERIAL */
+
+
+ virtual RID fixed_material_create();
+
+ virtual void fixed_material_set_flag(RID p_material, FixedMaterialFlags p_flag, bool p_enabled);
+ virtual bool fixed_material_get_flag(RID p_material, FixedMaterialFlags p_flag) const;
+
+ virtual void fixed_material_set_param(RID p_material, FixedMaterialParam p_parameter, const Variant& p_value);
+ virtual Variant fixed_material_get_param(RID p_material,FixedMaterialParam p_parameter) const;
+
+ virtual void fixed_material_set_texture(RID p_material,FixedMaterialParam p_parameter, RID p_texture);
+ virtual RID fixed_material_get_texture(RID p_material,FixedMaterialParam p_parameter) const;
+
+ virtual void fixed_material_set_detail_blend_mode(RID p_material,MaterialBlendMode p_mode);
+ virtual MaterialBlendMode fixed_material_get_detail_blend_mode(RID p_material) const;
+
+
+ virtual void fixed_material_set_texcoord_mode(RID p_material,FixedMaterialParam p_parameter, FixedMaterialTexCoordMode p_mode);
+ virtual FixedMaterialTexCoordMode fixed_material_get_texcoord_mode(RID p_material,FixedMaterialParam p_parameter) const;
+
+ virtual void fixed_material_set_uv_transform(RID p_material,const Transform& p_transform);
+ virtual Transform fixed_material_get_uv_transform(RID p_material) const;
+
+ virtual void fixed_material_set_point_size(RID p_material,float p_size);
+ virtual float fixed_material_get_point_size(RID p_material) const;
+
+ /* SURFACE API */
+ virtual RID mesh_create();
+
+ virtual void mesh_set_morph_target_count(RID p_mesh,int p_amount);
+ virtual int mesh_get_morph_target_count(RID p_mesh) const;
+
+ virtual void mesh_set_morph_target_mode(RID p_mesh,MorphTargetMode p_mode);
+ virtual MorphTargetMode mesh_get_morph_target_mode(RID p_mesh) const;
+
+ virtual void mesh_add_custom_surface(RID p_mesh,const Variant& p_dat); //this is used by each platform in a different way
+
+ virtual void mesh_add_surface(RID p_mesh,PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes=Array(),bool p_alpha_sort=false);
+ virtual Array mesh_get_surface_arrays(RID p_mesh,int p_surface) const;
+ virtual Array mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const;
+
+ virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned=false);
+ virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const;
+
+ virtual int mesh_surface_get_array_len(RID p_mesh, int p_surface) const;
+ virtual int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const;
+ virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const;
+ virtual PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const;
+
+ virtual void mesh_remove_surface(RID p_mesh,int p_index);
+ virtual int mesh_get_surface_count(RID p_mesh) const;
+
+
+ /* MULTIMESH API */
+
+ virtual RID multimesh_create();
+ virtual void multimesh_set_instance_count(RID p_multimesh,int p_count);
+ virtual int multimesh_get_instance_count(RID p_multimesh) const;
+
+ virtual void multimesh_set_mesh(RID p_multimesh,RID p_mesh);
+ virtual void multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb);
+ virtual void multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform);
+ virtual void multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color);
+
+ virtual RID multimesh_get_mesh(RID p_multimesh) const;
+ virtual AABB multimesh_get_aabb(RID p_multimesh,const AABB& p_aabb) const;
+ virtual Transform multimesh_instance_get_transform(RID p_multimesh,int p_index) const;
+ virtual Color multimesh_instance_get_color(RID p_multimesh,int p_index) const;
+
+ virtual void multimesh_set_visible_instances(RID p_multimesh,int p_visible);
+ virtual int multimesh_get_visible_instances(RID p_multimesh) const;
+
+
+ /* PARTICLES API */
+
+ virtual RID particles_create();
+
+ virtual void particles_set_amount(RID p_particles, int p_amount);
+ virtual int particles_get_amount(RID p_particles) const;
+
+ virtual void particles_set_emitting(RID p_particles, bool p_emitting);
+ virtual bool particles_is_emitting(RID p_particles) const;
+
+ virtual void particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility);
+ virtual AABB particles_get_visibility_aabb(RID p_particles) const;
+
+ virtual void particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents);
+ virtual Vector3 particles_get_emission_half_extents(RID p_particles) const;
+
+ virtual void particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity);
+ virtual Vector3 particles_get_emission_base_velocity(RID p_particles) const;
+
+ virtual void particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points);
+ virtual DVector<Vector3> particles_get_emission_points(RID p_particles) const;
+
+ virtual void particles_set_gravity_normal(RID p_particles, const Vector3& p_normal);
+ virtual Vector3 particles_get_gravity_normal(RID p_particles) const;
+
+ virtual void particles_set_variable(RID p_particles, ParticleVariable p_variable,float p_value);
+ virtual float particles_get_variable(RID p_particles, ParticleVariable p_variable) const;
+
+ virtual void particles_set_randomness(RID p_particles, ParticleVariable p_variable,float p_randomness);
+ virtual float particles_get_randomness(RID p_particles, ParticleVariable p_variable) const;
+
+ virtual void particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos);
+ virtual float particles_get_color_phase_pos(RID p_particles, int p_phase) const;
+
+ virtual void particles_set_color_phases(RID p_particles, int p_phases);
+ virtual int particles_get_color_phases(RID p_particles) const;
+
+ virtual void particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color);
+ virtual Color particles_get_color_phase_color(RID p_particles, int p_phase) const;
+
+ virtual void particles_set_attractors(RID p_particles, int p_attractors);
+ virtual int particles_get_attractors(RID p_particles) const;
+
+ virtual void particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos);
+ virtual Vector3 particles_get_attractor_pos(RID p_particles,int p_attractor) const;
+
+ virtual void particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force);
+ virtual float particles_get_attractor_strength(RID p_particles,int p_attractor) const;
+
+ virtual void particles_set_material(RID p_particles, RID p_material,bool p_owned=false);
+ virtual RID particles_get_material(RID p_particles) const;
+
+ virtual void particles_set_height_from_velocity(RID p_particles, bool p_enable);
+ virtual bool particles_has_height_from_velocity(RID p_particles) const;
+
+ virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable);
+ virtual bool particles_is_using_local_coordinates(RID p_particles) const;
+
+
+ /* Light API */
+
+ virtual RID light_create(LightType p_type);
+ virtual LightType light_get_type(RID p_light) const;
+
+ virtual void light_set_color(RID p_light,LightColor p_type, const Color& p_color);
+ virtual Color light_get_color(RID p_light,LightColor p_type) const;
+
+
+ virtual void light_set_shadow(RID p_light,bool p_enabled);
+ virtual bool light_has_shadow(RID p_light) const;
+
+ virtual void light_set_volumetric(RID p_light,bool p_enabled);
+ virtual bool light_is_volumetric(RID p_light) const;
+
+ virtual void light_set_projector(RID p_light,RID p_texture);
+ virtual RID light_get_projector(RID p_light) const;
+
+ virtual void light_set_param(RID p_light, LightParam p_var, float p_value);
+ virtual float light_get_param(RID p_light, LightParam p_var) const;
+
+ virtual void light_set_operator(RID p_light,LightOp p_op);
+ virtual LightOp light_get_operator(RID p_light) const;
+
+ virtual void light_omni_set_shadow_mode(RID p_light,LightOmniShadowMode p_mode);
+ virtual LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) const;
+
+ virtual void light_directional_set_shadow_mode(RID p_light,LightDirectionalShadowMode p_mode);
+ virtual LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) const;
+ virtual void light_directional_set_shadow_param(RID p_light,LightDirectionalShadowParam p_param, float p_value);
+ virtual float light_directional_get_shadow_param(RID p_light,LightDirectionalShadowParam p_param) const;
+
+
+ /* SKELETON API */
+
+ virtual RID skeleton_create();
+ virtual void skeleton_resize(RID p_skeleton,int p_bones);
+ virtual int skeleton_get_bone_count(RID p_skeleton) const;
+ virtual void skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform);
+ virtual Transform skeleton_bone_get_transform(RID p_skeleton,int p_bone);
+
+ /* ROOM API */
+
+ virtual RID room_create();
+ virtual void room_set_bounds(RID p_room, const BSP_Tree& p_bounds);
+ virtual BSP_Tree room_get_bounds(RID p_room) const;
+
+ /* PORTAL API */
+
+ virtual RID portal_create();
+ virtual void portal_set_shape(RID p_portal, const Vector<Point2>& p_shape);
+ virtual Vector<Point2> portal_get_shape(RID p_portal) const;
+ virtual void portal_set_enabled(RID p_portal, bool p_enabled);
+ virtual bool portal_is_enabled(RID p_portal) const;
+ virtual void portal_set_disable_distance(RID p_portal, float p_distance);
+ virtual float portal_get_disable_distance(RID p_portal) const;
+ virtual void portal_set_disabled_color(RID p_portal, const Color& p_color);
+ virtual Color portal_get_disabled_color(RID p_portal) const;
+ virtual void portal_set_connect_range(RID p_portal, float p_range);
+ virtual float portal_get_connect_range(RID p_portal) const;
+
+
+
+ /* CAMERA API */
+
+ virtual RID camera_create();
+ virtual void camera_set_perspective(RID p_camera,float p_fovy_degrees, float p_z_near, float p_z_far);
+ virtual void camera_set_orthogonal(RID p_camera,float p_size, float p_z_near, float p_z_far);
+ virtual void camera_set_transform(RID p_camera,const Transform& p_transform);
+
+ virtual void camera_set_visible_layers(RID p_camera,uint32_t p_layers);
+ virtual uint32_t camera_get_visible_layers(RID p_camera) const;
+
+ virtual void camera_set_environment(RID p_camera,RID p_env);
+ virtual RID camera_get_environment(RID p_camera) const;
+
+ virtual void camera_set_use_vertical_aspect(RID p_camera,bool p_enable);
+ virtual bool camera_is_using_vertical_aspect(RID p_camera,bool p_enable) const;
+
+ /* VIEWPORT API */
+
+ virtual RID viewport_create();
+
+ virtual void viewport_attach_to_screen(RID p_viewport,int p_screen=0);
+ virtual void viewport_detach(RID p_viewport);
+
+ virtual void viewport_set_as_render_target(RID p_viewport,bool p_enable);
+ virtual void viewport_set_render_target_update_mode(RID p_viewport,RenderTargetUpdateMode p_mode);
+ virtual RenderTargetUpdateMode viewport_get_render_target_update_mode(RID p_viewport) const;
+ virtual RID viewport_get_render_target_texture(RID p_viewport) const;
+
+ virtual void viewport_queue_screen_capture(RID p_viewport);
+ virtual Image viewport_get_screen_capture(RID p_viewport) const;
+
+ virtual void viewport_set_rect(RID p_viewport,const ViewportRect& p_rect);
+ virtual ViewportRect viewport_get_rect(RID p_viewport) const;
+
+ virtual void viewport_set_hide_scenario(RID p_viewport,bool p_hide);
+ virtual void viewport_set_hide_canvas(RID p_viewport,bool p_hide);
+ virtual void viewport_attach_camera(RID p_viewport,RID p_camera);
+ virtual void viewport_set_scenario(RID p_viewport,RID p_scenario);
+
+ virtual RID viewport_get_attached_camera(RID p_viewport) const;
+ virtual RID viewport_get_scenario(RID p_viewport) const;
+ virtual void viewport_attach_canvas(RID p_viewport,RID p_canvas);
+ virtual void viewport_remove_canvas(RID p_viewport,RID p_canvas);
+ virtual void viewport_set_canvas_transform(RID p_viewport,RID p_canvas,const Matrix32& p_offset);
+ virtual Matrix32 viewport_get_canvas_transform(RID p_viewport,RID p_canvas) const;
+ virtual void viewport_set_global_canvas_transform(RID p_viewport,const Matrix32& p_transform);
+ virtual Matrix32 viewport_get_global_canvas_transform(RID p_viewport) const;
+ virtual void viewport_set_canvas_layer(RID p_viewport,RID p_canvas,int p_layer);
+ virtual void viewport_set_transparent_background(RID p_viewport,bool p_enabled);
+ virtual bool viewport_has_transparent_background(RID p_viewport) const;
+
+
+ /* ENVIRONMENT API */
+
+ virtual RID environment_create();
+
+ virtual void environment_set_background(RID p_env,EnvironmentBG p_bg);
+ virtual EnvironmentBG environment_get_background(RID p_env) const;
+
+ virtual void environment_set_background_param(RID p_env,EnvironmentBGParam p_param, const Variant& p_value);
+ virtual Variant environment_get_background_param(RID p_env,EnvironmentBGParam p_param) const;
+
+ virtual void environment_set_enable_fx(RID p_env,EnvironmentFx p_effect,bool p_enabled);
+ virtual bool environment_is_fx_enabled(RID p_env,EnvironmentFx p_effect) const;
+
+
+ virtual void environment_fx_set_param(RID p_env,EnvironmentFxParam p_effect,const Variant& p_param);
+ virtual Variant environment_fx_get_param(RID p_env,EnvironmentFxParam p_effect) const;
+
+
+ /* SCENARIO API */
+
+ virtual RID scenario_create();
+
+ virtual void scenario_set_debug(RID p_scenario,ScenarioDebugMode p_debug_mode);
+ virtual void scenario_set_environment(RID p_scenario, RID p_environment);
+ virtual RID scenario_get_environment(RID p_scenario, RID p_environment) const;
+
+
+ /* INSTANCING API */
+
+ virtual RID instance_create();
+
+ virtual void instance_set_base(RID p_instance, RID p_base);
+ virtual RID instance_get_base(RID p_instance) const;
+
+ virtual void instance_set_scenario(RID p_instance, RID p_scenario);
+ virtual RID instance_get_scenario(RID p_instance) const;
+
+ virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask);
+ virtual uint32_t instance_get_layer_mask(RID p_instance) const;
+
+ virtual AABB instance_get_base_aabb(RID p_instance) const;
+
+ virtual void instance_attach_object_instance_ID(RID p_instance,uint32_t p_ID);
+ virtual uint32_t instance_get_object_instance_ID(RID p_instance) const;
+
+ virtual void instance_attach_skeleton(RID p_instance,RID p_skeleton);
+ virtual RID instance_get_skeleton(RID p_instance) const;
+
+ virtual void instance_set_morph_target_weight(RID p_instance,int p_shape, float p_weight);
+ virtual float instance_get_morph_target_weight(RID p_instance,int p_shape) const;
+
+ virtual void instance_set_transform(RID p_instance, const Transform& p_transform);
+ virtual Transform instance_get_transform(RID p_instance) const;
+
+ virtual void instance_set_exterior( RID p_instance, bool p_enabled );
+ virtual bool instance_is_exterior( RID p_instance) const;
+
+ virtual void instance_set_room( RID p_instance, RID p_room );
+ virtual RID instance_get_room( RID p_instance ) const ;
+
+ virtual void instance_set_extra_visibility_margin( RID p_instance, real_t p_margin );
+ virtual real_t instance_get_extra_visibility_margin( RID p_instance ) const;
+
+ virtual Vector<RID> instances_cull_aabb(const AABB& p_aabb, RID p_scenario=RID()) const;
+ virtual Vector<RID> instances_cull_ray(const Vector3& p_from, const Vector3& p_to, RID p_scenario=RID()) const;
+ virtual Vector<RID> instances_cull_convex(const Vector<Plane>& p_convex, RID p_scenario=RID()) const;
+
+ virtual void instance_geometry_set_flag(RID p_instance,InstanceFlags p_flags,bool p_enabled);
+ virtual bool instance_geometry_get_flag(RID p_instance,InstanceFlags p_flags) const;
+
+ virtual void instance_geometry_set_material_override(RID p_instance, RID p_material);
+ virtual RID instance_geometry_get_material_override(RID p_instance) const;
+
+ virtual void instance_geometry_set_draw_range(RID p_instance,float p_min,float p_max);
+ virtual float instance_geometry_get_draw_range_max(RID p_instance) const;
+ virtual float instance_geometry_get_draw_range_min(RID p_instance) const;
+
+ /* CANVAS (2D) */
+
+ virtual RID canvas_create();
+ virtual void canvas_set_item_mirroring(RID p_canvas,RID p_item,const Point2& p_mirroring);
+ virtual Point2 canvas_get_item_mirroring(RID p_canvas,RID p_item) const;
+
+ virtual RID canvas_item_create();
+
+ virtual void canvas_item_set_parent(RID p_item,RID p_parent_item);
+ virtual RID canvas_item_get_parent(RID p_canvas_item) const;
+
+ virtual void canvas_item_set_visible(RID p_item,bool p_visible);
+ virtual bool canvas_item_is_visible(RID p_item) const;
+
+ virtual void canvas_item_set_blend_mode(RID p_canvas_item,MaterialBlendMode p_blend);
+
+
+ //virtual void canvas_item_set_rect(RID p_item, const Rect2& p_rect);
+ virtual void canvas_item_set_transform(RID p_item, const Matrix32& p_transform);
+ virtual void canvas_item_set_clip(RID p_item, bool p_clip);
+ virtual void canvas_item_set_custom_rect(RID p_item, bool p_custom_rect,const Rect2& p_rect=Rect2());
+ virtual void canvas_item_set_opacity(RID p_item, float p_opacity);
+ virtual float canvas_item_get_opacity(RID p_item, float p_opacity) const;
+ virtual void canvas_item_set_on_top(RID p_item, bool p_on_top);
+ virtual bool canvas_item_is_on_top(RID p_item) const;
+
+ virtual void canvas_item_set_self_opacity(RID p_item, float p_self_opacity);
+ virtual float canvas_item_get_self_opacity(RID p_item, float p_self_opacity) const;
+
+ virtual void canvas_item_attach_viewport(RID p_item, RID p_viewport);
+
+ virtual void canvas_item_add_line(RID p_item, const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width=1.0);
+ virtual void canvas_item_add_rect(RID p_item, const Rect2& p_rect, const Color& p_color);
+ virtual void canvas_item_add_circle(RID p_item, const Point2& p_pos, float p_radius,const Color& p_color);
+ virtual void canvas_item_add_texture_rect(RID p_item, const Rect2& p_rect, RID p_texture,bool p_tile=false,const Color& p_modulate=Color(1,1,1));
+ virtual void canvas_item_add_texture_rect_region(RID p_item, const Rect2& p_rect, RID p_texture,const Rect2& p_src_rect,const Color& p_modulate=Color(1,1,1));
+ virtual void canvas_item_add_style_box(RID p_item, const Rect2& p_rect, RID p_texture,const Vector2& p_topleft, const Vector2& p_bottomright, bool p_draw_center=true,const Color& p_modulate=Color(1,1,1));
+ virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width=1.0);
+ virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs=Vector<Point2>(), RID p_texture=RID());
+ virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int>& p_indices, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs=Vector<Point2>(), RID p_texture=RID(), int p_count=-1);
+ virtual void canvas_item_add_triangle_array_ptr(RID p_item, int p_count, const int* p_indices, const Point2* p_points, const Color* p_colors,const Point2* p_uvs=NULL, RID p_texture=RID());
+ virtual void canvas_item_add_set_transform(RID p_item,const Matrix32& p_transform);
+ virtual void canvas_item_add_set_blend_mode(RID p_item, MaterialBlendMode p_blend);
+ virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
+
+ virtual void canvas_item_clear(RID p_item);
+ virtual void canvas_item_raise(RID p_item);
+
+ /* CURSOR */
+ virtual void cursor_set_rotation(float p_rotation, int p_cursor = 0); // radians
+ virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset, int p_cursor=0);
+ virtual void cursor_set_visible(bool p_visible, int p_cursor = 0);
+ virtual void cursor_set_pos(const Point2& p_pos, int p_cursor = 0);
+
+ /* BLACK BARS */
+
+ virtual void black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom);
+
+ /* FREE */
+
+ virtual void free( RID p_rid );
+
+ /* CUSTOM SHADE MODEL */
+
+ virtual void custom_shade_model_set_shader(int p_model, RID p_shader);
+ virtual RID custom_shade_model_get_shader(int p_model) const;
+ virtual void custom_shade_model_set_name(int p_model, const String& p_name);
+ virtual String custom_shade_model_get_name(int p_model) const;
+ virtual void custom_shade_model_set_param_info(int p_model, const List<PropertyInfo>& p_info);
+ virtual void custom_shade_model_get_param_info(int p_model, List<PropertyInfo>* p_info) const;
+
+ /* EVENT QUEUING */
+
+ virtual void draw();
+ virtual void flush();
+
+ virtual void init();
+ virtual void finish();
+
+ virtual bool has_changed() const;
+
+ /* RENDER INFO */
+
+ virtual int get_render_info(RenderInfo p_info);
+ virtual bool has_feature(Features p_feature) const;
+
+ RID get_test_cube();
+
+ virtual void set_boot_image(const Image& p_image, const Color& p_color);
+ virtual void set_default_clear_color(const Color& p_color);
+
+ VisualServerRaster(Rasterizer *p_rasterizer);
+ ~VisualServerRaster();
+
+};
+
+#endif
diff --git a/servers/visual/visual_server_wrap_mt.cpp b/servers/visual/visual_server_wrap_mt.cpp
new file mode 100644
index 0000000000..85f7b8c3cb
--- /dev/null
+++ b/servers/visual/visual_server_wrap_mt.cpp
@@ -0,0 +1,199 @@
+/*************************************************************************/
+/* visual_server_wrap_mt.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "visual_server_wrap_mt.h"
+#include "os/os.h"
+
+void VisualServerWrapMT::thread_exit() {
+
+ exit=true;
+}
+
+void VisualServerWrapMT::thread_draw() {
+
+
+ draw_mutex->lock();
+
+ draw_pending--;
+ bool draw=(draw_pending==0);// only draw when no more flushes are pending
+
+ draw_mutex->unlock();
+
+ if (draw) {
+
+ visual_server->draw();
+ }
+
+}
+
+void VisualServerWrapMT::thread_flush() {
+
+
+ draw_mutex->lock();
+
+ draw_pending--;
+
+ draw_mutex->unlock();
+
+}
+
+
+
+void VisualServerWrapMT::_thread_callback(void *_instance) {
+
+ VisualServerWrapMT *vsmt = reinterpret_cast<VisualServerWrapMT*>(_instance);
+
+
+ vsmt->thread_loop();
+}
+
+void VisualServerWrapMT::thread_loop() {
+
+ server_thread=Thread::get_caller_ID();
+
+ OS::get_singleton()->make_rendering_thread();
+
+ visual_server->init();
+
+ exit=false;
+ draw_thread_up=true;
+ while(!exit) {
+ // flush commands one by one, until exit is requested
+ command_queue.wait_and_flush_one();
+ }
+
+ command_queue.flush_all(); // flush all
+
+ visual_server->finish();
+
+}
+
+
+/* EVENT QUEUING */
+
+void VisualServerWrapMT::flush() {
+
+ if (create_thread) {
+
+ ERR_FAIL_COND(!draw_mutex);
+ draw_mutex->lock();
+ draw_pending++; //cambiar por un saferefcount
+ draw_mutex->unlock();
+
+ command_queue.push( this, &VisualServerWrapMT::thread_flush);
+ } else {
+
+ command_queue.flush_all(); //flush all pending from other threads
+ }
+
+}
+
+void VisualServerWrapMT::draw() {
+
+
+ if (create_thread) {
+
+ ERR_FAIL_COND(!draw_mutex);
+ draw_mutex->lock();
+ draw_pending++; //cambiar por un saferefcount
+ draw_mutex->unlock();
+
+ command_queue.push( this, &VisualServerWrapMT::thread_draw);
+ } else {
+
+ command_queue.flush_all(); //flush all pending from other threads
+ visual_server->draw();
+ }
+}
+
+
+void VisualServerWrapMT::init() {
+
+ if (create_thread) {
+
+ draw_mutex = Mutex::create();
+ print_line("CREATING RENDER THREAD");
+ OS::get_singleton()->release_rendering_thread();
+ if (create_thread) {
+ thread = Thread::create( _thread_callback, this );
+ print_line("STARTING RENDER THREAD");
+ }
+ while(!draw_thread_up) {
+ OS::get_singleton()->delay_usec(1000);
+ }
+ print_line("DONE RENDER THREAD");
+ } else {
+
+ visual_server->init();
+ }
+
+}
+
+void VisualServerWrapMT::finish() {
+
+
+ if (thread) {
+
+ command_queue.push( this, &VisualServerWrapMT::thread_exit);
+ Thread::wait_to_finish( thread );
+ memdelete(thread);
+ thread=NULL;
+ } else {
+ visual_server->finish();
+ }
+
+ if (draw_mutex)
+ memdelete(draw_mutex);
+
+}
+
+
+VisualServerWrapMT::VisualServerWrapMT(VisualServer* p_contained,bool p_create_thread) : command_queue(p_create_thread) {
+
+ visual_server=p_contained;
+ create_thread=p_create_thread;
+ thread=NULL;
+ draw_mutex=NULL;
+ draw_pending=0;
+ draw_thread_up=false;
+ if (!p_create_thread) {
+ server_thread=Thread::get_caller_ID();
+ } else {
+ server_thread=0;
+ }
+}
+
+
+VisualServerWrapMT::~VisualServerWrapMT() {
+
+ memdelete(visual_server);
+ //finish();
+
+}
+
+
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
new file mode 100644
index 0000000000..3d227cfdbc
--- /dev/null
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -0,0 +1,1057 @@
+/*************************************************************************/
+/* visual_server_wrap_mt.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 VISUAL_SERVER_WRAP_MT_H
+#define VISUAL_SERVER_WRAP_MT_H
+
+
+#include "servers/visual_server.h"
+#include "command_queue_mt.h"
+#include "os/thread.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class VisualServerWrapMT : public VisualServer {
+
+ // the real visual server
+ mutable VisualServer *visual_server;
+
+ mutable CommandQueueMT command_queue;
+
+ static void _thread_callback(void *_instance);
+ void thread_loop();
+
+ Thread::ID server_thread;
+ volatile bool exit;
+ Thread *thread;
+ volatile bool draw_thread_up;
+ bool create_thread;
+
+ Mutex *draw_mutex;
+ int draw_pending;
+ void thread_draw();
+ void thread_flush();
+
+ void thread_exit();
+
+public:
+
+#define FUNC0R(m_r,m_func)\
+ virtual m_r m_func() { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func();\
+ }\
+ }
+
+#define FUNC0RC(m_r,m_func)\
+ virtual m_r m_func() const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func();\
+ }\
+ }
+
+
+#define FUNC0(m_func)\
+ virtual void m_func() { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func);\
+ } else {\
+ visual_server->m_func();\
+ }\
+ }
+
+#define FUNC0C(m_func)\
+ virtual void m_func() const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func);\
+ } else {\
+ visual_server->m_func();\
+ }\
+ }
+
+
+#define FUNC0S(m_func)\
+ virtual void m_func() { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func);\
+ } else {\
+ visual_server->m_func();\
+ }\
+ }
+
+#define FUNC0SC(m_func)\
+ virtual void m_func() const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func);\
+ } else {\
+ visual_server->m_func();\
+ }\
+ }
+
+
+///////////////////////////////////////////////
+
+
+#define FUNC1R(m_r,m_func,m_arg1)\
+ virtual m_r m_func(m_arg1 p1) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1);\
+ }\
+ }
+
+#define FUNC1RC(m_r,m_func,m_arg1)\
+ virtual m_r m_func(m_arg1 p1) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1);\
+ }\
+ }
+
+
+#define FUNC1S(m_func,m_arg1)\
+ virtual void m_func(m_arg1 p1) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1);\
+ } else {\
+ visual_server->m_func(p1);\
+ }\
+ }
+
+#define FUNC1SC(m_func,m_arg1)\
+ virtual void m_func(m_arg1 p1) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1);\
+ } else {\
+ visual_server->m_func(p1);\
+ }\
+ }
+
+
+#define FUNC1(m_func,m_arg1)\
+ virtual void m_func(m_arg1 p1) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1);\
+ } else {\
+ visual_server->m_func(p1);\
+ }\
+ }
+
+#define FUNC1C(m_func,m_arg1)\
+ virtual void m_func(m_arg1 p1) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1);\
+ } else {\
+ visual_server->m_func(p1);\
+ }\
+ }
+
+
+
+
+#define FUNC2R(m_r,m_func,m_arg1, m_arg2)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2);\
+ }\
+ }
+
+#define FUNC2RC(m_r,m_func,m_arg1, m_arg2)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2);\
+ }\
+ }
+
+
+#define FUNC2S(m_func,m_arg1, m_arg2)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2);\
+ } else {\
+ visual_server->m_func(p1, p2);\
+ }\
+ }
+
+#define FUNC2SC(m_func,m_arg1, m_arg2)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2);\
+ } else {\
+ visual_server->m_func(p1, p2);\
+ }\
+ }
+
+
+#define FUNC2(m_func,m_arg1, m_arg2)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2);\
+ } else {\
+ visual_server->m_func(p1, p2);\
+ }\
+ }
+
+#define FUNC2C(m_func,m_arg1, m_arg2)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2);\
+ } else {\
+ visual_server->m_func(p1, p2);\
+ }\
+ }
+
+
+
+
+#define FUNC3R(m_r,m_func,m_arg1, m_arg2, m_arg3)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3);\
+ }\
+ }
+
+#define FUNC3RC(m_r,m_func,m_arg1, m_arg2, m_arg3)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3);\
+ }\
+ }
+
+
+#define FUNC3S(m_func,m_arg1, m_arg2, m_arg3)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3);\
+ } else {\
+ visual_server->m_func(p1, p2, p3);\
+ }\
+ }
+
+#define FUNC3SC(m_func,m_arg1, m_arg2, m_arg3)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3);\
+ } else {\
+ visual_server->m_func(p1, p2, p3);\
+ }\
+ }
+
+
+#define FUNC3(m_func,m_arg1, m_arg2, m_arg3)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3);\
+ } else {\
+ visual_server->m_func(p1, p2, p3);\
+ }\
+ }
+
+#define FUNC3C(m_func,m_arg1, m_arg2, m_arg3)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3);\
+ } else {\
+ visual_server->m_func(p1, p2, p3);\
+ }\
+ }
+
+
+
+
+#define FUNC4R(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4);\
+ }\
+ }
+
+#define FUNC4RC(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4);\
+ }\
+ }
+
+
+#define FUNC4S(m_func,m_arg1, m_arg2, m_arg3, m_arg4)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4);\
+ }\
+ }
+
+#define FUNC4SC(m_func,m_arg1, m_arg2, m_arg3, m_arg4)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4);\
+ }\
+ }
+
+
+#define FUNC4(m_func,m_arg1, m_arg2, m_arg3, m_arg4)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4);\
+ }\
+ }
+
+#define FUNC4C(m_func,m_arg1, m_arg2, m_arg3, m_arg4)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4);\
+ }\
+ }
+
+
+
+
+#define FUNC5R(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4, p5);\
+ }\
+ }
+
+#define FUNC5RC(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4, p5);\
+ }\
+ }
+
+
+#define FUNC5S(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5);\
+ }\
+ }
+
+#define FUNC5SC(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5);\
+ }\
+ }
+
+
+#define FUNC5(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5);\
+ }\
+ }
+
+#define FUNC5C(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5);\
+ }\
+ }
+
+
+
+
+#define FUNC6R(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4, p5, p6);\
+ }\
+ }
+
+#define FUNC6RC(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4, p5, p6);\
+ }\
+ }
+
+
+#define FUNC6S(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6);\
+ }\
+ }
+
+#define FUNC6SC(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6);\
+ }\
+ }
+
+
+#define FUNC6(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6);\
+ }\
+ }
+
+#define FUNC6C(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6);\
+ }\
+ }
+
+
+
+
+#define FUNC7R(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6, p7,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4, p5, p6, p7);\
+ }\
+ }
+
+#define FUNC7RC(m_r,m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7)\
+ virtual m_r m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ m_r ret;\
+ command_queue.push_and_ret( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6, p7,&ret);\
+ return ret;\
+ } else {\
+ return visual_server->m_func(p1, p2, p3, p4, p5, p6, p7);\
+ }\
+ }
+
+
+#define FUNC7S(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6, p7);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6, p7);\
+ }\
+ }
+
+#define FUNC7SC(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push_and_sync( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6, p7);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6, p7);\
+ }\
+ }
+
+
+#define FUNC7(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7) { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6, p7);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6, p7);\
+ }\
+ }
+
+#define FUNC7C(m_func,m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7)\
+ virtual void m_func(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7) const { \
+ if (Thread::get_caller_ID()!=server_thread) {\
+ command_queue.push( visual_server, &VisualServer::m_func,p1, p2, p3, p4, p5, p6, p7);\
+ } else {\
+ visual_server->m_func(p1, p2, p3, p4, p5, p6, p7);\
+ }\
+ }
+
+
+
+
+ FUNC0R(RID,texture_create);
+ FUNC5(texture_allocate,RID,int,int,Image::Format,uint32_t);
+ FUNC3(texture_set_data,RID,const Image&,CubeMapSide);
+ FUNC2RC(Image,texture_get_data,RID,CubeMapSide);
+ FUNC2(texture_set_flags,RID,uint32_t);
+ FUNC1RC(Image::Format,texture_get_format,RID);
+ FUNC1RC(uint32_t,texture_get_flags,RID);
+ FUNC1RC(uint32_t,texture_get_width,RID);
+ FUNC1RC(uint32_t,texture_get_height,RID);
+ FUNC3(texture_set_size_override,RID,int,int);
+ FUNC1RC(bool,texture_can_stream,RID);
+ FUNC3C(texture_set_reload_hook,RID,ObjectID,const StringName&);
+
+ /* SHADER API */
+
+ FUNC1R(RID,shader_create,ShaderMode);
+ FUNC2(shader_set_mode,RID,ShaderMode);
+ FUNC1RC(ShaderMode,shader_get_mode,RID);
+ FUNC5(shader_set_code,RID,const String&,const String&,int,int);
+ FUNC1RC(String,shader_get_vertex_code,RID);
+ FUNC1RC(String,shader_get_fragment_code,RID);
+ FUNC2SC(shader_get_param_list,RID,List<PropertyInfo>*);
+
+ /*virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) {
+ if (Thread::get_caller_ID()!=server_thread) {
+ command_queue.push_and_sync( visual_server, &VisualServer::shader_get_param_list,p_shader,p_param_list);
+ } else {
+ visual_server->m_func(p1, p2, p3, p4, p5);
+ }
+ }*/
+
+// virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list);
+
+
+ /* COMMON MATERIAL API */
+
+ FUNC0R(RID,material_create);
+ FUNC2(material_set_shader,RID,RID);
+ FUNC1RC(RID,material_get_shader,RID);
+
+ FUNC3(material_set_param,RID,const StringName&,const Variant&);
+ FUNC2RC(Variant,material_get_param,RID,const StringName&);
+
+ FUNC3(material_set_flag,RID,MaterialFlag,bool);
+ FUNC2RC(bool,material_get_flag,RID,MaterialFlag);
+
+ FUNC3(material_set_hint,RID,MaterialHint,bool);
+ FUNC2RC(bool,material_get_hint,RID,MaterialHint);
+
+ FUNC2(material_set_shade_model,RID,MaterialShadeModel);
+ FUNC1RC(MaterialShadeModel,material_get_shade_model,RID);
+
+ FUNC2(material_set_blend_mode,RID,MaterialBlendMode);
+ FUNC1RC(MaterialBlendMode,material_get_blend_mode,RID);
+
+ FUNC2(material_set_line_width,RID,float);
+ FUNC1RC(float,material_get_line_width,RID);
+
+ /* FIXED MATERIAL */
+
+
+ FUNC0R(RID,fixed_material_create);
+
+ FUNC3(fixed_material_set_flag,RID, FixedMaterialFlags , bool );
+ FUNC2RC(bool, fixed_material_get_flag,RID, FixedMaterialFlags);
+
+ FUNC3(fixed_material_set_param,RID, FixedMaterialParam, const Variant& );
+ FUNC2RC(Variant, fixed_material_get_param,RID ,FixedMaterialParam);
+
+ FUNC3(fixed_material_set_texture,RID ,FixedMaterialParam, RID );
+ FUNC2RC(RID, fixed_material_get_texture,RID,FixedMaterialParam);
+
+ FUNC2(fixed_material_set_detail_blend_mode,RID ,MaterialBlendMode );
+ FUNC1RC(MaterialBlendMode, fixed_material_get_detail_blend_mode,RID);
+
+
+ FUNC3(fixed_material_set_texcoord_mode,RID,FixedMaterialParam, FixedMaterialTexCoordMode );
+ FUNC2RC(FixedMaterialTexCoordMode, fixed_material_get_texcoord_mode,RID,FixedMaterialParam);
+
+ FUNC2(fixed_material_set_uv_transform,RID,const Transform&);
+ FUNC1RC(Transform, fixed_material_get_uv_transform,RID);
+
+ FUNC2(fixed_material_set_point_size,RID ,float);
+ FUNC1RC(float,fixed_material_get_point_size,RID);
+
+ /* SURFACE API */
+ FUNC0R(RID,mesh_create);
+
+ FUNC2(mesh_set_morph_target_count,RID,int);
+ FUNC1RC(int,mesh_get_morph_target_count,RID);
+
+ FUNC2(mesh_set_morph_target_mode,RID,MorphTargetMode);
+ FUNC1RC(MorphTargetMode,mesh_get_morph_target_mode,RID);
+
+ FUNC2(mesh_add_custom_surface,RID,const Variant&); //this is used by each platform in a different way
+
+ FUNC5(mesh_add_surface,RID,PrimitiveType,const Array&,const Array&,bool);
+ FUNC2RC(Array,mesh_get_surface_arrays,RID,int);
+ FUNC2RC(Array,mesh_get_surface_morph_arrays,RID,int);
+
+ FUNC4(mesh_surface_set_material,RID, int, RID,bool);
+ FUNC2RC(RID,mesh_surface_get_material,RID, int);
+
+ FUNC2RC(int,mesh_surface_get_array_len,RID, int);
+ FUNC2RC(int,mesh_surface_get_array_index_len,RID, int);
+ FUNC2RC(uint32_t,mesh_surface_get_format,RID, int);
+ FUNC2RC(PrimitiveType,mesh_surface_get_primitive_type,RID, int);
+
+ FUNC2(mesh_remove_surface,RID,int);
+ FUNC1RC(int,mesh_get_surface_count,RID);
+
+
+
+ /* MULTIMESH API */
+
+ FUNC0R(RID,multimesh_create);
+ FUNC2(multimesh_set_instance_count,RID,int);
+ FUNC1RC(int,multimesh_get_instance_count,RID);
+
+ FUNC2(multimesh_set_mesh,RID,RID);
+ FUNC2(multimesh_set_aabb,RID,const AABB&);
+ FUNC3(multimesh_instance_set_transform,RID,int,const Transform&);
+ FUNC3(multimesh_instance_set_color,RID,int,const Color&);
+
+ FUNC1RC(RID,multimesh_get_mesh,RID);
+ FUNC2RC(AABB,multimesh_get_aabb,RID,const AABB&);
+ FUNC2RC(Transform,multimesh_instance_get_transform,RID,int);
+ FUNC2RC(Color,multimesh_instance_get_color,RID,int);
+
+ FUNC2(multimesh_set_visible_instances,RID,int);
+ FUNC1RC(int,multimesh_get_visible_instances,RID);
+
+
+ /* PARTICLES API */
+
+ FUNC0R(RID,particles_create);
+
+ FUNC2(particles_set_amount,RID, int );
+ FUNC1RC(int,particles_get_amount,RID);
+
+ FUNC2(particles_set_emitting,RID, bool );
+ FUNC1RC(bool,particles_is_emitting,RID);
+
+ FUNC2(particles_set_visibility_aabb,RID, const AABB&);
+ FUNC1RC(AABB,particles_get_visibility_aabb,RID);
+
+ FUNC2(particles_set_emission_half_extents,RID, const Vector3&);
+ FUNC1RC(Vector3,particles_get_emission_half_extents,RID);
+
+ FUNC2(particles_set_emission_base_velocity,RID, const Vector3&);
+ FUNC1RC(Vector3,particles_get_emission_base_velocity,RID);
+
+ FUNC2(particles_set_emission_points,RID, const DVector<Vector3>& );
+ FUNC1RC(DVector<Vector3>,particles_get_emission_points,RID);
+
+ FUNC2(particles_set_gravity_normal,RID, const Vector3& );
+ FUNC1RC(Vector3,particles_get_gravity_normal,RID);
+
+ FUNC3(particles_set_variable,RID, ParticleVariable ,float);
+ FUNC2RC(float,particles_get_variable,RID, ParticleVariable );
+
+ FUNC3(particles_set_randomness,RID, ParticleVariable ,float);
+ FUNC2RC(float,particles_get_randomness,RID, ParticleVariable );
+
+ FUNC3(particles_set_color_phase_pos,RID, int , float);
+ FUNC2RC(float,particles_get_color_phase_pos,RID, int );
+
+ FUNC2(particles_set_color_phases,RID, int );
+ FUNC1RC(int,particles_get_color_phases,RID);
+
+ FUNC3(particles_set_color_phase_color,RID, int , const Color& );
+ FUNC2RC(Color,particles_get_color_phase_color,RID, int );
+
+ FUNC2(particles_set_attractors,RID, int);
+ FUNC1RC(int,particles_get_attractors,RID);
+
+ FUNC3(particles_set_attractor_pos,RID, int, const Vector3&);
+ FUNC2RC(Vector3,particles_get_attractor_pos,RID,int);
+
+ FUNC3(particles_set_attractor_strength,RID, int, float);
+ FUNC2RC(float,particles_get_attractor_strength,RID,int);
+
+ FUNC3(particles_set_material,RID, RID,bool);
+ FUNC1RC(RID,particles_get_material,RID);
+
+ FUNC2(particles_set_height_from_velocity,RID, bool);
+ FUNC1RC(bool,particles_has_height_from_velocity,RID);
+
+ FUNC2(particles_set_use_local_coordinates,RID, bool);
+ FUNC1RC(bool,particles_is_using_local_coordinates,RID);
+
+
+ /* Light API */
+
+ FUNC1R(RID,light_create,LightType);
+ FUNC1RC(LightType,light_get_type,RID);
+
+ FUNC3(light_set_color,RID,LightColor , const Color& );
+ FUNC2RC(Color,light_get_color,RID,LightColor );
+
+
+ FUNC2(light_set_shadow,RID,bool );
+ FUNC1RC(bool,light_has_shadow,RID);
+
+ FUNC2(light_set_volumetric,RID,bool );
+ FUNC1RC(bool,light_is_volumetric,RID);
+
+ FUNC2(light_set_projector,RID,RID );
+ FUNC1RC(RID,light_get_projector,RID);
+
+ FUNC3(light_set_param,RID, LightParam , float );
+ FUNC2RC(float,light_get_param,RID, LightParam );
+
+ FUNC2(light_set_operator,RID,LightOp);
+ FUNC1RC(LightOp,light_get_operator,RID);
+
+ FUNC2(light_omni_set_shadow_mode,RID,LightOmniShadowMode);
+ FUNC1RC(LightOmniShadowMode,light_omni_get_shadow_mode,RID);
+
+ FUNC2(light_directional_set_shadow_mode,RID,LightDirectionalShadowMode);
+ FUNC1RC(LightDirectionalShadowMode,light_directional_get_shadow_mode,RID);
+ FUNC3(light_directional_set_shadow_param,RID,LightDirectionalShadowParam, float );
+ FUNC2RC(float,light_directional_get_shadow_param,RID,LightDirectionalShadowParam );
+
+
+ /* SKELETON API */
+
+ FUNC0R(RID,skeleton_create);
+ FUNC2(skeleton_resize,RID,int );
+ FUNC1RC(int,skeleton_get_bone_count,RID) ;
+ FUNC3(skeleton_bone_set_transform,RID,int, const Transform&);
+ FUNC2R(Transform,skeleton_bone_get_transform,RID,int );
+
+ /* ROOM API */
+
+ FUNC0R(RID,room_create);
+ FUNC2(room_set_bounds,RID, const BSP_Tree&);
+ FUNC1RC(BSP_Tree,room_get_bounds,RID);
+
+ /* PORTAL API */
+
+ FUNC0R(RID,portal_create);
+ FUNC2(portal_set_shape,RID,const Vector<Point2>&);
+ FUNC1RC(Vector<Point2>,portal_get_shape,RID);
+ FUNC2(portal_set_enabled,RID, bool);
+ FUNC1RC(bool,portal_is_enabled,RID);
+ FUNC2(portal_set_disable_distance,RID, float);
+ FUNC1RC(float,portal_get_disable_distance,RID);
+ FUNC2(portal_set_disabled_color,RID, const Color&);
+ FUNC1RC(Color,portal_get_disabled_color,RID);
+ FUNC2(portal_set_connect_range,RID, float);
+ FUNC1RC(float,portal_get_connect_range,RID);
+
+
+
+ /* CAMERA API */
+
+ FUNC0R(RID,camera_create);
+ FUNC4(camera_set_perspective,RID,float , float , float );
+ FUNC4(camera_set_orthogonal,RID,float, float , float );
+ FUNC2(camera_set_transform,RID,const Transform& );
+
+ FUNC2(camera_set_visible_layers,RID,uint32_t);
+ FUNC1RC(uint32_t,camera_get_visible_layers,RID);
+
+ FUNC2(camera_set_environment,RID,RID);
+ FUNC1RC(RID,camera_get_environment,RID);
+
+ FUNC2(camera_set_use_vertical_aspect,RID,bool);
+ FUNC2RC(bool,camera_is_using_vertical_aspect,RID,bool);
+
+
+ /* VIEWPORT API */
+
+ FUNC0R(RID,viewport_create);
+
+ FUNC2(viewport_attach_to_screen,RID,int );
+ FUNC1(viewport_detach,RID);
+
+ FUNC2(viewport_set_as_render_target,RID,bool);
+ FUNC2(viewport_set_render_target_update_mode,RID,RenderTargetUpdateMode);
+ FUNC1RC(RenderTargetUpdateMode,viewport_get_render_target_update_mode,RID);
+ FUNC1RC(RID,viewport_get_render_target_texture,RID);
+
+ FUNC1(viewport_queue_screen_capture,RID);
+ FUNC1RC(Image,viewport_get_screen_capture,RID);
+
+ FUNC2(viewport_set_rect,RID,const ViewportRect&);
+ FUNC1RC(ViewportRect,viewport_get_rect,RID);
+
+ FUNC2(viewport_set_hide_scenario,RID,bool );
+ FUNC2(viewport_set_hide_canvas,RID,bool );
+ FUNC2(viewport_attach_camera,RID,RID );
+ FUNC2(viewport_set_scenario,RID,RID );
+
+ FUNC1RC(RID,viewport_get_attached_camera,RID);
+ FUNC1RC(RID,viewport_get_scenario,RID );
+ FUNC2(viewport_attach_canvas,RID,RID);
+ FUNC2(viewport_remove_canvas,RID,RID);
+ FUNC3(viewport_set_canvas_transform,RID,RID,const Matrix32&);
+ FUNC2RC(Matrix32,viewport_get_canvas_transform,RID,RID);
+ FUNC2(viewport_set_global_canvas_transform,RID,const Matrix32&);
+ FUNC1RC(Matrix32,viewport_get_global_canvas_transform,RID);
+ FUNC3(viewport_set_canvas_layer,RID,RID ,int);
+ FUNC2(viewport_set_transparent_background,RID,bool);
+ FUNC1RC(bool,viewport_has_transparent_background,RID);
+
+
+ /* ENVIRONMENT API */
+
+ FUNC0R(RID,environment_create);
+
+ FUNC2(environment_set_background,RID,EnvironmentBG);
+ FUNC1RC(EnvironmentBG,environment_get_background,RID);
+
+ FUNC3(environment_set_background_param,RID,EnvironmentBGParam, const Variant&);
+ FUNC2RC(Variant,environment_get_background_param,RID,EnvironmentBGParam );
+
+ FUNC3(environment_set_enable_fx,RID,EnvironmentFx,bool);
+ FUNC2RC(bool,environment_is_fx_enabled,RID,EnvironmentFx);
+
+
+ FUNC3(environment_fx_set_param,RID,EnvironmentFxParam,const Variant&);
+ FUNC2RC(Variant,environment_fx_get_param,RID,EnvironmentFxParam);
+
+
+ /* SCENARIO API */
+
+ FUNC0R(RID,scenario_create);
+
+ FUNC2(scenario_set_debug,RID,ScenarioDebugMode);
+ FUNC2(scenario_set_environment,RID, RID);
+ FUNC2RC(RID,scenario_get_environment,RID, RID);
+
+
+ /* INSTANCING API */
+
+ FUNC0R(RID,instance_create);
+
+ FUNC2(instance_set_base,RID, RID);
+ FUNC1RC(RID,instance_get_base,RID);
+
+ FUNC2(instance_set_scenario,RID, RID);
+ FUNC1RC(RID,instance_get_scenario,RID);
+
+ FUNC2(instance_set_layer_mask,RID, uint32_t);
+ FUNC1RC(uint32_t,instance_get_layer_mask,RID);
+
+ FUNC1RC(AABB,instance_get_base_aabb,RID);
+
+ FUNC2(instance_attach_object_instance_ID,RID,uint32_t);
+ FUNC1RC(uint32_t,instance_get_object_instance_ID,RID);
+
+ FUNC2(instance_attach_skeleton,RID,RID);
+ FUNC1RC(RID,instance_get_skeleton,RID);
+
+ FUNC3(instance_set_morph_target_weight,RID,int, float);
+ FUNC2RC(float,instance_get_morph_target_weight,RID,int);
+
+ FUNC2(instance_set_transform,RID, const Transform&);
+ FUNC1RC(Transform,instance_get_transform,RID);
+
+ FUNC2(instance_set_exterior,RID, bool );
+ FUNC1RC(bool,instance_is_exterior,RID);
+
+ FUNC2(instance_set_room,RID, RID );
+ FUNC1RC(RID,instance_get_room,RID ) ;
+
+ FUNC2(instance_set_extra_visibility_margin,RID, real_t );
+ FUNC1RC(real_t,instance_get_extra_visibility_margin,RID );
+
+ FUNC2RC(Vector<RID>,instances_cull_aabb,const AABB& , RID );
+ FUNC3RC(Vector<RID>,instances_cull_ray,const Vector3& ,const Vector3&, RID );
+ FUNC2RC(Vector<RID>,instances_cull_convex,const Vector<Plane>& , RID );
+
+ FUNC3(instance_geometry_set_flag,RID,InstanceFlags ,bool );
+ FUNC2RC(bool,instance_geometry_get_flag,RID,InstanceFlags );
+
+ FUNC2(instance_geometry_set_material_override,RID, RID );
+ FUNC1RC(RID,instance_geometry_get_material_override,RID);
+
+ FUNC3(instance_geometry_set_draw_range,RID,float ,float);
+ FUNC1RC(float,instance_geometry_get_draw_range_max,RID);
+ FUNC1RC(float,instance_geometry_get_draw_range_min,RID);
+
+
+ /* CANVAS (2D) */
+
+ FUNC0R(RID,canvas_create);
+ FUNC3(canvas_set_item_mirroring,RID,RID,const Point2&);
+ FUNC2RC(Point2,canvas_get_item_mirroring,RID,RID);
+
+ FUNC0R(RID,canvas_item_create);
+
+ FUNC2(canvas_item_set_parent,RID,RID );
+ FUNC1RC(RID,canvas_item_get_parent,RID);
+
+ FUNC2(canvas_item_set_visible,RID,bool );
+ FUNC1RC(bool,canvas_item_is_visible,RID);
+
+ FUNC2(canvas_item_set_blend_mode,RID,MaterialBlendMode );
+
+
+ //FUNC(canvas_item_set_rect,RID, const Rect2& p_rect);
+ FUNC2(canvas_item_set_transform,RID, const Matrix32& );
+ FUNC2(canvas_item_set_clip,RID, bool );
+ FUNC3(canvas_item_set_custom_rect,RID, bool ,const Rect2&);
+ FUNC2(canvas_item_set_opacity,RID, float );
+ FUNC2RC(float,canvas_item_get_opacity,RID, float );
+ FUNC2(canvas_item_set_on_top,RID, bool );
+ FUNC1RC(bool,canvas_item_is_on_top,RID);
+
+ FUNC2(canvas_item_set_self_opacity,RID, float );
+ FUNC2RC(float,canvas_item_get_self_opacity,RID, float );
+
+ FUNC2(canvas_item_attach_viewport,RID, RID );
+
+ FUNC5(canvas_item_add_line,RID, const Point2& , const Point2& ,const Color& ,float );
+ FUNC3(canvas_item_add_rect,RID, const Rect2& , const Color& );
+ FUNC4(canvas_item_add_circle,RID, const Point2& , float ,const Color& );
+ FUNC5(canvas_item_add_texture_rect,RID, const Rect2& , RID ,bool ,const Color& );
+ FUNC5(canvas_item_add_texture_rect_region,RID, const Rect2& , RID ,const Rect2& ,const Color& );
+
+ FUNC7(canvas_item_add_style_box,RID, const Rect2& , RID ,const Vector2& ,const Vector2&, bool ,const Color& );
+ FUNC6(canvas_item_add_primitive,RID, const Vector<Point2>& , const Vector<Color>& ,const Vector<Point2>& , RID ,float );
+ FUNC5(canvas_item_add_polygon,RID, const Vector<Point2>& , const Vector<Color>& ,const Vector<Point2>& , RID );
+ FUNC7(canvas_item_add_triangle_array,RID, const Vector<int>& , const Vector<Point2>& , const Vector<Color>& ,const Vector<Point2>& , RID , int );
+ FUNC7(canvas_item_add_triangle_array_ptr,RID, int , const int* , const Point2* , const Color* ,const Point2* , RID );
+
+
+ FUNC2(canvas_item_add_set_transform,RID,const Matrix32& );
+ FUNC2(canvas_item_add_set_blend_mode,RID, MaterialBlendMode );
+ FUNC2(canvas_item_add_clip_ignore,RID, bool );
+
+ FUNC1(canvas_item_clear,RID);
+ FUNC1(canvas_item_raise,RID);
+
+
+ /* CURSOR */
+ FUNC2(cursor_set_rotation,float , int ); // radians
+ FUNC3(cursor_set_texture,RID , const Point2 &, int );
+ FUNC2(cursor_set_visible,bool , int );
+ FUNC2(cursor_set_pos,const Point2& , int );
+
+ /* BLACK BARS */
+
+ FUNC4(black_bars_set_margins,int , int , int , int );
+
+ /* FREE */
+
+ FUNC1(free,RID);
+
+ /* CUSTOM SHADE MODEL */
+
+ FUNC2(custom_shade_model_set_shader,int , RID );
+ FUNC1RC(RID,custom_shade_model_get_shader,int );
+ FUNC2(custom_shade_model_set_name,int , const String& );
+ FUNC1RC(String,custom_shade_model_get_name,int );
+ FUNC2(custom_shade_model_set_param_info,int , const List<PropertyInfo>& );
+ FUNC2SC(custom_shade_model_get_param_info,int , List<PropertyInfo>* );
+
+ /* EVENT QUEUING */
+
+
+ virtual void init();
+ virtual void finish();
+ virtual void draw();
+ virtual void flush();
+ FUNC0RC(bool,has_changed);
+
+ /* RENDER INFO */
+
+ FUNC1R(int,get_render_info,RenderInfo );
+ FUNC1RC(bool,has_feature,Features );
+
+ FUNC2(set_boot_image,const Image& , const Color& );
+ FUNC1(set_default_clear_color,const Color& );
+
+ FUNC0R(RID,get_test_cube );
+
+
+ VisualServerWrapMT(VisualServer* p_contained,bool p_create_thread);
+ ~VisualServerWrapMT();
+
+};
+
+
+#endif
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
new file mode 100644
index 0000000000..a45e2c8eaf
--- /dev/null
+++ b/servers/visual_server.cpp
@@ -0,0 +1,751 @@
+/*************************************************************************/
+/* visual_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "visual_server.h"
+#include "globals.h"
+
+VisualServer *VisualServer::singleton=NULL;
+VisualServer* (*VisualServer::create_func)()=NULL;
+
+VisualServer *VisualServer::get_singleton() {
+
+ return singleton;
+}
+
+
+void VisualServer::set_mipmap_policy(MipMapPolicy p_policy) {
+
+ mm_policy=p_policy;
+}
+
+VisualServer::MipMapPolicy VisualServer::get_mipmap_policy() const {
+
+ return (VisualServer::MipMapPolicy)mm_policy;
+}
+
+
+DVector<String> VisualServer::_shader_get_param_list(RID p_shader) const {
+
+//remove at some point
+
+ DVector<String> pl;
+
+
+#if 0
+ List<StringName> params;
+ shader_get_param_list(p_shader,&params);
+
+
+ for(List<StringName>::Element *E=params.front();E;E=E->next()) {
+
+ pl.push_back(E->get());
+ }
+#endif
+ return pl;
+}
+
+VisualServer *VisualServer::create() {
+
+ ERR_FAIL_COND_V(singleton,NULL);
+
+ if (create_func)
+ return create_func();
+
+ return NULL;
+}
+
+RID VisualServer::texture_create_from_image(const Image& p_image,uint32_t p_flags) {
+
+ RID texture = texture_create();
+ texture_allocate(texture,p_image.get_width(), p_image.get_height(), p_image.get_format(), p_flags); //if it has mipmaps, use, else generate
+ ERR_FAIL_COND_V(!texture.is_valid(),texture);
+
+ texture_set_data(texture, p_image );
+
+ return texture;
+}
+
+RID VisualServer::get_test_texture() {
+
+ if (test_texture) {
+ return test_texture;
+ };
+
+#define TEST_TEXTURE_SIZE 256
+
+ Image data(TEST_TEXTURE_SIZE,TEST_TEXTURE_SIZE,0,Image::FORMAT_RGB);
+
+ for (int x=0;x<TEST_TEXTURE_SIZE;x++) {
+
+ for (int y=0;y<TEST_TEXTURE_SIZE;y++) {
+
+ Color c;
+ int r=255-(x+y)/2;
+
+ if ((x%(TEST_TEXTURE_SIZE/8))<2 ||(y%(TEST_TEXTURE_SIZE/8))<2) {
+
+ c.r=y;
+ c.g=r;
+ c.b=x;
+
+ } else {
+
+ c.r=r;
+ c.g=x;
+ c.b=y;
+ }
+
+ data.put_pixel(x, y, c);
+ }
+ }
+
+ test_texture = texture_create_from_image(data);
+
+ return test_texture;
+};
+
+RID VisualServer::_make_test_cube() {
+
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+ DVector<float> tangents;
+ DVector<Vector3> uvs;
+
+ int vtx_idx=0;
+#define ADD_VTX(m_idx);\
+ vertices.push_back( face_points[m_idx] );\
+ normals.push_back( normal_points[m_idx] );\
+ tangents.push_back( normal_points[m_idx][1] );\
+ tangents.push_back( normal_points[m_idx][2] );\
+ tangents.push_back( normal_points[m_idx][0] );\
+ tangents.push_back( 1.0 );\
+ uvs.push_back( Vector3(uv_points[m_idx*2+0],uv_points[m_idx*2+1],0) );\
+ vtx_idx++;\
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+ Vector3 normal_points[4];
+ float uv_points[8]={0,0,0,1,1,1,1,0};
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ normal_points[j]=Vector3();
+ normal_points[j][i%3]=(i>=3?-1:1);
+ }
+
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+
+ }
+
+ RID test_cube = mesh_create();
+
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[VisualServer::ARRAY_NORMAL]= normals ;
+ d[VisualServer::ARRAY_TANGENT]= tangents ;
+ d[VisualServer::ARRAY_TEX_UV]= uvs ;
+ d[VisualServer::ARRAY_VERTEX]= vertices ;
+
+ DVector<int> indices;
+ indices.resize(vertices.size());
+ for(int i=0;i<vertices.size();i++)
+ indices.set(i,i);
+ d[VisualServer::ARRAY_INDEX]=indices;
+
+ mesh_add_surface( test_cube, PRIMITIVE_TRIANGLES,d );
+
+
+ RID material = fixed_material_create();
+ //material_set_flag(material, MATERIAL_FLAG_BILLBOARD_TOGGLE,true);
+ fixed_material_set_texture( material, FIXED_MATERIAL_PARAM_DIFFUSE, get_test_texture() );
+ fixed_material_set_param( material, FIXED_MATERIAL_PARAM_SPECULAR_EXP, 70 );
+ fixed_material_set_param( material, FIXED_MATERIAL_PARAM_EMISSION, Vector3(0.2,0.2,0.2) );
+
+ fixed_material_set_param( material, FIXED_MATERIAL_PARAM_DIFFUSE, Color(1, 1, 1) );
+ fixed_material_set_param( material, FIXED_MATERIAL_PARAM_SPECULAR, Color(1,1,1) );
+
+ mesh_surface_set_material(test_cube, 0, material );
+
+ return test_cube;
+}
+
+
+RID VisualServer::make_sphere_mesh(int p_lats,int p_lons,float p_radius) {
+
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+
+ for(int i = 1; i <= p_lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / p_lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / p_lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = p_lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / p_lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / p_lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+
+ Vector3 v[4]={
+ Vector3(x1 * zr0, z0, y1 *zr0),
+ Vector3(x1 * zr1, z1, y1 *zr1),
+ Vector3(x0 * zr1, z1, y0 *zr1),
+ Vector3(x0 * zr0, z0, y0 *zr0)
+ };
+
+#define ADD_POINT(m_idx)\
+ normals.push_back(v[m_idx]); \
+ vertices.push_back(v[m_idx]*p_radius);\
+
+ ADD_POINT(0);
+ ADD_POINT(1);
+ ADD_POINT(2);
+
+ ADD_POINT(2);
+ ADD_POINT(3);
+ ADD_POINT(0);
+ }
+ }
+
+ RID mesh = mesh_create();
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+
+ d[ARRAY_VERTEX]=vertices;
+ d[ARRAY_NORMAL]=normals;
+
+ mesh_add_surface(mesh,PRIMITIVE_TRIANGLES,d);
+
+ return mesh;
+}
+
+void VisualServer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("texture_create"),&VisualServer::texture_create);
+ ObjectTypeDB::bind_method(_MD("texture_create_from_image"),&VisualServer::texture_create_from_image,DEFVAL( TEXTURE_FLAGS_DEFAULT ) );
+ //ObjectTypeDB::bind_method(_MD("texture_allocate"),&VisualServer::texture_allocate,DEFVAL( TEXTURE_FLAGS_DEFAULT ) );
+ //ObjectTypeDB::bind_method(_MD("texture_set_data"),&VisualServer::texture_blit_rect,DEFVAL( CUBEMAP_LEFT ) );
+ //ObjectTypeDB::bind_method(_MD("texture_get_rect"),&VisualServer::texture_get_rect );
+ ObjectTypeDB::bind_method(_MD("texture_set_flags"),&VisualServer::texture_set_flags );
+ ObjectTypeDB::bind_method(_MD("texture_get_flags"),&VisualServer::texture_get_flags );
+ ObjectTypeDB::bind_method(_MD("texture_get_width"),&VisualServer::texture_get_width );
+ ObjectTypeDB::bind_method(_MD("texture_get_height"),&VisualServer::texture_get_height );
+#ifndef _3D_DISABLED
+
+ ObjectTypeDB::bind_method(_MD("shader_create","mode"),&VisualServer::shader_create,DEFVAL(SHADER_MATERIAL));
+ ObjectTypeDB::bind_method(_MD("shader_set_mode","shader","mode"),&VisualServer::shader_set_mode);
+
+
+
+ ObjectTypeDB::bind_method(_MD("material_create"),&VisualServer::material_create);
+
+ ObjectTypeDB::bind_method(_MD("material_set_shader","shader"),&VisualServer::material_set_shader);
+ ObjectTypeDB::bind_method(_MD("material_get_shader"),&VisualServer::material_get_shader);
+
+ ObjectTypeDB::bind_method(_MD("material_set_param"),&VisualServer::material_set_param);
+ ObjectTypeDB::bind_method(_MD("material_get_param"),&VisualServer::material_get_param);
+ ObjectTypeDB::bind_method(_MD("material_set_flag"),&VisualServer::material_set_flag);
+ ObjectTypeDB::bind_method(_MD("material_get_flag"),&VisualServer::material_get_flag);
+ ObjectTypeDB::bind_method(_MD("material_set_blend_mode"),&VisualServer::material_set_blend_mode);
+ ObjectTypeDB::bind_method(_MD("material_get_blend_mode"),&VisualServer::material_get_blend_mode);
+ ObjectTypeDB::bind_method(_MD("material_set_line_width"),&VisualServer::material_set_line_width);
+ ObjectTypeDB::bind_method(_MD("material_get_line_width"),&VisualServer::material_get_line_width);
+
+
+ ObjectTypeDB::bind_method(_MD("mesh_create"),&VisualServer::mesh_create);
+ ObjectTypeDB::bind_method(_MD("mesh_add_surface"),&VisualServer::mesh_add_surface, DEFVAL(NO_INDEX_ARRAY));
+ ObjectTypeDB::bind_method(_MD("mesh_surface_set_material"),&VisualServer::mesh_surface_set_material,DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("mesh_surface_get_material"),&VisualServer::mesh_surface_get_material);
+
+ ObjectTypeDB::bind_method(_MD("mesh_surface_get_array_len"),&VisualServer::mesh_surface_get_array_len);
+ ObjectTypeDB::bind_method(_MD("mesh_surface_get_array_index_len"),&VisualServer::mesh_surface_get_array_index_len);
+ ObjectTypeDB::bind_method(_MD("mesh_surface_get_format"),&VisualServer::mesh_surface_get_format);
+ ObjectTypeDB::bind_method(_MD("mesh_surface_get_primitive_type"),&VisualServer::mesh_surface_get_primitive_type);
+
+ ObjectTypeDB::bind_method(_MD("mesh_remove_surface"),&VisualServer::mesh_remove_surface);
+ ObjectTypeDB::bind_method(_MD("mesh_get_surface_count"),&VisualServer::mesh_get_surface_count);
+
+
+ ObjectTypeDB::bind_method(_MD("multimesh_create"),&VisualServer::multimesh_create);
+ ObjectTypeDB::bind_method(_MD("multimesh_set_mesh"),&VisualServer::multimesh_set_mesh);
+ ObjectTypeDB::bind_method(_MD("multimesh_set_aabb"),&VisualServer::multimesh_set_aabb);
+ ObjectTypeDB::bind_method(_MD("multimesh_instance_set_transform"),&VisualServer::multimesh_instance_set_transform);
+ ObjectTypeDB::bind_method(_MD("multimesh_instance_set_color"),&VisualServer::multimesh_instance_set_color);
+ ObjectTypeDB::bind_method(_MD("multimesh_get_mesh"),&VisualServer::multimesh_get_mesh);
+ ObjectTypeDB::bind_method(_MD("multimesh_get_aabb"),&VisualServer::multimesh_get_aabb);
+ ObjectTypeDB::bind_method(_MD("multimesh_instance_get_transform"),&VisualServer::multimesh_instance_get_transform);
+ ObjectTypeDB::bind_method(_MD("multimesh_instance_get_color"),&VisualServer::multimesh_instance_get_color);
+
+
+
+ ObjectTypeDB::bind_method(_MD("particles_create"),&VisualServer::particles_create);
+ ObjectTypeDB::bind_method(_MD("particles_set_amount"),&VisualServer::particles_set_amount);
+ ObjectTypeDB::bind_method(_MD("particles_get_amount"),&VisualServer::particles_get_amount);
+ ObjectTypeDB::bind_method(_MD("particles_set_emitting"),&VisualServer::particles_set_emitting);
+ ObjectTypeDB::bind_method(_MD("particles_is_emitting"),&VisualServer::particles_is_emitting);
+ ObjectTypeDB::bind_method(_MD("particles_set_visibility_aabb"),&VisualServer::particles_set_visibility_aabb);
+ ObjectTypeDB::bind_method(_MD("particles_get_visibility_aabb"),&VisualServer::particles_get_visibility_aabb);
+ ObjectTypeDB::bind_method(_MD("particles_set_variable"),&VisualServer::particles_set_variable);
+ ObjectTypeDB::bind_method(_MD("particles_get_variable"),&VisualServer::particles_get_variable);
+ ObjectTypeDB::bind_method(_MD("particles_set_randomness"),&VisualServer::particles_set_randomness);
+ ObjectTypeDB::bind_method(_MD("particles_get_randomness"),&VisualServer::particles_get_randomness);
+ ObjectTypeDB::bind_method(_MD("particles_set_color_phases"),&VisualServer::particles_set_color_phases);
+ ObjectTypeDB::bind_method(_MD("particles_get_color_phases"),&VisualServer::particles_get_color_phases);
+ ObjectTypeDB::bind_method(_MD("particles_set_color_phase_pos"),&VisualServer::particles_set_color_phase_pos);
+ ObjectTypeDB::bind_method(_MD("particles_get_color_phase_pos"),&VisualServer::particles_get_color_phase_pos);
+ ObjectTypeDB::bind_method(_MD("particles_set_color_phase_color"),&VisualServer::particles_set_color_phase_color);
+ ObjectTypeDB::bind_method(_MD("particles_get_color_phase_color"),&VisualServer::particles_get_color_phase_color);
+ ObjectTypeDB::bind_method(_MD("particles_set_attractors"),&VisualServer::particles_set_attractors);
+ ObjectTypeDB::bind_method(_MD("particles_get_attractors"),&VisualServer::particles_get_attractors);
+ ObjectTypeDB::bind_method(_MD("particles_set_attractor_pos"),&VisualServer::particles_set_attractor_pos);
+ ObjectTypeDB::bind_method(_MD("particles_get_attractor_pos"),&VisualServer::particles_get_attractor_pos);
+ ObjectTypeDB::bind_method(_MD("particles_set_attractor_strength"),&VisualServer::particles_set_attractor_strength);
+ ObjectTypeDB::bind_method(_MD("particles_get_attractor_strength"),&VisualServer::particles_get_attractor_strength);
+ ObjectTypeDB::bind_method(_MD("particles_set_material"),&VisualServer::particles_set_material,DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("particles_set_height_from_velocity"),&VisualServer::particles_set_height_from_velocity);
+ ObjectTypeDB::bind_method(_MD("particles_has_height_from_velocity"),&VisualServer::particles_has_height_from_velocity);
+
+
+
+ ObjectTypeDB::bind_method(_MD("light_create"),&VisualServer::light_create);
+ ObjectTypeDB::bind_method(_MD("light_get_type"),&VisualServer::light_get_type);
+ ObjectTypeDB::bind_method(_MD("light_set_color"),&VisualServer::light_set_color);
+ ObjectTypeDB::bind_method(_MD("light_get_color"),&VisualServer::light_get_color);
+ ObjectTypeDB::bind_method(_MD("light_set_shadow"),&VisualServer::light_set_shadow);
+ ObjectTypeDB::bind_method(_MD("light_has_shadow"),&VisualServer::light_has_shadow);
+ ObjectTypeDB::bind_method(_MD("light_set_volumetric"),&VisualServer::light_set_volumetric);
+ ObjectTypeDB::bind_method(_MD("light_is_volumetric"),&VisualServer::light_is_volumetric);
+ ObjectTypeDB::bind_method(_MD("light_set_projector"),&VisualServer::light_set_projector);
+ ObjectTypeDB::bind_method(_MD("light_get_projector"),&VisualServer::light_get_projector);
+ ObjectTypeDB::bind_method(_MD("light_set_var"),&VisualServer::light_set_param);
+ ObjectTypeDB::bind_method(_MD("light_get_var"),&VisualServer::light_get_param);
+
+ ObjectTypeDB::bind_method(_MD("skeleton_create"),&VisualServer::skeleton_create);
+ ObjectTypeDB::bind_method(_MD("skeleton_resize"),&VisualServer::skeleton_resize);
+ ObjectTypeDB::bind_method(_MD("skeleton_get_bone_count"),&VisualServer::skeleton_get_bone_count);
+ ObjectTypeDB::bind_method(_MD("skeleton_bone_set_transform"),&VisualServer::skeleton_bone_set_transform);
+ ObjectTypeDB::bind_method(_MD("skeleton_bone_get_transform"),&VisualServer::skeleton_bone_get_transform);
+
+
+
+ ObjectTypeDB::bind_method(_MD("room_create"),&VisualServer::room_create);
+ ObjectTypeDB::bind_method(_MD("room_set_bounds"),&VisualServer::room_set_bounds);
+ ObjectTypeDB::bind_method(_MD("room_get_bounds"),&VisualServer::room_get_bounds);
+
+ ObjectTypeDB::bind_method(_MD("portal_create"),&VisualServer::portal_create);
+ ObjectTypeDB::bind_method(_MD("portal_set_shape"),&VisualServer::portal_set_shape);
+ ObjectTypeDB::bind_method(_MD("portal_get_shape"),&VisualServer::portal_get_shape);
+ ObjectTypeDB::bind_method(_MD("portal_set_enabled"),&VisualServer::portal_set_enabled);
+ ObjectTypeDB::bind_method(_MD("portal_is_enabled"),&VisualServer::portal_is_enabled);
+ ObjectTypeDB::bind_method(_MD("portal_set_disable_distance"),&VisualServer::portal_set_disable_distance);
+ ObjectTypeDB::bind_method(_MD("portal_get_disable_distance"),&VisualServer::portal_get_disable_distance);
+ ObjectTypeDB::bind_method(_MD("portal_set_disabled_color"),&VisualServer::portal_set_disabled_color);
+ ObjectTypeDB::bind_method(_MD("portal_get_disabled_color"),&VisualServer::portal_get_disabled_color);
+
+
+ ObjectTypeDB::bind_method(_MD("camera_create"),&VisualServer::camera_create);
+ ObjectTypeDB::bind_method(_MD("camera_set_perspective"),&VisualServer::camera_set_perspective);
+ ObjectTypeDB::bind_method(_MD("camera_set_orthogonal"),&VisualServer::_camera_set_orthogonal);
+ ObjectTypeDB::bind_method(_MD("camera_set_transform"),&VisualServer::camera_set_transform);
+
+
+ ObjectTypeDB::bind_method(_MD("viewport_create"),&VisualServer::viewport_create);
+ ObjectTypeDB::bind_method(_MD("viewport_set_rect"),&VisualServer::_viewport_set_rect);
+ ObjectTypeDB::bind_method(_MD("viewport_get_rect"),&VisualServer::_viewport_get_rect);
+ ObjectTypeDB::bind_method(_MD("viewport_attach_camera"),&VisualServer::viewport_attach_camera,DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("viewport_get_attached_camera"),&VisualServer::viewport_get_attached_camera);
+ ObjectTypeDB::bind_method(_MD("viewport_get_scenario"),&VisualServer::viewport_get_scenario);
+ ObjectTypeDB::bind_method(_MD("viewport_attach_canvas"),&VisualServer::viewport_attach_canvas);
+ ObjectTypeDB::bind_method(_MD("viewport_remove_canvas"),&VisualServer::viewport_remove_canvas);
+ ObjectTypeDB::bind_method(_MD("viewport_set_global_canvas_transform"),&VisualServer::viewport_set_global_canvas_transform);
+
+ ObjectTypeDB::bind_method(_MD("scenario_create"),&VisualServer::scenario_create);
+ ObjectTypeDB::bind_method(_MD("scenario_set_debug"),&VisualServer::scenario_set_debug);
+
+
+ ObjectTypeDB::bind_method(_MD("instance_create"),&VisualServer::instance_create,DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("instance_get_base"),&VisualServer::instance_get_base);
+ ObjectTypeDB::bind_method(_MD("instance_get_base_aabb"),&VisualServer::instance_get_base);
+ ObjectTypeDB::bind_method(_MD("instance_set_transform"),&VisualServer::instance_set_transform);
+ ObjectTypeDB::bind_method(_MD("instance_get_transform"),&VisualServer::instance_get_transform);
+ ObjectTypeDB::bind_method(_MD("instance_attach_object_instance_ID"),&VisualServer::instance_attach_object_instance_ID);
+ ObjectTypeDB::bind_method(_MD("instance_get_object_instance_ID"),&VisualServer::instance_get_object_instance_ID);
+ ObjectTypeDB::bind_method(_MD("instance_attach_skeleton"),&VisualServer::instance_attach_skeleton);
+ ObjectTypeDB::bind_method(_MD("instance_get_skeleton"),&VisualServer::instance_get_skeleton);
+ ObjectTypeDB::bind_method(_MD("instance_set_room"),&VisualServer::instance_set_room);
+ ObjectTypeDB::bind_method(_MD("instance_get_room"),&VisualServer::instance_get_room);
+
+ ObjectTypeDB::bind_method(_MD("instance_set_exterior"),&VisualServer::instance_set_exterior);
+ ObjectTypeDB::bind_method(_MD("instance_is_exterior"),&VisualServer::instance_is_exterior);
+
+ ObjectTypeDB::bind_method(_MD("instances_cull_aabb"),&VisualServer::instances_cull_aabb);
+ ObjectTypeDB::bind_method(_MD("instances_cull_ray"),&VisualServer::instances_cull_ray);
+ ObjectTypeDB::bind_method(_MD("instances_cull_convex"),&VisualServer::instances_cull_ray);
+
+
+
+ ObjectTypeDB::bind_method(_MD("instance_geometry_override_material_param"),&VisualServer::instance_get_room);
+ ObjectTypeDB::bind_method(_MD("instance_geometry_get_material_param"),&VisualServer::instance_get_room);
+
+ ObjectTypeDB::bind_method(_MD("get_test_cube"),&VisualServer::get_test_cube);
+
+#endif
+ ObjectTypeDB::bind_method(_MD("canvas_create"),&VisualServer::canvas_create);
+ ObjectTypeDB::bind_method(_MD("canvas_item_create"),&VisualServer::canvas_item_create);
+ ObjectTypeDB::bind_method(_MD("canvas_item_set_parent"),&VisualServer::canvas_item_set_parent);
+ ObjectTypeDB::bind_method(_MD("canvas_item_get_parent"),&VisualServer::canvas_item_get_parent);
+ ObjectTypeDB::bind_method(_MD("canvas_item_set_transform"),&VisualServer::canvas_item_set_transform);
+ ObjectTypeDB::bind_method(_MD("canvas_item_set_custom_rect"),&VisualServer::canvas_item_set_custom_rect);
+ ObjectTypeDB::bind_method(_MD("canvas_item_set_clip"),&VisualServer::canvas_item_set_clip);
+ ObjectTypeDB::bind_method(_MD("canvas_item_set_opacity"),&VisualServer::canvas_item_set_opacity);
+ ObjectTypeDB::bind_method(_MD("canvas_item_get_opacity"),&VisualServer::canvas_item_get_opacity);
+ ObjectTypeDB::bind_method(_MD("canvas_item_set_self_opacity"),&VisualServer::canvas_item_set_self_opacity);
+ ObjectTypeDB::bind_method(_MD("canvas_item_get_self_opacity"),&VisualServer::canvas_item_get_self_opacity);
+
+ ObjectTypeDB::bind_method(_MD("canvas_item_add_line"),&VisualServer::canvas_item_add_line, DEFVAL(1.0));
+ ObjectTypeDB::bind_method(_MD("canvas_item_add_rect"),&VisualServer::canvas_item_add_rect);
+ ObjectTypeDB::bind_method(_MD("canvas_item_add_texture_rect"),&VisualServer::canvas_item_add_texture_rect, DEFVAL(Color(1,1,1)));
+ ObjectTypeDB::bind_method(_MD("canvas_item_add_texture_rect_region"),&VisualServer::canvas_item_add_texture_rect_region, DEFVAL(Color(1,1,1)));
+ ObjectTypeDB::bind_method(_MD("canvas_item_add_style_box"),&VisualServer::_canvas_item_add_style_box, DEFVAL(Color(1,1,1)));
+// ObjectTypeDB::bind_method(_MD("canvas_item_add_primitive"),&VisualServer::canvas_item_add_primitive,DEFVAL(Vector<Vector2>()),DEFVAL(RID()));
+ ObjectTypeDB::bind_method(_MD("canvas_item_add_circle"),&VisualServer::canvas_item_add_circle);
+
+ ObjectTypeDB::bind_method(_MD("viewport_set_canvas_transform"),&VisualServer::viewport_set_canvas_transform);
+
+ ObjectTypeDB::bind_method(_MD("canvas_item_clear"),&VisualServer::canvas_item_clear);
+ ObjectTypeDB::bind_method(_MD("canvas_item_raise"),&VisualServer::canvas_item_raise);
+
+
+ ObjectTypeDB::bind_method(_MD("cursor_set_rotation"),&VisualServer::cursor_set_rotation);
+ ObjectTypeDB::bind_method(_MD("cursor_set_texture"),&VisualServer::cursor_set_texture);
+ ObjectTypeDB::bind_method(_MD("cursor_set_visible"),&VisualServer::cursor_set_visible);
+ ObjectTypeDB::bind_method(_MD("cursor_set_pos"),&VisualServer::cursor_set_pos);
+
+ ObjectTypeDB::bind_method(_MD("black_bars_set_margins","left","top","right","bottom"),&VisualServer::black_bars_set_margins);
+
+ ObjectTypeDB::bind_method(_MD("make_sphere_mesh"),&VisualServer::make_sphere_mesh);
+ ObjectTypeDB::bind_method(_MD("mesh_add_surface_from_planes"),&VisualServer::mesh_add_surface_from_planes);
+
+ ObjectTypeDB::bind_method(_MD("draw"),&VisualServer::draw);
+ ObjectTypeDB::bind_method(_MD("free"),&VisualServer::free);
+
+ ObjectTypeDB::bind_method(_MD("set_default_clear_color"),&VisualServer::set_default_clear_color);
+
+ ObjectTypeDB::bind_method(_MD("get_render_info"),&VisualServer::get_render_info);
+
+ BIND_CONSTANT( NO_INDEX_ARRAY );
+ BIND_CONSTANT( CUSTOM_ARRAY_SIZE );
+ BIND_CONSTANT( ARRAY_WEIGHTS_SIZE );
+ BIND_CONSTANT( MAX_PARTICLE_COLOR_PHASES );
+ BIND_CONSTANT( MAX_PARTICLE_ATTRACTORS );
+ BIND_CONSTANT( MAX_CURSORS );
+
+ BIND_CONSTANT( TEXTURE_FLAG_MIPMAPS );
+ BIND_CONSTANT( TEXTURE_FLAG_REPEAT );
+ BIND_CONSTANT( TEXTURE_FLAG_FILTER );
+ BIND_CONSTANT( TEXTURE_FLAG_CUBEMAP );
+ BIND_CONSTANT( TEXTURE_FLAGS_DEFAULT );
+
+ BIND_CONSTANT( CUBEMAP_LEFT );
+ BIND_CONSTANT( CUBEMAP_RIGHT );
+ BIND_CONSTANT( CUBEMAP_BOTTOM );
+ BIND_CONSTANT( CUBEMAP_TOP );
+ BIND_CONSTANT( CUBEMAP_FRONT );
+ BIND_CONSTANT( CUBEMAP_BACK );
+
+ BIND_CONSTANT( SHADER_MATERIAL ); ///< param 0: name
+ BIND_CONSTANT( SHADER_POST_PROCESS ); ///< param 0: name
+
+ BIND_CONSTANT( MATERIAL_FLAG_VISIBLE );
+ BIND_CONSTANT( MATERIAL_FLAG_DOUBLE_SIDED );
+ BIND_CONSTANT( MATERIAL_FLAG_INVERT_FACES );
+ BIND_CONSTANT( MATERIAL_FLAG_UNSHADED );
+ BIND_CONSTANT( MATERIAL_FLAG_ONTOP );
+ BIND_CONSTANT( MATERIAL_FLAG_WIREFRAME );
+ BIND_CONSTANT( MATERIAL_FLAG_BILLBOARD );
+ BIND_CONSTANT( MATERIAL_FLAG_MAX );
+
+ BIND_CONSTANT( MATERIAL_BLEND_MODE_MIX );
+ BIND_CONSTANT( MATERIAL_BLEND_MODE_ADD );
+ BIND_CONSTANT( MATERIAL_BLEND_MODE_SUB );
+ BIND_CONSTANT( MATERIAL_BLEND_MODE_MUL );
+
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_DIFFUSE );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_DETAIL );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_SPECULAR );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_EMISSION );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_SPECULAR_EXP );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_GLOW );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_NORMAL );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_SHADE_PARAM );
+ BIND_CONSTANT( FIXED_MATERIAL_PARAM_MAX );
+
+
+
+ BIND_CONSTANT( FIXED_MATERIAL_TEXCOORD_SPHERE );
+ BIND_CONSTANT( FIXED_MATERIAL_TEXCOORD_UV );
+ BIND_CONSTANT( FIXED_MATERIAL_TEXCOORD_UV_TRANSFORM );
+ BIND_CONSTANT( FIXED_MATERIAL_TEXCOORD_UV2 );
+
+
+ BIND_CONSTANT( ARRAY_VERTEX );
+ BIND_CONSTANT( ARRAY_NORMAL );
+ BIND_CONSTANT( ARRAY_TANGENT );
+ BIND_CONSTANT( ARRAY_COLOR );
+ BIND_CONSTANT( ARRAY_TEX_UV );
+ BIND_CONSTANT( ARRAY_BONES );
+ BIND_CONSTANT( ARRAY_WEIGHTS );
+ BIND_CONSTANT( ARRAY_INDEX );
+ BIND_CONSTANT( ARRAY_MAX );
+
+ BIND_CONSTANT( ARRAY_FORMAT_VERTEX );
+ BIND_CONSTANT( ARRAY_FORMAT_NORMAL );
+ BIND_CONSTANT( ARRAY_FORMAT_TANGENT );
+ BIND_CONSTANT( ARRAY_FORMAT_COLOR );
+ BIND_CONSTANT( ARRAY_FORMAT_TEX_UV );
+ BIND_CONSTANT( ARRAY_FORMAT_BONES );
+ BIND_CONSTANT( ARRAY_FORMAT_WEIGHTS );
+ BIND_CONSTANT( ARRAY_FORMAT_INDEX );
+
+ BIND_CONSTANT( PRIMITIVE_POINTS );
+ BIND_CONSTANT( PRIMITIVE_LINES );
+ BIND_CONSTANT( PRIMITIVE_LINE_STRIP );
+ BIND_CONSTANT( PRIMITIVE_LINE_LOOP );
+ BIND_CONSTANT( PRIMITIVE_TRIANGLES );
+ BIND_CONSTANT( PRIMITIVE_TRIANGLE_STRIP );
+ BIND_CONSTANT( PRIMITIVE_TRIANGLE_FAN );
+ BIND_CONSTANT( PRIMITIVE_MAX );
+
+ BIND_CONSTANT( PARTICLE_LIFETIME );
+ BIND_CONSTANT( PARTICLE_SPREAD );
+ BIND_CONSTANT( PARTICLE_GRAVITY );
+ BIND_CONSTANT( PARTICLE_LINEAR_VELOCITY );
+ BIND_CONSTANT( PARTICLE_ANGULAR_VELOCITY );
+ BIND_CONSTANT( PARTICLE_LINEAR_ACCELERATION );
+ BIND_CONSTANT( PARTICLE_RADIAL_ACCELERATION );
+ BIND_CONSTANT( PARTICLE_TANGENTIAL_ACCELERATION );
+ BIND_CONSTANT( PARTICLE_INITIAL_SIZE );
+ BIND_CONSTANT( PARTICLE_FINAL_SIZE );
+ BIND_CONSTANT( PARTICLE_INITIAL_ANGLE );
+ BIND_CONSTANT( PARTICLE_HEIGHT );
+ BIND_CONSTANT( PARTICLE_HEIGHT_SPEED_SCALE );
+ BIND_CONSTANT( PARTICLE_VAR_MAX );
+
+ BIND_CONSTANT( LIGHT_DIRECTIONAL );
+ BIND_CONSTANT( LIGHT_OMNI );
+ BIND_CONSTANT( LIGHT_SPOT );
+
+ BIND_CONSTANT( LIGHT_COLOR_AMBIENT );
+ BIND_CONSTANT( LIGHT_COLOR_DIFFUSE );
+ BIND_CONSTANT( LIGHT_COLOR_SPECULAR );
+
+ BIND_CONSTANT( LIGHT_PARAM_SPOT_ATTENUATION );
+ BIND_CONSTANT( LIGHT_PARAM_SPOT_ANGLE );
+ BIND_CONSTANT( LIGHT_PARAM_RADIUS );
+ BIND_CONSTANT( LIGHT_PARAM_ENERGY );
+ BIND_CONSTANT( LIGHT_PARAM_ATTENUATION );
+ BIND_CONSTANT( LIGHT_PARAM_MAX );
+
+ BIND_CONSTANT( SCENARIO_DEBUG_DISABLED );
+ BIND_CONSTANT( SCENARIO_DEBUG_WIREFRAME );
+ BIND_CONSTANT( SCENARIO_DEBUG_OVERDRAW );
+
+ BIND_CONSTANT( INSTANCE_MESH );
+ BIND_CONSTANT( INSTANCE_MULTIMESH );
+
+ BIND_CONSTANT( INSTANCE_PARTICLES );
+ BIND_CONSTANT( INSTANCE_LIGHT );
+ BIND_CONSTANT( INSTANCE_ROOM );
+ BIND_CONSTANT( INSTANCE_PORTAL );
+ BIND_CONSTANT( INSTANCE_GEOMETRY_MASK );
+
+
+ BIND_CONSTANT( INFO_OBJECTS_IN_FRAME );
+ BIND_CONSTANT( INFO_VERTICES_IN_FRAME );
+ BIND_CONSTANT( INFO_MATERIAL_CHANGES_IN_FRAME );
+ BIND_CONSTANT( INFO_SHADER_CHANGES_IN_FRAME );
+ BIND_CONSTANT( INFO_SURFACE_CHANGES_IN_FRAME );
+ BIND_CONSTANT( INFO_DRAW_CALLS_IN_FRAME );
+ BIND_CONSTANT( INFO_USAGE_VIDEO_MEM_TOTAL );
+ BIND_CONSTANT( INFO_VIDEO_MEM_USED );
+ BIND_CONSTANT( INFO_TEXTURE_MEM_USED );
+ BIND_CONSTANT( INFO_VERTEX_MEM_USED );
+
+
+}
+
+void VisualServer::_canvas_item_add_style_box(RID p_item, const Rect2& p_rect, RID p_texture,const Vector<float>& p_margins, const Color& p_modulate) {
+
+ ERR_FAIL_COND(p_margins.size()!=4);
+ canvas_item_add_style_box(p_item, p_rect, p_texture,Vector2(p_margins[0],p_margins[1]),Vector2(p_margins[2],p_margins[3]),true,p_modulate);
+}
+
+void VisualServer::_camera_set_orthogonal(RID p_camera,float p_size,float p_z_near,float p_z_far) {
+
+ camera_set_orthogonal(p_camera,p_size,p_z_near,p_z_far);
+}
+
+void VisualServer::_viewport_set_rect(RID p_viewport,const Rect2& p_rect) {
+
+ ViewportRect r;
+ r.x=p_rect.pos.x;
+ r.y=p_rect.pos.y;
+ r.width=p_rect.size.x;
+ r.height=p_rect.size.y;
+ viewport_set_rect(p_viewport,r);
+}
+Rect2 VisualServer::_viewport_get_rect(RID p_viewport) const {
+
+ ViewportRect r=viewport_get_rect(p_viewport);
+ return Rect2(r.x,r.y,r.width,r.height);
+}
+
+
+
+
+
+void VisualServer::mesh_add_surface_from_mesh_data( RID p_mesh, const Geometry::MeshData& p_mesh_data) {
+
+#if 1
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+
+ for (int i=0;i<p_mesh_data.faces.size();i++) {
+
+ const Geometry::MeshData::Face& f = p_mesh_data.faces[i];
+
+ for (int j=2;j<f.indices.size();j++) {
+
+#define _ADD_VERTEX(m_idx)\
+ vertices.push_back( p_mesh_data.vertices[ f.indices[m_idx] ] );\
+ normals.push_back( f.plane.normal );
+
+ _ADD_VERTEX( 0 );
+ _ADD_VERTEX( j-1 );
+ _ADD_VERTEX( j );
+ }
+ }
+
+ int s = mesh_get_surface_count(p_mesh);
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[ARRAY_VERTEX]=vertices;
+ d[ARRAY_NORMAL]=normals;
+ mesh_add_surface(p_mesh,PRIMITIVE_TRIANGLES, d);
+
+#else
+
+
+ DVector<Vector3> vertices;
+
+
+
+ for (int i=0;i<p_mesh_data.edges.size();i++) {
+
+ const Geometry::MeshData::Edge& f = p_mesh_data.edges[i];
+ vertices.push_back(p_mesh_data.vertices[ f.a]);
+ vertices.push_back(p_mesh_data.vertices[ f.b]);
+ }
+
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[ARRAY_VERTEX]=vertices;
+ mesh_add_surface(p_mesh,PRIMITIVE_LINES, d);
+
+
+
+
+#endif
+
+}
+
+void VisualServer::mesh_add_surface_from_planes( RID p_mesh, const DVector<Plane>& p_planes) {
+
+
+ Geometry::MeshData mdata = Geometry::build_convex_mesh(p_planes);
+ mesh_add_surface_from_mesh_data(p_mesh,mdata);
+
+}
+
+RID VisualServer::instance_create2(RID p_base, RID p_scenario) {
+
+ RID instance = instance_create();
+ instance_set_base(instance,p_base);
+ instance_set_scenario(instance,p_scenario);
+ return instance;
+}
+
+
+VisualServer::VisualServer() {
+
+// ERR_FAIL_COND(singleton);
+ singleton=this;
+ mm_policy=GLOBAL_DEF("render/mipmap_policy",0);
+ if (mm_policy<0 || mm_policy>2)
+ mm_policy=0;
+
+}
+
+
+VisualServer::~VisualServer() {
+
+ singleton=NULL;
+}
+
+
diff --git a/servers/visual_server.h b/servers/visual_server.h
new file mode 100644
index 0000000000..b3922ab4fc
--- /dev/null
+++ b/servers/visual_server.h
@@ -0,0 +1,971 @@
+/*************************************************************************/
+/* visual_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 VISUAL_SERVER_H
+#define VISUAL_SERVER_H
+
+
+#include "rid.h"
+#include "variant.h"
+#include "math_2d.h"
+#include "bsp_tree.h"
+#include "geometry.h"
+#include "object.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class VisualServer : public Object {
+
+ OBJ_TYPE( VisualServer, Object );
+
+ static VisualServer *singleton;
+
+ int mm_policy;
+
+ DVector<String> _shader_get_param_list(RID p_shader) const;
+ void _camera_set_orthogonal(RID p_camera,float p_size,float p_z_near,float p_z_far);
+ void _viewport_set_rect(RID p_viewport,const Rect2& p_rect);
+ Rect2 _viewport_get_rect(RID p_viewport) const;
+ void _canvas_item_add_style_box(RID p_item, const Rect2& p_rect, RID p_texture,const Vector<float>& p_margins, const Color& p_modulate=Color(1,1,1));
+protected:
+ RID _make_test_cube();
+ RID test_texture;
+
+ static VisualServer* (*create_func)();
+ static void _bind_methods();
+public:
+
+ static VisualServer *get_singleton();
+ static VisualServer *create();
+
+ enum MipMapPolicy {
+
+ MIPMAPS_ENABLED,
+ MIPMAPS_ENABLED_FOR_PO2,
+ MIPMAPS_DISABLED
+ };
+
+
+ virtual void set_mipmap_policy(MipMapPolicy p_policy);
+ virtual MipMapPolicy get_mipmap_policy() const;
+
+
+
+ enum {
+
+ NO_INDEX_ARRAY=-1,
+ CUSTOM_ARRAY_SIZE=8,
+ ARRAY_WEIGHTS_SIZE=4,
+ MAX_PARTICLE_COLOR_PHASES=4,
+ MAX_PARTICLE_ATTRACTORS=4,
+
+ MAX_CURSORS = 8,
+ };
+
+ /* TEXTURE API */
+
+ enum TextureFlags {
+ TEXTURE_FLAG_MIPMAPS=1, /// Enable automatic mipmap generation - when available
+ TEXTURE_FLAG_REPEAT=2, /// Repeat texture (Tiling), otherwise Clamping
+ TEXTURE_FLAG_FILTER=4, /// Create texure with linear (or available) filter
+ TEXTURE_FLAG_CUBEMAP=8,
+ TEXTURE_FLAG_VIDEO_SURFACE=16,
+ TEXTURE_FLAGS_DEFAULT=TEXTURE_FLAG_REPEAT|TEXTURE_FLAG_MIPMAPS|TEXTURE_FLAG_FILTER
+ };
+
+ enum CubeMapSide {
+
+ CUBEMAP_LEFT,
+ CUBEMAP_RIGHT,
+ CUBEMAP_BOTTOM,
+ CUBEMAP_TOP,
+ CUBEMAP_FRONT,
+ CUBEMAP_BACK
+ };
+
+
+ virtual RID texture_create()=0;
+ RID texture_create_from_image(const Image& p_image,uint32_t p_flags=TEXTURE_FLAGS_DEFAULT); // helper
+ virtual void texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags=TEXTURE_FLAGS_DEFAULT)=0;
+ virtual void texture_set_data(RID p_texture,const Image& p_image,CubeMapSide p_cube_side=CUBEMAP_LEFT)=0;
+ virtual Image texture_get_data(RID p_texture,CubeMapSide p_cube_side=CUBEMAP_LEFT) const=0;
+ virtual void texture_set_flags(RID p_texture,uint32_t p_flags) =0;
+ virtual uint32_t texture_get_flags(RID p_texture) const=0;
+ virtual Image::Format texture_get_format(RID p_texture) const=0;
+ virtual uint32_t texture_get_width(RID p_texture) const=0;
+ virtual uint32_t texture_get_height(RID p_texture) const=0;
+ virtual void texture_set_size_override(RID p_texture,int p_width, int p_height)=0;
+ virtual bool texture_can_stream(RID p_texture) const=0;
+ virtual void texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const=0;
+
+
+
+ /* SHADER API */
+
+ enum ShaderMode {
+
+ SHADER_MATERIAL,
+ SHADER_CANVAS_ITEM,
+ SHADER_POST_PROCESS,
+ };
+
+ virtual RID shader_create(ShaderMode p_mode=SHADER_MATERIAL)=0;
+
+ virtual void shader_set_mode(RID p_shader,ShaderMode p_mode)=0;
+ virtual ShaderMode shader_get_mode(RID p_shader) const=0;
+
+ virtual void shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs=0,int p_fragment_ofs=0)=0;
+ virtual String shader_get_fragment_code(RID p_shader) const=0;
+ virtual String shader_get_vertex_code(RID p_shader) const=0;
+ virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const=0;
+
+
+ /* COMMON MATERIAL API */
+
+ virtual RID material_create()=0;
+
+ virtual void material_set_shader(RID p_shader_material, RID p_shader)=0;
+ virtual RID material_get_shader(RID p_shader_material) const=0;
+
+ virtual void material_set_param(RID p_material, const StringName& p_param, const Variant& p_value)=0;
+ virtual Variant material_get_param(RID p_material, const StringName& p_param) const=0;
+
+ enum MaterialFlag {
+ MATERIAL_FLAG_VISIBLE,
+ MATERIAL_FLAG_DOUBLE_SIDED,
+ MATERIAL_FLAG_INVERT_FACES, ///< Invert front/back of the object
+ MATERIAL_FLAG_UNSHADED,
+ MATERIAL_FLAG_ONTOP,
+ MATERIAL_FLAG_WIREFRAME,
+ MATERIAL_FLAG_BILLBOARD,
+ MATERIAL_FLAG_MAX,
+ };
+
+ virtual void material_set_flag(RID p_material, MaterialFlag p_flag,bool p_enabled)=0;
+ virtual bool material_get_flag(RID p_material,MaterialFlag p_flag) const=0;
+
+ enum MaterialShadeModel {
+ MATERIAL_SHADE_MODEL_LAMBERT,
+ MATERIAL_SHADE_MODEL_LAMBERT_WRAP,
+ MATERIAL_SHADE_MODEL_TOON
+ };
+
+ /* FIXED MATERIAL */
+
+
+
+ virtual void material_set_shade_model(RID p_material, MaterialShadeModel p_model)=0;
+ virtual MaterialShadeModel material_get_shade_model(RID p_material) const=0;
+
+ enum MaterialHint {
+
+ MATERIAL_HINT_DECAL,
+ MATERIAL_HINT_OPAQUE_PRE_PASS,
+ MATERIAL_HINT_NO_SHADOW,
+ MATERIAL_HINT_NO_DEPTH_DRAW,
+ MATERIAL_HINT_MAX
+ };
+
+ virtual void material_set_hint(RID p_material, MaterialHint p_hint,bool p_enabled)=0;
+ virtual bool material_get_hint(RID p_material,MaterialHint p_hint) const=0;
+
+ enum MaterialBlendMode {
+ MATERIAL_BLEND_MODE_MIX, //default
+ MATERIAL_BLEND_MODE_ADD,
+ MATERIAL_BLEND_MODE_SUB,
+ MATERIAL_BLEND_MODE_MUL
+ };
+
+
+ virtual void material_set_blend_mode(RID p_material,MaterialBlendMode p_mode)=0;
+ virtual MaterialBlendMode material_get_blend_mode(RID p_material) const=0;
+
+ virtual void material_set_line_width(RID p_material,float p_line_width)=0;
+ virtual float material_get_line_width(RID p_material) const=0;
+
+
+ //fixed material api
+
+ virtual RID fixed_material_create()=0;
+
+ enum FixedMaterialParam {
+
+ FIXED_MATERIAL_PARAM_DIFFUSE,
+ FIXED_MATERIAL_PARAM_DETAIL,
+ FIXED_MATERIAL_PARAM_SPECULAR,
+ FIXED_MATERIAL_PARAM_EMISSION,
+ FIXED_MATERIAL_PARAM_SPECULAR_EXP,
+ FIXED_MATERIAL_PARAM_GLOW,
+ FIXED_MATERIAL_PARAM_NORMAL,
+ FIXED_MATERIAL_PARAM_SHADE_PARAM,
+ FIXED_MATERIAL_PARAM_MAX
+ };
+
+ enum FixedMaterialTexCoordMode {
+
+ FIXED_MATERIAL_TEXCOORD_UV,
+ FIXED_MATERIAL_TEXCOORD_UV_TRANSFORM,
+ FIXED_MATERIAL_TEXCOORD_UV2,
+ FIXED_MATERIAL_TEXCOORD_SPHERE
+ };
+
+ enum FixedMaterialFlags {
+
+ FIXED_MATERIAL_FLAG_USE_ALPHA,
+ FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,
+ FIXED_MATERIAL_FLAG_USE_POINT_SIZE,
+ FIXED_MATERIAL_FLAG_MAX,
+ };
+
+
+ virtual void fixed_material_set_flag(RID p_material, FixedMaterialFlags p_flag, bool p_enabled)=0;
+ virtual bool fixed_material_get_flag(RID p_material, FixedMaterialFlags p_flag) const=0;
+
+ virtual void fixed_material_set_param(RID p_material, FixedMaterialParam p_parameter, const Variant& p_value)=0;
+ virtual Variant fixed_material_get_param(RID p_material,FixedMaterialParam p_parameter) const=0;
+
+ virtual void fixed_material_set_texture(RID p_material,FixedMaterialParam p_parameter, RID p_texture)=0;
+ virtual RID fixed_material_get_texture(RID p_material,FixedMaterialParam p_parameter) const=0;
+
+ virtual void fixed_material_set_detail_blend_mode(RID p_material,MaterialBlendMode p_mode)=0;
+ virtual MaterialBlendMode fixed_material_get_detail_blend_mode(RID p_material) const=0;
+
+ virtual void fixed_material_set_texcoord_mode(RID p_material,FixedMaterialParam p_parameter, FixedMaterialTexCoordMode p_mode)=0;
+ virtual FixedMaterialTexCoordMode fixed_material_get_texcoord_mode(RID p_material,FixedMaterialParam p_parameter) const=0;
+
+ virtual void fixed_material_set_uv_transform(RID p_material,const Transform& p_transform)=0;
+ virtual Transform fixed_material_get_uv_transform(RID p_material) const=0;
+
+ virtual void fixed_material_set_point_size(RID p_material,float p_size)=0;
+ virtual float fixed_material_get_point_size(RID p_material) const=0;
+
+ /* MESH API */
+
+ enum ArrayType {
+
+ ARRAY_VERTEX=0,
+ ARRAY_NORMAL=1,
+ ARRAY_TANGENT=2,
+ ARRAY_COLOR=3,
+ ARRAY_TEX_UV=4,
+ ARRAY_TEX_UV2=5,
+ ARRAY_BONES=6,
+ ARRAY_WEIGHTS=7,
+ ARRAY_INDEX=8,
+ ARRAY_MAX=9
+ };
+
+ enum ArrayFormat {
+ /* ARRAY FORMAT FLAGS */
+ ARRAY_FORMAT_VERTEX=1<<ARRAY_VERTEX, // mandatory
+ ARRAY_FORMAT_NORMAL=1<<ARRAY_NORMAL,
+ ARRAY_FORMAT_TANGENT=1<<ARRAY_TANGENT,
+ ARRAY_FORMAT_COLOR=1<<ARRAY_COLOR,
+ ARRAY_FORMAT_TEX_UV=1<<ARRAY_TEX_UV,
+ ARRAY_FORMAT_TEX_UV2=1<<ARRAY_TEX_UV2,
+ ARRAY_FORMAT_BONES=1<<ARRAY_BONES,
+ ARRAY_FORMAT_WEIGHTS=1<<ARRAY_WEIGHTS,
+ ARRAY_FORMAT_INDEX=1<<ARRAY_INDEX,
+ };
+
+ enum PrimitiveType {
+ PRIMITIVE_POINTS=0,
+ PRIMITIVE_LINES=1,
+ PRIMITIVE_LINE_STRIP=2,
+ PRIMITIVE_LINE_LOOP=3,
+ PRIMITIVE_TRIANGLES=4,
+ PRIMITIVE_TRIANGLE_STRIP=5,
+ PRIMITIVE_TRIANGLE_FAN=6,
+ PRIMITIVE_MAX=7,
+ };
+
+ virtual RID mesh_create()=0;
+
+ virtual void mesh_add_surface(RID p_mesh,PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes=Array(),bool p_alpha_sort=false)=0;
+ virtual Array mesh_get_surface_arrays(RID p_mesh,int p_surface) const=0;
+ virtual Array mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const=0;
+
+
+ virtual void mesh_add_custom_surface(RID p_mesh,const Variant& p_dat)=0; //this is used by each platform in a different way
+ virtual void mesh_set_morph_target_count(RID p_mesh,int p_amount)=0;
+ virtual int mesh_get_morph_target_count(RID p_mesh) const=0;
+
+ enum MorphTargetMode {
+ MORPH_MODE_NORMALIZED,
+ MORPH_MODE_RELATIVE,
+ };
+
+ virtual void mesh_set_morph_target_mode(RID p_mesh,MorphTargetMode p_mode)=0;
+ virtual MorphTargetMode mesh_get_morph_target_mode(RID p_mesh) const=0;
+
+ virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned=false)=0;
+ virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const=0;
+
+ virtual int mesh_surface_get_array_len(RID p_mesh, int p_surface) const=0;
+ virtual int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const=0;
+ virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const=0;
+ virtual PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const=0;
+
+ virtual void mesh_remove_surface(RID p_mesh,int p_index)=0;
+ virtual int mesh_get_surface_count(RID p_mesh) const=0;
+
+ /* MULTIMESH API */
+
+ virtual RID multimesh_create()=0;
+
+ virtual void multimesh_set_instance_count(RID p_multimesh,int p_count)=0;
+ virtual int multimesh_get_instance_count(RID p_multimesh) const=0;
+
+ virtual void multimesh_set_mesh(RID p_multimesh,RID p_mesh)=0;
+ virtual void multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb)=0;
+ virtual void multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform)=0;
+ virtual void multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color)=0;
+
+ virtual RID multimesh_get_mesh(RID p_multimesh) const=0;
+ virtual AABB multimesh_get_aabb(RID p_multimesh,const AABB& p_aabb) const=0;;
+
+ virtual Transform multimesh_instance_get_transform(RID p_multimesh,int p_index) const=0;
+ virtual Color multimesh_instance_get_color(RID p_multimesh,int p_index) const=0;
+
+ virtual void multimesh_set_visible_instances(RID p_multimesh,int p_visible)=0;
+ virtual int multimesh_get_visible_instances(RID p_multimesh) const=0;
+
+
+ /* PARTICLES API */
+
+ virtual RID particles_create()=0;
+
+ enum ParticleVariable {
+ PARTICLE_LIFETIME,
+ PARTICLE_SPREAD,
+ PARTICLE_GRAVITY,
+ PARTICLE_LINEAR_VELOCITY,
+ PARTICLE_ANGULAR_VELOCITY,
+ PARTICLE_LINEAR_ACCELERATION,
+ PARTICLE_RADIAL_ACCELERATION,
+ PARTICLE_TANGENTIAL_ACCELERATION,
+ PARTICLE_DAMPING,
+ PARTICLE_INITIAL_SIZE,
+ PARTICLE_FINAL_SIZE,
+ PARTICLE_INITIAL_ANGLE,
+ PARTICLE_HEIGHT,
+ PARTICLE_HEIGHT_SPEED_SCALE,
+ PARTICLE_VAR_MAX
+ };
+
+ virtual void particles_set_amount(RID p_particles, int p_amount)=0;
+ virtual int particles_get_amount(RID p_particles) const=0;
+
+ virtual void particles_set_emitting(RID p_particles, bool p_emitting)=0;
+ virtual bool particles_is_emitting(RID p_particles) const=0;
+
+ virtual void particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility)=0;
+ virtual AABB particles_get_visibility_aabb(RID p_particles) const=0;
+
+ virtual void particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents)=0;
+ virtual Vector3 particles_get_emission_half_extents(RID p_particles) const=0;
+
+ virtual void particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity)=0;
+ virtual Vector3 particles_get_emission_base_velocity(RID p_particles) const=0;
+
+ virtual void particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points)=0;
+ virtual DVector<Vector3> particles_get_emission_points(RID p_particles) const=0;
+
+ virtual void particles_set_gravity_normal(RID p_particles, const Vector3& p_normal)=0;
+ virtual Vector3 particles_get_gravity_normal(RID p_particles) const=0;
+
+ virtual void particles_set_variable(RID p_particles, ParticleVariable p_variable,float p_value)=0;
+ virtual float particles_get_variable(RID p_particles, ParticleVariable p_variable) const=0;
+
+ virtual void particles_set_randomness(RID p_particles, ParticleVariable p_variable,float p_randomness)=0;
+ virtual float particles_get_randomness(RID p_particles, ParticleVariable p_variable) const=0;
+
+ virtual void particles_set_color_phases(RID p_particles, int p_phases)=0;
+ virtual int particles_get_color_phases(RID p_particles) const=0;
+
+ virtual void particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos)=0;
+ virtual float particles_get_color_phase_pos(RID p_particles, int p_phase) const=0;
+
+ virtual void particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color)=0;
+ virtual Color particles_get_color_phase_color(RID p_particles, int p_phase) const=0;
+
+ virtual void particles_set_attractors(RID p_particles, int p_attractors)=0;
+ virtual int particles_get_attractors(RID p_particles) const=0;
+
+ virtual void particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos)=0;
+ virtual Vector3 particles_get_attractor_pos(RID p_particles,int p_attractor) const=0;
+
+ virtual void particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force)=0;
+ virtual float particles_get_attractor_strength(RID p_particles,int p_attractor) const=0;
+
+ virtual void particles_set_material(RID p_particles, RID p_material,bool p_owned=false)=0;
+ virtual RID particles_get_material(RID p_particles) const=0;
+
+ virtual void particles_set_height_from_velocity(RID p_particles, bool p_enable)=0;
+ virtual bool particles_has_height_from_velocity(RID p_particles) const=0;
+
+ virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable)=0;
+ virtual bool particles_is_using_local_coordinates(RID p_particles) const=0;
+
+ /* Light API */
+
+ enum LightType {
+ LIGHT_DIRECTIONAL,
+ LIGHT_OMNI,
+ LIGHT_SPOT
+ };
+
+ enum LightColor {
+
+ LIGHT_COLOR_AMBIENT,
+ LIGHT_COLOR_DIFFUSE,
+ LIGHT_COLOR_SPECULAR
+ };
+
+ enum LightParam {
+
+ LIGHT_PARAM_SPOT_ATTENUATION,
+ LIGHT_PARAM_SPOT_ANGLE,
+ LIGHT_PARAM_RADIUS,
+ LIGHT_PARAM_ENERGY,
+ LIGHT_PARAM_ATTENUATION,
+ LIGHT_PARAM_SHADOW_DARKENING,
+ LIGHT_PARAM_SHADOW_Z_OFFSET,
+ LIGHT_PARAM_SHADOW_Z_SLOPE_SCALE,
+ LIGHT_PARAM_MAX
+ };
+
+ virtual RID light_create(LightType p_type)=0;
+ virtual LightType light_get_type(RID p_light) const=0;
+
+ virtual void light_set_color(RID p_light,LightColor p_type, const Color& p_color)=0;
+ virtual Color light_get_color(RID p_light,LightColor p_type) const=0;
+
+ virtual void light_set_shadow(RID p_light,bool p_enabled)=0;
+ virtual bool light_has_shadow(RID p_light) const=0;
+
+ virtual void light_set_volumetric(RID p_light,bool p_enabled)=0;
+ virtual bool light_is_volumetric(RID p_light) const=0;
+
+ virtual void light_set_projector(RID p_light,RID p_texture)=0;
+ virtual RID light_get_projector(RID p_light) const=0;
+
+ virtual void light_set_param(RID p_light, LightParam p_var, float p_value)=0;
+ virtual float light_get_param(RID p_light, LightParam p_var) const=0;
+
+ enum LightOp {
+
+ LIGHT_OPERATOR_ADD,
+ LIGHT_OPERATOR_SUB
+ };
+
+ virtual void light_set_operator(RID p_light,LightOp p_op)=0;
+ virtual LightOp light_get_operator(RID p_light) const=0;
+
+ // omni light
+ enum LightOmniShadowMode {
+ LIGHT_OMNI_SHADOW_DEFAULT,
+ LIGHT_OMNI_SHADOW_DUAL_PARABOLOID,
+ LIGHT_OMNI_SHADOW_CUBEMAP
+ };
+
+ virtual void light_omni_set_shadow_mode(RID p_light,LightOmniShadowMode p_mode)=0;
+ virtual LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) const=0;
+
+ // directional light
+ enum LightDirectionalShadowMode {
+ LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL,
+ LIGHT_DIRECTIONAL_SHADOW_PERSPECTIVE,
+ LIGHT_DIRECTIONAL_SHADOW_PARALLEL_SPLIT
+ };
+
+ virtual void light_directional_set_shadow_mode(RID p_light,LightDirectionalShadowMode p_mode)=0;
+ virtual LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) const=0;
+
+ enum LightDirectionalShadowParam {
+
+ LIGHT_DIRECTIONAL_SHADOW_PARAM_MAX_DISTANCE,
+ LIGHT_DIRECTIONAL_SHADOW_PARAM_PSSM_SPLIT_WEIGHT,
+ LIGHT_DIRECTIONAL_SHADOW_PARAM_PSSM_ZOFFSET_SCALE,
+ };
+
+ virtual void light_directional_set_shadow_param(RID p_light,LightDirectionalShadowParam p_param, float p_value)=0;
+ virtual float light_directional_get_shadow_param(RID p_light,LightDirectionalShadowParam p_param) const=0;
+
+ //@TODO fallof model and all that stuff
+
+ /* SKELETON API */
+
+ virtual RID skeleton_create()=0;
+ virtual void skeleton_resize(RID p_skeleton,int p_bones)=0;
+ virtual int skeleton_get_bone_count(RID p_skeleton) const=0;
+ virtual void skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform)=0;
+ virtual Transform skeleton_bone_get_transform(RID p_skeleton,int p_bone)=0;
+
+ /* ROOM API */
+
+ virtual RID room_create()=0;
+ virtual void room_set_bounds(RID p_room, const BSP_Tree& p_bounds)=0;
+ virtual BSP_Tree room_get_bounds(RID p_room) const=0;
+
+ /* PORTAL API */
+
+ // portals are only (x/y) points, forming a convex shape, which its clockwise
+ // order points outside. (z is 0);
+
+ virtual RID portal_create()=0;
+ virtual void portal_set_shape(RID p_portal, const Vector<Point2>& p_shape)=0;
+ virtual Vector<Point2> portal_get_shape(RID p_portal) const=0;
+ virtual void portal_set_enabled(RID p_portal, bool p_enabled)=0;
+ virtual bool portal_is_enabled(RID p_portal) const=0;
+ virtual void portal_set_disable_distance(RID p_portal, float p_distance)=0;
+ virtual float portal_get_disable_distance(RID p_portal) const=0;
+ virtual void portal_set_disabled_color(RID p_portal, const Color& p_color)=0;
+ virtual Color portal_get_disabled_color(RID p_portal) const=0;
+ virtual void portal_set_connect_range(RID p_portal, float p_range) =0;
+ virtual float portal_get_connect_range(RID p_portal) const =0;
+
+ /* CAMERA API */
+
+ virtual RID camera_create()=0;
+ virtual void camera_set_perspective(RID p_camera,float p_fovy_degrees, float p_z_near, float p_z_far)=0;
+ virtual void camera_set_orthogonal(RID p_camera,float p_size, float p_z_near, float p_z_far)=0;
+ virtual void camera_set_transform(RID p_camera,const Transform& p_transform)=0;
+
+ virtual void camera_set_visible_layers(RID p_camera,uint32_t p_layers)=0;
+ virtual uint32_t camera_get_visible_layers(RID p_camera) const=0;
+
+ virtual void camera_set_environment(RID p_camera,RID p_env)=0;
+ virtual RID camera_get_environment(RID p_camera) const=0;
+
+ virtual void camera_set_use_vertical_aspect(RID p_camera,bool p_enable)=0;
+ virtual bool camera_is_using_vertical_aspect(RID p_camera,bool p_enable) const=0;
+
+/*
+ virtual void camera_add_layer(RID p_camera);
+ virtual void camera_layer_move_up(RID p_camera,int p_layer);
+ virtual void camera_layer_move_down(RID p_camera,int p_layer);
+ virtual void camera_layer_set_mask(RID p_camera,int p_layer,int p_mask);
+ virtual int camera_layer_get_mask(RID p_camera,int p_layer) const;
+
+ enum CameraLayerFlag {
+
+ FLAG_CLEAR_DEPTH,
+ FLAG_CLEAR_COLOR,
+ FLAG_IGNORE_FOG,
+ };
+ virtual void camera_layer_set_flag(RID p_camera,int p_layer,bool p_enable);
+ virtual bool camera_layer_get_flag(RID p_camera,int p_layer) const;
+
+*/
+
+
+ /* VIEWPORT API */
+
+ virtual RID viewport_create()=0;
+
+ virtual void viewport_attach_to_screen(RID p_viewport,int p_screen=0)=0;
+ virtual void viewport_detach(RID p_viewport)=0;
+
+ enum RenderTargetUpdateMode {
+ RENDER_TARGET_UPDATE_DISABLED,
+ RENDER_TARGET_UPDATE_ONCE, //then goes to disabled
+ RENDER_TARGET_UPDATE_WHEN_VISIBLE, // default
+ RENDER_TARGET_UPDATE_ALWAYS
+ };
+
+ virtual void viewport_set_as_render_target(RID p_viewport,bool p_enable)=0;
+ virtual void viewport_set_render_target_update_mode(RID p_viewport,RenderTargetUpdateMode p_mode)=0;
+ virtual RenderTargetUpdateMode viewport_get_render_target_update_mode(RID p_viewport) const=0;
+ virtual RID viewport_get_render_target_texture(RID p_viewport) const=0;
+
+ virtual void viewport_queue_screen_capture(RID p_viewport)=0;
+ virtual Image viewport_get_screen_capture(RID p_viewport) const=0;
+
+ struct ViewportRect {
+
+ int x,y,width,height;
+ ViewportRect() { x=y=width=height=0; }
+ };
+
+ virtual void viewport_set_rect(RID p_viewport,const ViewportRect& p_rect)=0;
+ virtual ViewportRect viewport_get_rect(RID p_viewport) const=0;
+
+ virtual void viewport_set_hide_scenario(RID p_viewport,bool p_hide)=0;
+ virtual void viewport_set_hide_canvas(RID p_viewport,bool p_hide)=0;
+
+ virtual void viewport_attach_camera(RID p_viewport,RID p_camera)=0;
+ virtual void viewport_set_scenario(RID p_viewport,RID p_scenario)=0;
+ virtual RID viewport_get_attached_camera(RID p_viewport) const=0;
+ virtual RID viewport_get_scenario(RID p_viewport) const=0;
+ virtual void viewport_attach_canvas(RID p_viewport,RID p_canvas)=0;
+ virtual void viewport_remove_canvas(RID p_viewport,RID p_canvas)=0;
+ virtual void viewport_set_canvas_transform(RID p_viewport,RID p_canvas,const Matrix32& p_offset)=0;
+ virtual Matrix32 viewport_get_canvas_transform(RID p_viewport,RID p_canvas) const=0;
+ virtual void viewport_set_transparent_background(RID p_viewport,bool p_enabled)=0;
+ virtual bool viewport_has_transparent_background(RID p_viewport) const=0;
+
+
+ virtual void viewport_set_global_canvas_transform(RID p_viewport,const Matrix32& p_transform)=0;
+ virtual Matrix32 viewport_get_global_canvas_transform(RID p_viewport) const=0;
+ virtual void viewport_set_canvas_layer(RID p_viewport,RID p_canvas,int p_layer)=0;
+
+
+
+ /* ENVIRONMENT API */
+
+ virtual RID environment_create()=0;
+
+ enum EnvironmentBG {
+
+ ENV_BG_KEEP,
+ ENV_BG_DEFAULT_COLOR,
+ ENV_BG_COLOR,
+ ENV_BG_TEXTURE,
+ ENV_BG_CUBEMAP,
+ ENV_BG_TEXTURE_RGBE,
+ ENV_BG_CUBEMAP_RGBE,
+ ENV_BG_MAX
+ };
+
+ virtual void environment_set_background(RID p_env,EnvironmentBG p_bg)=0;
+ virtual EnvironmentBG environment_get_background(RID p_env) const=0;
+
+ enum EnvironmentBGParam {
+
+ ENV_BG_PARAM_COLOR,
+ ENV_BG_PARAM_TEXTURE,
+ ENV_BG_PARAM_CUBEMAP,
+ ENV_BG_PARAM_ENERGY,
+ ENV_BG_PARAM_SCALE,
+ ENV_BG_PARAM_MAX
+ };
+
+
+ virtual void environment_set_background_param(RID p_env,EnvironmentBGParam p_param, const Variant& p_value)=0;
+ virtual Variant environment_get_background_param(RID p_env,EnvironmentBGParam p_param) const=0;
+
+ enum EnvironmentFx {
+ ENV_FX_GLOW,
+ ENV_FX_DOF_BLUR,
+ ENV_FX_HDR,
+ ENV_FX_FOG,
+ ENV_FX_BCS,
+ ENV_FX_GAMMA,
+ ENV_FX_MAX
+ };
+
+
+
+ virtual void environment_set_enable_fx(RID p_env,EnvironmentFx p_effect,bool p_enabled)=0;
+ virtual bool environment_is_fx_enabled(RID p_env,EnvironmentFx p_mode) const=0;
+
+ enum EnvironmentFxParam {
+ ENV_FX_PARAM_GLOW_BLUR_PASSES,
+ ENV_FX_PARAM_GLOW_BLOOM,
+ ENV_FX_PARAM_GLOW_BLOOM_TRESHOLD,
+ ENV_FX_PARAM_DOF_BLUR_PASSES,
+ ENV_FX_PARAM_DOF_BLUR_BEGIN,
+ ENV_FX_PARAM_DOF_BLUR_RANGE,
+ ENV_FX_PARAM_HDR_EXPOSURE,
+ ENV_FX_PARAM_HDR_SCALAR,
+ ENV_FX_PARAM_HDR_GLOW_TRESHOLD,
+ ENV_FX_PARAM_HDR_GLOW_SCALE,
+ ENV_FX_PARAM_HDR_MIN_LUMINANCE,
+ ENV_FX_PARAM_HDR_MAX_LUMINANCE,
+ ENV_FX_PARAM_HDR_EXPOSURE_ADJUST_SPEED,
+ ENV_FX_PARAM_FOG_BEGIN,
+ ENV_FX_PARAM_FOG_BEGIN_COLOR,
+ ENV_FX_PARAM_FOG_END_COLOR,
+ ENV_FX_PARAM_FOG_ATTENUATION,
+ ENV_FX_PARAM_FOG_BG,
+ ENV_FX_PARAM_BCS_BRIGHTNESS,
+ ENV_FX_PARAM_BCS_CONTRAST,
+ ENV_FX_PARAM_BCS_SATURATION,
+ ENV_FX_PARAM_GAMMA,
+ ENV_FX_PARAM_MAX
+ };
+
+ virtual void environment_fx_set_param(RID p_env,EnvironmentFxParam p_effect,const Variant& p_param)=0;
+ virtual Variant environment_fx_get_param(RID p_env,EnvironmentFxParam p_effect) const=0;
+
+
+ /* SCENARIO API */
+
+
+
+
+ virtual RID scenario_create()=0;
+
+ enum ScenarioDebugMode {
+ SCENARIO_DEBUG_DISABLED,
+ SCENARIO_DEBUG_WIREFRAME,
+ SCENARIO_DEBUG_OVERDRAW,
+
+ };
+
+
+ virtual void scenario_set_debug(RID p_scenario,ScenarioDebugMode p_debug_mode)=0;
+ virtual void scenario_set_environment(RID p_scenario, RID p_environment)=0;
+ virtual RID scenario_get_environment(RID p_scenario, RID p_environment) const=0;
+
+
+
+ /* INSTANCING API */
+
+ enum InstanceType {
+
+ INSTANCE_NONE,
+ INSTANCE_MESH,
+ INSTANCE_MULTIMESH,
+ INSTANCE_PARTICLES,
+ INSTANCE_LIGHT,
+ INSTANCE_ROOM,
+ INSTANCE_PORTAL,
+
+ INSTANCE_GEOMETRY_MASK=(1<<INSTANCE_MESH)|(1<<INSTANCE_MULTIMESH)|(1<<INSTANCE_PARTICLES)
+ };
+
+
+
+ virtual RID instance_create2(RID p_base, RID p_scenario);
+
+// virtual RID instance_create(RID p_base,RID p_scenario)=0; // from can be mesh, light, area and portal so far.
+ virtual RID instance_create()=0; // from can be mesh, light, poly, area and portal so far.
+
+ virtual void instance_set_base(RID p_instance, RID p_base)=0; // from can be mesh, light, poly, area and portal so far.
+ virtual RID instance_get_base(RID p_instance) const=0;
+
+ virtual void instance_set_scenario(RID p_instance, RID p_scenario)=0; // from can be mesh, light, poly, area and portal so far.
+ virtual RID instance_get_scenario(RID p_instance) const=0;
+
+ virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask)=0;
+ virtual uint32_t instance_get_layer_mask(RID p_instance) const=0;
+
+ virtual AABB instance_get_base_aabb(RID p_instance) const=0;
+
+ virtual void instance_set_transform(RID p_instance, const Transform& p_transform)=0;
+ virtual Transform instance_get_transform(RID p_instance) const=0;
+
+
+ virtual void instance_attach_object_instance_ID(RID p_instance,uint32_t p_ID)=0;
+ virtual uint32_t instance_get_object_instance_ID(RID p_instance) const=0;
+
+ virtual void instance_set_morph_target_weight(RID p_instance,int p_shape, float p_weight)=0;
+ virtual float instance_get_morph_target_weight(RID p_instance,int p_shape) const=0;
+
+ virtual void instance_attach_skeleton(RID p_instance,RID p_skeleton)=0;
+ virtual RID instance_get_skeleton(RID p_instance) const=0;
+
+ virtual void instance_set_exterior( RID p_instance, bool p_enabled )=0;
+ virtual bool instance_is_exterior( RID p_instance) const=0;
+
+ virtual void instance_set_room( RID p_instance, RID p_room )=0;
+ virtual RID instance_get_room( RID p_instance ) const =0;
+
+ virtual void instance_set_extra_visibility_margin( RID p_instance, real_t p_margin )=0;
+ virtual real_t instance_get_extra_visibility_margin( RID p_instance ) const =0;
+
+ // don't use these in a game!
+ virtual Vector<RID> instances_cull_aabb(const AABB& p_aabb, RID p_scenario=RID()) const=0;
+ virtual Vector<RID> instances_cull_ray(const Vector3& p_from, const Vector3& p_to, RID p_scenario=RID()) const=0;
+ virtual Vector<RID> instances_cull_convex(const Vector<Plane>& p_convex, RID p_scenario=RID()) const=0;
+
+ enum InstanceFlags {
+ INSTANCE_FLAG_VISIBLE,
+ INSTANCE_FLAG_BILLBOARD,
+ INSTANCE_FLAG_BILLBOARD_FIX_Y,
+ INSTANCE_FLAG_CAST_SHADOW,
+ INSTANCE_FLAG_RECEIVE_SHADOWS,
+ INSTANCE_FLAG_DEPH_SCALE,
+ INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS,
+ INSTANCE_FLAG_MAX
+ };
+
+ virtual void instance_geometry_set_flag(RID p_instance,InstanceFlags p_flags,bool p_enabled)=0;
+ virtual bool instance_geometry_get_flag(RID p_instance,InstanceFlags p_flags) const=0;
+
+ virtual void instance_geometry_set_material_override(RID p_instance, RID p_material)=0;
+ virtual RID instance_geometry_get_material_override(RID p_instance) const=0;
+
+ virtual void instance_geometry_set_draw_range(RID p_instance,float p_min,float p_max)=0;
+ virtual float instance_geometry_get_draw_range_max(RID p_instance) const=0;
+ virtual float instance_geometry_get_draw_range_min(RID p_instance) const=0;
+
+
+ /* CANVAS (2D) */
+
+ virtual RID canvas_create()=0;
+ virtual void canvas_set_item_mirroring(RID p_canvas,RID p_item,const Point2& p_mirroring)=0;
+ virtual Point2 canvas_get_item_mirroring(RID p_canvas,RID p_item) const=0;
+
+
+ virtual RID canvas_item_create()=0;
+ virtual void canvas_item_set_parent(RID p_item,RID p_parent)=0;
+ virtual RID canvas_item_get_parent(RID p_canvas_item) const=0;
+
+ virtual void canvas_item_set_visible(RID p_item,bool p_visible)=0;
+ virtual bool canvas_item_is_visible(RID p_item) const=0;
+
+ virtual void canvas_item_set_blend_mode(RID p_canvas_item,MaterialBlendMode p_blend)=0;
+
+ virtual void canvas_item_attach_viewport(RID p_item, RID p_viewport)=0;
+
+ //virtual void canvas_item_set_rect(RID p_item, const Rect2& p_rect)=0;
+
+ virtual void canvas_item_set_transform(RID p_item, const Matrix32& p_transform)=0;
+ virtual void canvas_item_set_clip(RID p_item, bool p_clip)=0;
+ virtual void canvas_item_set_custom_rect(RID p_item, bool p_custom_rect,const Rect2& p_rect=Rect2())=0;
+ virtual void canvas_item_set_opacity(RID p_item, float p_opacity)=0;
+ virtual float canvas_item_get_opacity(RID p_item, float p_opacity) const=0;
+
+ virtual void canvas_item_set_self_opacity(RID p_item, float p_self_opacity)=0;
+ virtual float canvas_item_get_self_opacity(RID p_item, float p_self_opacity) const=0;
+
+ virtual void canvas_item_set_on_top(RID p_item, bool p_on_top)=0;
+ virtual bool canvas_item_is_on_top(RID p_item) const=0;
+
+ virtual void canvas_item_add_line(RID p_item, const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width=1.0)=0;
+ virtual void canvas_item_add_rect(RID p_item, const Rect2& p_rect, const Color& p_color)=0;
+ virtual void canvas_item_add_circle(RID p_item, const Point2& p_pos, float p_radius,const Color& p_color)=0;
+ virtual void canvas_item_add_texture_rect(RID p_item, const Rect2& p_rect, RID p_texture,bool p_tile=false,const Color& p_modulate=Color(1,1,1))=0;
+ virtual void canvas_item_add_texture_rect_region(RID p_item, const Rect2& p_rect, RID p_texture,const Rect2& p_src_rect,const Color& p_modulate=Color(1,1,1))=0;
+ virtual void canvas_item_add_style_box(RID p_item, const Rect2& p_rect, RID p_texture,const Vector2& p_topleft, const Vector2& p_bottomright, bool p_draw_center=true,const Color& p_modulate=Color(1,1,1))=0;
+ virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width=1.0)=0;
+ virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs=Vector<Point2>(), RID p_texture=RID())=0;
+ virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int>& p_indices, const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs=Vector<Point2>(), RID p_texture=RID(), int p_count=-1)=0;
+ virtual void canvas_item_add_triangle_array_ptr(RID p_item, int p_count, const int* p_indices, const Point2* p_points, const Color* p_colors,const Point2* p_uvs=NULL, RID p_texture=RID())=0;
+ virtual void canvas_item_add_set_transform(RID p_item,const Matrix32& p_transform)=0;
+ virtual void canvas_item_add_set_blend_mode(RID p_item, MaterialBlendMode p_blend)=0;
+ virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore)=0;
+
+ virtual void canvas_item_clear(RID p_item)=0;
+ virtual void canvas_item_raise(RID p_item)=0;
+
+ /* CURSOR */
+ virtual void cursor_set_rotation(float p_rotation, int p_cursor = 0)=0; // radians
+ virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset = Point2(0, 0), int p_cursor=0)=0;
+ virtual void cursor_set_visible(bool p_visible, int p_cursor = 0)=0;
+ virtual void cursor_set_pos(const Point2& p_pos, int p_cursor = 0)=0;
+
+ /* BLACK BARS */
+
+
+ virtual void black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom)=0;
+
+
+ /* FREE */
+
+ virtual void free( RID p_rid )=0; ///< free RIDs associated with the visual server
+
+ /* CUSTOM SHADING */
+
+ virtual void custom_shade_model_set_shader(int p_model, RID p_shader)=0;
+ virtual RID custom_shade_model_get_shader(int p_model) const=0;
+ virtual void custom_shade_model_set_name(int p_model, const String& p_name)=0;
+ virtual String custom_shade_model_get_name(int p_model) const=0;
+ virtual void custom_shade_model_set_param_info(int p_model, const List<PropertyInfo>& p_info)=0;
+ virtual void custom_shade_model_get_param_info(int p_model, List<PropertyInfo>* p_info) const=0;
+
+ /* EVENT QUEUING */
+
+ virtual void draw()=0;
+ virtual void flush()=0;
+ virtual bool has_changed() const=0;
+ virtual void init()=0;
+ virtual void finish()=0;
+
+ /* STATUS INFORMATION */
+
+ enum RenderInfo {
+
+ INFO_OBJECTS_IN_FRAME,
+ INFO_VERTICES_IN_FRAME,
+ INFO_MATERIAL_CHANGES_IN_FRAME,
+ INFO_SHADER_CHANGES_IN_FRAME,
+ INFO_SURFACE_CHANGES_IN_FRAME,
+ INFO_DRAW_CALLS_IN_FRAME,
+ INFO_USAGE_VIDEO_MEM_TOTAL,
+ INFO_VIDEO_MEM_USED,
+ INFO_TEXTURE_MEM_USED,
+ INFO_VERTEX_MEM_USED,
+ };
+
+ virtual int get_render_info(RenderInfo p_info)=0;
+
+
+ /* TESTING */
+
+ virtual RID get_test_cube()=0;
+
+ virtual RID get_test_texture();
+
+ virtual RID make_sphere_mesh(int p_lats,int p_lons,float p_radius);
+
+ virtual void mesh_add_surface_from_mesh_data( RID p_mesh, const Geometry::MeshData& p_mesh_data);
+ virtual void mesh_add_surface_from_planes( RID p_mesh, const DVector<Plane>& p_planes);
+
+ virtual void set_boot_image(const Image& p_image, const Color& p_color)=0;
+ virtual void set_default_clear_color(const Color& p_color)=0;
+
+ enum Features {
+ FEATURE_SHADERS,
+ FEATURE_MULTITHREADED,
+ FEATURE_NEEDS_RELOAD_HOOK,
+ };
+
+ virtual bool has_feature(Features p_feature) const=0;
+
+ VisualServer();
+ virtual ~VisualServer();
+
+};
+
+// make variant understand the enums
+
+VARIANT_ENUM_CAST( VisualServer::CubeMapSide );
+VARIANT_ENUM_CAST( VisualServer::TextureFlags );
+VARIANT_ENUM_CAST( VisualServer::ShaderMode );
+VARIANT_ENUM_CAST( VisualServer::MaterialFlag );
+VARIANT_ENUM_CAST( VisualServer::MaterialBlendMode );
+VARIANT_ENUM_CAST( VisualServer::ParticleVariable );
+VARIANT_ENUM_CAST( VisualServer::ArrayType );
+VARIANT_ENUM_CAST( VisualServer::ArrayFormat );
+VARIANT_ENUM_CAST( VisualServer::PrimitiveType );
+VARIANT_ENUM_CAST( VisualServer::LightType );
+VARIANT_ENUM_CAST( VisualServer::LightColor );
+VARIANT_ENUM_CAST( VisualServer::LightParam );
+VARIANT_ENUM_CAST( VisualServer::ScenarioDebugMode );
+VARIANT_ENUM_CAST( VisualServer::InstanceType );
+VARIANT_ENUM_CAST( VisualServer::RenderInfo );
+VARIANT_ENUM_CAST( VisualServer::MipMapPolicy );
+//typedef VisualServer VS; // makes it easier to use
+#define VS VisualServer
+
+#endif